LCOV - code coverage report
Current view: top level - src/fst - fst_group.c (source / functions) Hit Total Coverage
Test: wpa_supplicant/hostapd combined for hwsim test run 1475438200 Lines: 200 208 96.2 %
Date: 2016-10-02 Functions: 18 18 100.0 %

          Line data    Source code
       1             : /*
       2             :  * FST module - FST group object implementation
       3             :  * Copyright (c) 2014, 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             : #include "utils/common.h"
      11             : #include "common/defs.h"
      12             : #include "common/ieee802_11_defs.h"
      13             : #include "common/ieee802_11_common.h"
      14             : #include "drivers/driver.h"
      15             : #include "fst/fst_internal.h"
      16             : #include "fst/fst_defs.h"
      17             : 
      18             : 
      19             : struct dl_list fst_global_groups_list;
      20             : 
      21             : 
      22        1530 : static void fst_dump_mb_ies(const char *group_id, const char *ifname,
      23             :                             struct wpabuf *mbies)
      24             : {
      25        1530 :         const u8 *p = wpabuf_head(mbies);
      26        1530 :         size_t s = wpabuf_len(mbies);
      27             : 
      28        5355 :         while (s >= 2) {
      29        2295 :                 const struct multi_band_ie *mbie =
      30             :                         (const struct multi_band_ie *) p;
      31             :                 WPA_ASSERT(mbie->eid == WLAN_EID_MULTI_BAND);
      32             :                 WPA_ASSERT(2 + mbie->len >= sizeof(*mbie));
      33             : 
      34        2295 :                 fst_printf(MSG_WARNING,
      35             :                            "%s: %s: mb_ctrl=%u band_id=%u op_class=%u chan=%u bssid="
      36             :                            MACSTR
      37             :                            " beacon_int=%u tsf_offs=[%u %u %u %u %u %u %u %u] mb_cc=0x%02x tmout=%u",
      38             :                            group_id, ifname,
      39             :                            mbie->mb_ctrl, mbie->band_id, mbie->op_class,
      40             :                            mbie->chan, MAC2STR(mbie->bssid), mbie->beacon_int,
      41             :                            mbie->tsf_offs[0], mbie->tsf_offs[1],
      42             :                            mbie->tsf_offs[2], mbie->tsf_offs[3],
      43             :                            mbie->tsf_offs[4], mbie->tsf_offs[5],
      44             :                            mbie->tsf_offs[6], mbie->tsf_offs[7],
      45             :                            mbie->mb_connection_capability,
      46             :                            mbie->fst_session_tmout);
      47             : 
      48        2295 :                 p += 2 + mbie->len;
      49        2295 :                 s -= 2 + mbie->len;
      50             :         }
      51        1530 : }
      52             : 
      53             : 
      54        2295 : static void fst_fill_mb_ie(struct wpabuf *buf, const u8 *bssid,
      55             :                            const u8 *own_addr, enum mb_band_id band, u8 channel)
      56             : {
      57             :         struct multi_band_ie *mbie;
      58        2295 :         size_t len = sizeof(*mbie);
      59             : 
      60        2295 :         if (own_addr)
      61        2295 :                 len += ETH_ALEN;
      62             : 
      63        2295 :         mbie = wpabuf_put(buf, len);
      64             : 
      65        2295 :         os_memset(mbie, 0, len);
      66             : 
      67        2295 :         mbie->eid = WLAN_EID_MULTI_BAND;
      68        2295 :         mbie->len = len - 2;
      69             : #ifdef HOSTAPD
      70         266 :         mbie->mb_ctrl = MB_STA_ROLE_AP;
      71         266 :         mbie->mb_connection_capability = MB_CONNECTION_CAPABILITY_AP;
      72             : #else /* HOSTAPD */
      73        2029 :         mbie->mb_ctrl = MB_STA_ROLE_NON_PCP_NON_AP;
      74        2029 :         mbie->mb_connection_capability = 0;
      75             : #endif /* HOSTAPD */
      76        2295 :         if (bssid)
      77         765 :                 os_memcpy(mbie->bssid, bssid, ETH_ALEN);
      78        2295 :         mbie->band_id = band;
      79        2295 :         mbie->op_class = 0;  /* means all */
      80        2295 :         mbie->chan = channel;
      81        2295 :         mbie->fst_session_tmout = FST_DEFAULT_SESSION_TIMEOUT_TU;
      82             : 
      83        2295 :         if (own_addr) {
      84        2295 :                 mbie->mb_ctrl |= MB_CTRL_STA_MAC_PRESENT;
      85        2295 :                 os_memcpy(&mbie[1], own_addr, ETH_ALEN);
      86             :         }
      87        2295 : }
      88             : 
      89             : 
      90        3060 : static unsigned fst_fill_iface_mb_ies(struct fst_iface *f, struct wpabuf *buf)
      91             : {
      92             :         const  u8 *bssid;
      93             : 
      94        3060 :         bssid = fst_iface_get_bssid(f);
      95        3060 :         if (bssid) {
      96             :                 enum hostapd_hw_mode hw_mode;
      97             :                 u8 channel;
      98             : 
      99        1530 :                 if (buf) {
     100         765 :                         fst_iface_get_channel_info(f, &hw_mode, &channel);
     101         765 :                         fst_fill_mb_ie(buf, bssid, fst_iface_get_addr(f),
     102             :                                        fst_hw_mode_to_band(hw_mode), channel);
     103             :                 }
     104        1530 :                 return 1;
     105             :         } else {
     106        1530 :                 unsigned bands[MB_BAND_ID_WIFI_60GHZ + 1] = {};
     107             :                 struct hostapd_hw_modes *modes;
     108             :                 enum mb_band_id b;
     109        1530 :                 int num_modes = fst_iface_get_hw_modes(f, &modes);
     110        1530 :                 int ret = 0;
     111             : 
     112        7650 :                 while (num_modes--) {
     113        4590 :                         b = fst_hw_mode_to_band(modes->mode);
     114        4590 :                         modes++;
     115        4590 :                         if (b >= ARRAY_SIZE(bands) || bands[b]++)
     116        1530 :                                 continue;
     117        3060 :                         ret++;
     118        3060 :                         if (buf)
     119        1530 :                                 fst_fill_mb_ie(buf, NULL, fst_iface_get_addr(f),
     120             :                                                b, MB_STA_CHANNEL_ALL);
     121             :                 }
     122        1530 :                 return ret;
     123             :         }
     124             : }
     125             : 
     126             : 
     127        2077 : static struct wpabuf * fst_group_create_mb_ie(struct fst_group *g,
     128             :                                               struct fst_iface *i)
     129             : {
     130             :         struct wpabuf *buf;
     131             :         struct fst_iface *f;
     132        2077 :         unsigned int nof_mbies = 0;
     133        2077 :         unsigned int nof_ifaces_added = 0;
     134             : 
     135        5684 :         foreach_fst_group_iface(g, f) {
     136        3607 :                 if (f == i)
     137        2077 :                         continue;
     138        1530 :                 nof_mbies += fst_fill_iface_mb_ies(f, NULL);
     139             :         }
     140             : 
     141        2077 :         buf = wpabuf_alloc(nof_mbies *
     142             :                            (sizeof(struct multi_band_ie) + ETH_ALEN));
     143        2077 :         if (!buf) {
     144           1 :                 fst_printf_iface(i, MSG_ERROR,
     145             :                                  "cannot allocate mem for %u MB IEs",
     146             :                                  nof_mbies);
     147           1 :                 return NULL;
     148             :         }
     149             : 
     150             :         /* The list is sorted in descending order by priorities, so MB IEs will
     151             :          * be arranged in the same order, as required by spec (see corresponding
     152             :          * comment in.fst_attach().
     153             :          */
     154        5682 :         foreach_fst_group_iface(g, f) {
     155        3606 :                 if (f == i)
     156        2076 :                         continue;
     157             : 
     158        1530 :                 fst_fill_iface_mb_ies(f, buf);
     159        1530 :                 ++nof_ifaces_added;
     160             : 
     161        1530 :                 fst_printf_iface(i, MSG_DEBUG, "added to MB IE");
     162             :         }
     163             : 
     164        2076 :         if (!nof_ifaces_added) {
     165         546 :                 wpabuf_free(buf);
     166         546 :                 buf = NULL;
     167         546 :                 fst_printf_iface(i, MSG_INFO,
     168             :                                  "cannot add MB IE: no backup ifaces");
     169             :         } else {
     170        1530 :                 fst_dump_mb_ies(fst_group_get_id(g), fst_iface_get_name(i),
     171             :                                 buf);
     172             :         }
     173             : 
     174        2076 :         return buf;
     175             : }
     176             : 
     177             : 
     178         324 : static const u8 * fst_mbie_get_peer_addr(const struct multi_band_ie *mbie)
     179             : {
     180         324 :         const u8 *peer_addr = NULL;
     181             : 
     182         324 :         switch (MB_CTRL_ROLE(mbie->mb_ctrl)) {
     183             :         case MB_STA_ROLE_AP:
     184          29 :                 peer_addr = mbie->bssid;
     185          29 :                 break;
     186             :         case MB_STA_ROLE_NON_PCP_NON_AP:
     187         587 :                 if (mbie->mb_ctrl & MB_CTRL_STA_MAC_PRESENT &&
     188         293 :                     (size_t) 2 + mbie->len >= sizeof(*mbie) + ETH_ALEN)
     189         293 :                         peer_addr = (const u8 *) &mbie[1];
     190         294 :                 break;
     191             :         default:
     192           1 :                 break;
     193             :         }
     194             : 
     195         324 :         return peer_addr;
     196             : }
     197             : 
     198             : 
     199         327 : static const u8 * fst_mbie_get_peer_addr_for_band(const struct wpabuf *mbies,
     200             :                                                   u8 band_id)
     201             : {
     202         327 :         const u8 *p = wpabuf_head(mbies);
     203         327 :         size_t s = wpabuf_len(mbies);
     204             : 
     205         657 :         while (s >= 2) {
     206         327 :                 const struct multi_band_ie *mbie =
     207             :                         (const struct multi_band_ie *) p;
     208             : 
     209         327 :                 if (mbie->eid != WLAN_EID_MULTI_BAND) {
     210           0 :                         fst_printf(MSG_INFO, "unexpected eid %d", mbie->eid);
     211           0 :                         return NULL;
     212             :                 }
     213             : 
     214         327 :                 if (mbie->len < sizeof(*mbie) - 2 || mbie->len > s - 2) {
     215           0 :                         fst_printf(MSG_INFO, "invalid mbie len %d",
     216             :                                    mbie->len);
     217           0 :                         return NULL;
     218             :                 }
     219             : 
     220         327 :                 if (mbie->band_id == band_id)
     221         324 :                         return fst_mbie_get_peer_addr(mbie);
     222             : 
     223           3 :                 p += 2 + mbie->len;
     224           3 :                 s -= 2 + mbie->len;
     225             :         }
     226             : 
     227           3 :         fst_printf(MSG_INFO, "mbie doesn't contain band %d", band_id);
     228           3 :         return NULL;
     229             : }
     230             : 
     231             : 
     232         775 : struct fst_iface * fst_group_get_iface_by_name(struct fst_group *g,
     233             :                                                const char *ifname)
     234             : {
     235             :         struct fst_iface *f;
     236             : 
     237        1168 :         foreach_fst_group_iface(g, f) {
     238        1157 :                 const char *in = fst_iface_get_name(f);
     239             : 
     240        1157 :                 if (os_strncmp(in, ifname, os_strlen(in)) == 0)
     241         764 :                         return f;
     242             :         }
     243             : 
     244          11 :         return NULL;
     245             : }
     246             : 
     247             : 
     248         318 : u8 fst_group_assign_dialog_token(struct fst_group *g)
     249             : {
     250         318 :         g->dialog_token++;
     251         318 :         if (g->dialog_token == 0)
     252           1 :                 g->dialog_token++;
     253         318 :         return g->dialog_token;
     254             : }
     255             : 
     256             : 
     257         312 : u32 fst_group_assign_fsts_id(struct fst_group *g)
     258             : {
     259         312 :         g->fsts_id++;
     260         312 :         return g->fsts_id;
     261             : }
     262             : 
     263             : 
     264             : /**
     265             :  * fst_group_get_peer_other_connection_1 - Find peer's "other" connection
     266             :  * (iface, MAC tuple) by using peer's MB IE on iface.
     267             :  *
     268             :  * @iface: iface on which FST Setup Request was received
     269             :  * @peer_addr: Peer address on iface
     270             :  * @band_id: "other" connection band id
     271             :  * @other_peer_addr (out): Peer's MAC address on the "other" connection (on the
     272             :  *   "other" iface)
     273             :  *
     274             :  * This function parses peer's MB IE on iface. It looks for peer's MAC address
     275             :  * on band_id (tmp_peer_addr). Next all interfaces are iterated to find an
     276             :  * interface which correlates with band_id. If such interface is found, peer
     277             :  * database is iterated to see if tmp_peer_addr is connected over it.
     278             :  */
     279             : static struct fst_iface *
     280         322 : fst_group_get_peer_other_connection_1(struct fst_iface *iface,
     281             :                                       const u8 *peer_addr, u8 band_id,
     282             :                                       u8 *other_peer_addr)
     283             : {
     284             :         const struct wpabuf *mbies;
     285             :         struct fst_iface *other_iface;
     286             :         const u8 *tmp_peer_addr;
     287             : 
     288             :         /* Get peer's MB IEs on iface */
     289         322 :         mbies = fst_iface_get_peer_mb_ie(iface, peer_addr);
     290         322 :         if (!mbies)
     291           2 :                 return NULL;
     292             : 
     293             :         /* Get peer's MAC address on the "other" interface */
     294         320 :         tmp_peer_addr = fst_mbie_get_peer_addr_for_band(mbies, band_id);
     295         320 :         if (!tmp_peer_addr) {
     296           5 :                 fst_printf(MSG_INFO,
     297             :                            "couldn't extract other peer addr from mbies");
     298           5 :                 return NULL;
     299             :         }
     300             : 
     301         315 :         fst_printf(MSG_DEBUG, "found other peer addr from mbies: " MACSTR,
     302             :                    MAC2STR(tmp_peer_addr));
     303             : 
     304         577 :         foreach_fst_group_iface(fst_iface_get_group(iface), other_iface) {
     305         891 :                 if (other_iface == iface ||
     306         315 :                     band_id != fst_iface_get_band_id(other_iface))
     307         261 :                         continue;
     308         315 :                 if (fst_iface_is_connected(other_iface, tmp_peer_addr, FALSE)) {
     309         314 :                         os_memcpy(other_peer_addr, tmp_peer_addr, ETH_ALEN);
     310         314 :                         return other_iface;
     311             :                 }
     312             :         }
     313             : 
     314           1 :         return NULL;
     315             : }
     316             : 
     317             : 
     318             : /**
     319             :  * fst_group_get_peer_other_connection_2 - Find peer's "other" connection
     320             :  * (iface, MAC tuple) by using MB IEs of other peers.
     321             :  *
     322             :  * @iface: iface on which FST Setup Request was received
     323             :  * @peer_addr: Peer address on iface
     324             :  * @band_id: "other" connection band id
     325             :  * @other_peer_addr (out): Peer's MAC address on the "other" connection (on the
     326             :  *   "other" iface)
     327             :  *
     328             :  * This function iterates all connection (other_iface, cur_peer_addr tuples).
     329             :  * For each connection, MB IE (of cur_peer_addr on other_iface) is parsed and
     330             :  * MAC address on iface's band_id is extracted (this_peer_addr).
     331             :  * this_peer_addr is then compared to peer_addr. A match indicates we have
     332             :  * found the "other" connection.
     333             :  */
     334             : static struct fst_iface *
     335           8 : fst_group_get_peer_other_connection_2(struct fst_iface *iface,
     336             :                                       const u8 *peer_addr, u8 band_id,
     337             :                                       u8 *other_peer_addr)
     338             : {
     339           8 :         u8 this_band_id = fst_iface_get_band_id(iface);
     340             :         const u8 *cur_peer_addr, *this_peer_addr;
     341             :         struct fst_get_peer_ctx *ctx;
     342             :         struct fst_iface *other_iface;
     343             :         const struct wpabuf *cur_mbie;
     344             : 
     345          16 :         foreach_fst_group_iface(fst_iface_get_group(iface), other_iface) {
     346          23 :                 if (other_iface == iface ||
     347           8 :                     band_id != fst_iface_get_band_id(other_iface))
     348           8 :                         continue;
     349           7 :                 cur_peer_addr = fst_iface_get_peer_first(other_iface, &ctx,
     350             :                                                          TRUE);
     351          14 :                 for (; cur_peer_addr;
     352           0 :                      cur_peer_addr = fst_iface_get_peer_next(other_iface, &ctx,
     353             :                                                              TRUE)) {
     354           7 :                         cur_mbie = fst_iface_get_peer_mb_ie(other_iface,
     355             :                                                             cur_peer_addr);
     356           7 :                         if (!cur_mbie)
     357           0 :                                 continue;
     358           7 :                         this_peer_addr = fst_mbie_get_peer_addr_for_band(
     359             :                                 cur_mbie, this_band_id);
     360           7 :                         if (!this_peer_addr)
     361           0 :                                 continue;
     362           7 :                         if (os_memcmp(this_peer_addr, peer_addr, ETH_ALEN) ==
     363             :                             0) {
     364           7 :                                 os_memcpy(other_peer_addr, cur_peer_addr,
     365             :                                           ETH_ALEN);
     366           7 :                                 return other_iface;
     367             :                         }
     368             :                 }
     369             :         }
     370             : 
     371           1 :         return NULL;
     372             : }
     373             : 
     374             : 
     375             : /**
     376             :  * fst_group_get_peer_other_connection - Find peer's "other" connection (iface,
     377             :  * MAC tuple).
     378             :  *
     379             :  * @iface: iface on which FST Setup Request was received
     380             :  * @peer_addr: Peer address on iface
     381             :  * @band_id: "other" connection band id
     382             :  * @other_peer_addr (out): Peer's MAC address on the "other" connection (on the
     383             :  *   "other" iface)
     384             :  *
     385             :  * This function is called upon receiving FST Setup Request from some peer who
     386             :  * has peer_addr on iface. It searches for another connection of the same peer
     387             :  * on different interface which correlates with band_id. MB IEs received from
     388             :  * peer (on the two different interfaces) are used to identify same peer.
     389             :  */
     390             : struct fst_iface *
     391         322 : fst_group_get_peer_other_connection(struct fst_iface *iface,
     392             :                                     const u8 *peer_addr, u8 band_id,
     393             :                                     u8 *other_peer_addr)
     394             : {
     395             :         struct fst_iface *other_iface;
     396             : 
     397         322 :         fst_printf(MSG_DEBUG, "%s: %s:" MACSTR ", %d", __func__,
     398             :                    fst_iface_get_name(iface), MAC2STR(peer_addr), band_id);
     399             : 
     400             :         /*
     401             :          * Two search methods are used:
     402             :          * 1. Use peer's MB IE on iface to extract peer's MAC address on
     403             :          *    "other" connection. Then check if such "other" connection exists.
     404             :          * 2. Iterate peer database, examine each MB IE to see if it points to
     405             :          *    (iface, peer_addr) tuple
     406             :          */
     407             : 
     408         322 :         other_iface = fst_group_get_peer_other_connection_1(iface, peer_addr,
     409             :                                                             band_id,
     410             :                                                             other_peer_addr);
     411         322 :         if (other_iface) {
     412         314 :                 fst_printf(MSG_DEBUG, "found by method #1. %s:" MACSTR,
     413             :                            fst_iface_get_name(other_iface),
     414             :                            MAC2STR(other_peer_addr));
     415         314 :                 return other_iface;
     416             :         }
     417             : 
     418           8 :         other_iface = fst_group_get_peer_other_connection_2(iface, peer_addr,
     419             :                                                             band_id,
     420             :                                                             other_peer_addr);
     421           8 :         if (other_iface) {
     422           7 :                 fst_printf(MSG_DEBUG, "found by method #2. %s:" MACSTR,
     423             :                            fst_iface_get_name(other_iface),
     424             :                            MAC2STR(other_peer_addr));
     425           7 :                 return other_iface;
     426             :         }
     427             : 
     428           1 :         fst_printf(MSG_INFO, "%s: other connection not found", __func__);
     429           1 :         return NULL;
     430             : }
     431             : 
     432             : 
     433         281 : struct fst_group * fst_group_create(const char *group_id)
     434             : {
     435             :         struct fst_group *g;
     436             : 
     437         281 :         g = os_zalloc(sizeof(*g));
     438         281 :         if (g == NULL) {
     439           1 :                 fst_printf(MSG_ERROR, "%s: Cannot alloc group", group_id);
     440           1 :                 return NULL;
     441             :         }
     442             : 
     443         280 :         dl_list_init(&g->ifaces);
     444         280 :         os_strlcpy(g->group_id, group_id, sizeof(g->group_id));
     445             : 
     446         280 :         dl_list_add_tail(&fst_global_groups_list, &g->global_groups_lentry);
     447         280 :         fst_printf_group(g, MSG_DEBUG, "instance created");
     448             : 
     449         280 :         foreach_fst_ctrl_call(on_group_created, g);
     450             : 
     451         280 :         return g;
     452             : }
     453             : 
     454             : 
     455         544 : void fst_group_attach_iface(struct fst_group *g, struct fst_iface *i)
     456             : {
     457         544 :         struct dl_list *list = &g->ifaces;
     458             :         struct fst_iface *f;
     459             : 
     460             :         /*
     461             :          * Add new interface to the list.
     462             :          * The list is sorted in descending order by priority to allow
     463             :          * multiple MB IEs creation according to the spec (see 10.32 Multi-band
     464             :          * operation, 10.32.1 General), as they should be ordered according to
     465             :          * priorities.
     466             :          */
     467         558 :         foreach_fst_group_iface(g, f) {
     468         265 :                 if (fst_iface_get_priority(f) < fst_iface_get_priority(i))
     469         251 :                         break;
     470          14 :                 list = &f->group_lentry;
     471             :         }
     472         544 :         dl_list_add(list, &i->group_lentry);
     473         544 : }
     474             : 
     475             : 
     476         544 : void fst_group_detach_iface(struct fst_group *g, struct fst_iface *i)
     477             : {
     478         544 :         dl_list_del(&i->group_lentry);
     479         544 : }
     480             : 
     481             : 
     482         280 : void fst_group_delete(struct fst_group *group)
     483             : {
     484             :         struct fst_session *s;
     485             : 
     486         280 :         dl_list_del(&group->global_groups_lentry);
     487             :         WPA_ASSERT(dl_list_empty(&group->ifaces));
     488         280 :         foreach_fst_ctrl_call(on_group_deleted, group);
     489         280 :         fst_printf_group(group, MSG_DEBUG, "instance deleted");
     490         560 :         while ((s = fst_session_global_get_first_by_group(group)) != NULL)
     491           0 :                 fst_session_delete(s);
     492         280 :         os_free(group);
     493         280 : }
     494             : 
     495             : 
     496         977 : Boolean fst_group_delete_if_empty(struct fst_group *group)
     497             : {
     498        1256 :         Boolean is_empty = !fst_group_has_ifaces(group) &&
     499         279 :                 !fst_session_global_get_first_by_group(group);
     500             : 
     501         977 :         if (is_empty)
     502         279 :                 fst_group_delete(group);
     503             : 
     504         977 :         return is_empty;
     505             : }
     506             : 
     507             : 
     508        1591 : void fst_group_update_ie(struct fst_group *g)
     509             : {
     510             :         struct fst_iface *i;
     511             : 
     512        3668 :         foreach_fst_group_iface(g, i) {
     513        2077 :                 struct wpabuf *mbie = fst_group_create_mb_ie(g, i);
     514             : 
     515        2077 :                 if (!mbie)
     516         547 :                         fst_printf_iface(i, MSG_WARNING, "cannot create MB IE");
     517             : 
     518        2077 :                 fst_iface_attach_mbie(i, mbie);
     519        2077 :                 fst_iface_set_ies(i, mbie);
     520        2077 :                 fst_printf_iface(i, MSG_DEBUG, "multi-band IE set to %p", mbie);
     521             :         }
     522        1591 : }

Generated by: LCOV version 1.10