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 1443382998 Lines: 197 198 99.5 %
Date: 2015-09-27 Functions: 19 19 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             : #ifndef HOSTAPD
      22        2782 : static Boolean fst_has_fst_peer(struct fst_iface *iface, Boolean *has_peer)
      23             : {
      24             :         const u8 *bssid;
      25             : 
      26        2782 :         bssid = fst_iface_get_bssid(iface);
      27        2782 :         if (!bssid) {
      28        1793 :                 *has_peer = FALSE;
      29        1793 :                 return FALSE;
      30             :         }
      31             : 
      32         989 :         *has_peer = TRUE;
      33         989 :         return fst_iface_get_peer_mb_ie(iface, bssid) != NULL;
      34             : }
      35             : #endif /* HOSTAPD */
      36             : 
      37             : 
      38        1504 : static void fst_dump_mb_ies(const char *group_id, const char *ifname,
      39             :                             struct wpabuf *mbies)
      40             : {
      41        1504 :         const u8 *p = wpabuf_head(mbies);
      42        1504 :         size_t s = wpabuf_len(mbies);
      43             : 
      44        5271 :         while (s >= 2) {
      45        2263 :                 const struct multi_band_ie *mbie =
      46             :                         (const struct multi_band_ie *) p;
      47             :                 WPA_ASSERT(mbie->eid == WLAN_EID_MULTI_BAND);
      48             :                 WPA_ASSERT(2 + mbie->len >= sizeof(*mbie));
      49             : 
      50        2263 :                 fst_printf(MSG_WARNING,
      51             :                            "%s: %s: mb_ctrl=%u band_id=%u op_class=%u chan=%u bssid="
      52             :                            MACSTR
      53             :                            " beacon_int=%u tsf_offs=[%u %u %u %u %u %u %u %u] mb_cc=0x%02x tmout=%u",
      54             :                            group_id, ifname,
      55             :                            mbie->mb_ctrl, mbie->band_id, mbie->op_class,
      56             :                            mbie->chan, MAC2STR(mbie->bssid), mbie->beacon_int,
      57             :                            mbie->tsf_offs[0], mbie->tsf_offs[1],
      58             :                            mbie->tsf_offs[2], mbie->tsf_offs[3],
      59             :                            mbie->tsf_offs[4], mbie->tsf_offs[5],
      60             :                            mbie->tsf_offs[6], mbie->tsf_offs[7],
      61             :                            mbie->mb_connection_capability,
      62             :                            mbie->fst_session_tmout);
      63             : 
      64        2263 :                 p += 2 + mbie->len;
      65        2263 :                 s -= 2 + mbie->len;
      66             :         }
      67        1504 : }
      68             : 
      69             : 
      70        2263 : static void fst_fill_mb_ie(struct wpabuf *buf, const u8 *bssid,
      71             :                            const u8 *own_addr, enum mb_band_id band, u8 channel)
      72             : {
      73             :         struct multi_band_ie *mbie;
      74        2263 :         size_t len = sizeof(*mbie);
      75             : 
      76        2263 :         if (own_addr)
      77        2263 :                 len += ETH_ALEN;
      78             : 
      79        2263 :         mbie = wpabuf_put(buf, len);
      80             : 
      81        2263 :         os_memset(mbie, 0, len);
      82             : 
      83        2263 :         mbie->eid = WLAN_EID_MULTI_BAND;
      84        2263 :         mbie->len = len - 2;
      85             : #ifdef HOSTAPD
      86         266 :         mbie->mb_ctrl = MB_STA_ROLE_AP;
      87         266 :         mbie->mb_connection_capability = MB_CONNECTION_CAPABILITY_AP;
      88             : #else /* HOSTAPD */
      89        1997 :         mbie->mb_ctrl = MB_STA_ROLE_NON_PCP_NON_AP;
      90        1997 :         mbie->mb_connection_capability = 0;
      91             : #endif /* HOSTAPD */
      92        2263 :         if (bssid)
      93         745 :                 os_memcpy(mbie->bssid, bssid, ETH_ALEN);
      94        2263 :         mbie->band_id = band;
      95        2263 :         mbie->op_class = 0;  /* means all */
      96        2263 :         mbie->chan = channel;
      97        2263 :         mbie->fst_session_tmout = FST_DEFAULT_SESSION_TIMEOUT_TU;
      98             : 
      99        2263 :         if (own_addr) {
     100        2263 :                 mbie->mb_ctrl |= MB_CTRL_STA_MAC_PRESENT;
     101        2263 :                 os_memcpy(&mbie[1], own_addr, ETH_ALEN);
     102             :         }
     103        2263 : }
     104             : 
     105             : 
     106        3008 : static unsigned fst_fill_iface_mb_ies(struct fst_iface *f, struct wpabuf *buf)
     107             : {
     108             :         const  u8 *bssid;
     109             : 
     110        3008 :         bssid = fst_iface_get_bssid(f);
     111        3008 :         if (bssid) {
     112             :                 enum hostapd_hw_mode hw_mode;
     113             :                 u8 channel;
     114             : 
     115        1490 :                 if (buf) {
     116         745 :                         fst_iface_get_channel_info(f, &hw_mode, &channel);
     117         745 :                         fst_fill_mb_ie(buf, bssid, fst_iface_get_addr(f),
     118             :                                        fst_hw_mode_to_band(hw_mode), channel);
     119             :                 }
     120        1490 :                 return 1;
     121             :         } else {
     122        1518 :                 unsigned bands[MB_BAND_ID_WIFI_60GHZ + 1] = {};
     123             :                 struct hostapd_hw_modes *modes;
     124             :                 enum mb_band_id b;
     125        1518 :                 int num_modes = fst_iface_get_hw_modes(f, &modes);
     126        1518 :                 int ret = 0;
     127             : 
     128        7590 :                 while (num_modes--) {
     129        4554 :                         b = fst_hw_mode_to_band(modes->mode);
     130        4554 :                         modes++;
     131        4554 :                         if (b >= ARRAY_SIZE(bands) || bands[b]++)
     132        1518 :                                 continue;
     133        3036 :                         ret++;
     134        3036 :                         if (buf)
     135        1518 :                                 fst_fill_mb_ie(buf, NULL, fst_iface_get_addr(f),
     136             :                                                b, MB_STA_CHANNEL_ALL);
     137             :                 }
     138        1518 :                 return ret;
     139             :         }
     140             : }
     141             : 
     142             : 
     143        2077 : static struct wpabuf * fst_group_create_mb_ie(struct fst_group *g,
     144             :                                               struct fst_iface *i)
     145             : {
     146             :         struct wpabuf *buf;
     147             :         struct fst_iface *f;
     148        2077 :         unsigned int nof_mbies = 0;
     149        2077 :         unsigned int nof_ifaces_added = 0;
     150             : #ifndef HOSTAPD
     151             :         Boolean has_peer;
     152             :         Boolean has_fst_peer;
     153             : 
     154        4298 :         foreach_fst_group_iface(g, f) {
     155        2782 :                 has_fst_peer = fst_has_fst_peer(f, &has_peer);
     156        2782 :                 if (has_peer && !has_fst_peer)
     157          26 :                         return NULL;
     158             :         }
     159             : #endif /* HOSTAPD */
     160             : 
     161        5606 :         foreach_fst_group_iface(g, f) {
     162        3555 :                 if (f == i)
     163        2051 :                         continue;
     164        1504 :                 nof_mbies += fst_fill_iface_mb_ies(f, NULL);
     165             :         }
     166             : 
     167        2051 :         buf = wpabuf_alloc(nof_mbies *
     168             :                            (sizeof(struct multi_band_ie) + ETH_ALEN));
     169        2051 :         if (!buf) {
     170           1 :                 fst_printf_iface(i, MSG_ERROR,
     171             :                                  "cannot allocate mem for %u MB IEs",
     172             :                                  nof_mbies);
     173           1 :                 return NULL;
     174             :         }
     175             : 
     176             :         /* The list is sorted in descending order by priorities, so MB IEs will
     177             :          * be arranged in the same order, as required by spec (see corresponding
     178             :          * comment in.fst_attach().
     179             :          */
     180        5604 :         foreach_fst_group_iface(g, f) {
     181        3554 :                 if (f == i)
     182        2050 :                         continue;
     183             : 
     184        1504 :                 fst_fill_iface_mb_ies(f, buf);
     185        1504 :                 ++nof_ifaces_added;
     186             : 
     187        1504 :                 fst_printf_iface(i, MSG_DEBUG, "added to MB IE");
     188             :         }
     189             : 
     190        2050 :         if (!nof_ifaces_added) {
     191         546 :                 wpabuf_free(buf);
     192         546 :                 buf = NULL;
     193         546 :                 fst_printf_iface(i, MSG_INFO,
     194             :                                  "cannot add MB IE: no backup ifaces");
     195             :         } else {
     196        1504 :                 fst_dump_mb_ies(fst_group_get_id(g), fst_iface_get_name(i),
     197             :                                 buf);
     198             :         }
     199             : 
     200        2050 :         return buf;
     201             : }
     202             : 
     203             : 
     204         587 : static const u8 * fst_mbie_get_peer_addr(const struct multi_band_ie *mbie)
     205             : {
     206         587 :         const u8 *peer_addr = NULL;
     207             : 
     208         587 :         switch (MB_CTRL_ROLE(mbie->mb_ctrl)) {
     209             :         case MB_STA_ROLE_AP:
     210          28 :                 peer_addr = mbie->bssid;
     211          28 :                 break;
     212             :         case MB_STA_ROLE_NON_PCP_NON_AP:
     213        1112 :                 if (mbie->mb_ctrl & MB_CTRL_STA_MAC_PRESENT &&
     214         555 :                     (size_t) 2 + mbie->len >= sizeof(*mbie) + ETH_ALEN)
     215         555 :                         peer_addr = (const u8 *) &mbie[1];
     216         557 :                 break;
     217             :         default:
     218           2 :                 break;
     219             :         }
     220             : 
     221         587 :         return peer_addr;
     222             : }
     223             : 
     224             : 
     225             : static struct fst_iface *
     226         326 : fst_group_get_new_iface_by_mbie_and_band_id(struct fst_group *g,
     227             :                                             const u8 *mb_ies_buff,
     228             :                                             size_t mb_ies_size,
     229             :                                             u8 band_id,
     230             :                                             u8 *iface_peer_addr)
     231             : {
     232         655 :         while (mb_ies_size >= 2) {
     233         326 :                 const struct multi_band_ie *mbie =
     234             :                         (const struct multi_band_ie *) mb_ies_buff;
     235             : 
     236         652 :                 if (mbie->eid != WLAN_EID_MULTI_BAND ||
     237         326 :                     (size_t) 2 + mbie->len < sizeof(*mbie))
     238             :                         break;
     239             : 
     240         326 :                 if (mbie->band_id == band_id) {
     241             :                         struct fst_iface *iface;
     242             : 
     243         591 :                         foreach_fst_group_iface(g, iface) {
     244         587 :                                 const u8 *peer_addr =
     245             :                                         fst_mbie_get_peer_addr(mbie);
     246             : 
     247        1170 :                                 if (peer_addr &&
     248         902 :                                     fst_iface_is_connected(iface, peer_addr) &&
     249         319 :                                     band_id == fst_iface_get_band_id(iface)) {
     250         319 :                                         os_memcpy(iface_peer_addr, peer_addr,
     251             :                                                   ETH_ALEN);
     252         319 :                                         return iface;
     253             :                                 }
     254             :                         }
     255           4 :                         break;
     256             :                 }
     257             : 
     258           3 :                 mb_ies_buff += 2 + mbie->len;
     259           3 :                 mb_ies_size -= 2 + mbie->len;
     260             :         }
     261             : 
     262           7 :         return NULL;
     263             : }
     264             : 
     265             : 
     266         792 : struct fst_iface * fst_group_get_iface_by_name(struct fst_group *g,
     267             :                                                const char *ifname)
     268             : {
     269             :         struct fst_iface *f;
     270             : 
     271        1182 :         foreach_fst_group_iface(g, f) {
     272        1153 :                 const char *in = fst_iface_get_name(f);
     273             : 
     274        1153 :                 if (os_strncmp(in, ifname, os_strlen(in)) == 0)
     275         763 :                         return f;
     276             :         }
     277             : 
     278          29 :         return NULL;
     279             : }
     280             : 
     281             : 
     282         318 : u8 fst_group_assign_dialog_token(struct fst_group *g)
     283             : {
     284         318 :         g->dialog_token++;
     285         318 :         if (g->dialog_token == 0)
     286           1 :                 g->dialog_token++;
     287         318 :         return g->dialog_token;
     288             : }
     289             : 
     290             : 
     291         312 : u32 fst_group_assign_fsts_id(struct fst_group *g)
     292             : {
     293         312 :         g->fsts_id++;
     294         312 :         return g->fsts_id;
     295             : }
     296             : 
     297             : 
     298             : static Boolean
     299           6 : fst_group_does_iface_appear_in_other_mbies(struct fst_group *g,
     300             :                                            struct fst_iface *iface,
     301             :                                            struct fst_iface *other,
     302             :                                            u8 *peer_addr)
     303             : {
     304             :         struct fst_get_peer_ctx *ctx;
     305             :         const u8 *addr;
     306             :         const u8 *iface_addr;
     307             :         enum mb_band_id  iface_band_id;
     308             : 
     309             :         WPA_ASSERT(g == fst_iface_get_group(iface));
     310             :         WPA_ASSERT(g == fst_iface_get_group(other));
     311             : 
     312           6 :         iface_addr = fst_iface_get_addr(iface);
     313           6 :         iface_band_id = fst_iface_get_band_id(iface);
     314             : 
     315           6 :         addr = fst_iface_get_peer_first(other, &ctx, TRUE);
     316           7 :         for (; addr; addr = fst_iface_get_peer_next(other, &ctx, TRUE)) {
     317             :                 const struct wpabuf *mbies;
     318             :                 u8 other_iface_peer_addr[ETH_ALEN];
     319             :                 struct fst_iface *other_new_iface;
     320             : 
     321           6 :                 mbies = fst_iface_get_peer_mb_ie(other, addr);
     322           6 :                 if (!mbies)
     323           0 :                         continue;
     324             : 
     325          12 :                 other_new_iface = fst_group_get_new_iface_by_mbie_and_band_id(
     326           6 :                         g, wpabuf_head(mbies), wpabuf_len(mbies),
     327             :                         iface_band_id, other_iface_peer_addr);
     328          11 :                 if (other_new_iface == iface &&
     329           5 :                     os_memcmp(iface_addr, other_iface_peer_addr,
     330             :                               ETH_ALEN) != 0) {
     331           5 :                         os_memcpy(peer_addr, addr, ETH_ALEN);
     332           5 :                         return TRUE;
     333             :                 }
     334             :         }
     335             : 
     336           1 :         return FALSE;
     337             : }
     338             : 
     339             : 
     340             : struct fst_iface *
     341           7 : fst_group_find_new_iface_by_stie(struct fst_group *g,
     342             :                                  struct fst_iface *iface,
     343             :                                  const u8 *peer_addr,
     344             :                                  const struct session_transition_ie *stie,
     345             :                                  u8 *iface_peer_addr)
     346             : {
     347             :         struct fst_iface *i;
     348             : 
     349          30 :         foreach_fst_group_iface(g, i) {
     350          21 :                 if (i == iface ||
     351           7 :                     stie->new_band_id != fst_iface_get_band_id(i))
     352           8 :                         continue;
     353           6 :                 if (fst_group_does_iface_appear_in_other_mbies(g, iface, i,
     354             :                         iface_peer_addr))
     355           5 :                         return i;
     356           1 :                 break;
     357             :         }
     358           2 :         return NULL;
     359             : }
     360             : 
     361             : 
     362             : struct fst_iface *
     363         320 : fst_group_get_new_iface_by_stie_and_mbie(
     364             :         struct fst_group *g, const u8 *mb_ies_buff, size_t mb_ies_size,
     365             :         const struct session_transition_ie *stie, u8 *iface_peer_addr)
     366             : {
     367         320 :         return fst_group_get_new_iface_by_mbie_and_band_id(
     368         320 :                 g, mb_ies_buff, mb_ies_size, stie->new_band_id,
     369             :                 iface_peer_addr);
     370             : }
     371             : 
     372             : 
     373         281 : struct fst_group * fst_group_create(const char *group_id)
     374             : {
     375             :         struct fst_group *g;
     376             : 
     377         281 :         g = os_zalloc(sizeof(*g));
     378         281 :         if (g == NULL) {
     379           1 :                 fst_printf(MSG_ERROR, "%s: Cannot alloc group", group_id);
     380           1 :                 return NULL;
     381             :         }
     382             : 
     383         280 :         dl_list_init(&g->ifaces);
     384         280 :         os_strlcpy(g->group_id, group_id, sizeof(g->group_id));
     385             : 
     386         280 :         dl_list_add_tail(&fst_global_groups_list, &g->global_groups_lentry);
     387         280 :         fst_printf_group(g, MSG_DEBUG, "instance created");
     388             : 
     389         280 :         foreach_fst_ctrl_call(on_group_created, g);
     390             : 
     391         280 :         return g;
     392             : }
     393             : 
     394             : 
     395         544 : void fst_group_attach_iface(struct fst_group *g, struct fst_iface *i)
     396             : {
     397         544 :         struct dl_list *list = &g->ifaces;
     398             :         struct fst_iface *f;
     399             : 
     400             :         /*
     401             :          * Add new interface to the list.
     402             :          * The list is sorted in descending order by priority to allow
     403             :          * multiple MB IEs creation according to the spec (see 10.32 Multi-band
     404             :          * operation, 10.32.1 General), as they should be ordered according to
     405             :          * priorities.
     406             :          */
     407         558 :         foreach_fst_group_iface(g, f) {
     408         265 :                 if (fst_iface_get_priority(f) < fst_iface_get_priority(i))
     409         251 :                         break;
     410          14 :                 list = &f->group_lentry;
     411             :         }
     412         544 :         dl_list_add(list, &i->group_lentry);
     413         544 : }
     414             : 
     415             : 
     416         544 : void fst_group_detach_iface(struct fst_group *g, struct fst_iface *i)
     417             : {
     418         544 :         dl_list_del(&i->group_lentry);
     419         544 : }
     420             : 
     421             : 
     422         280 : void fst_group_delete(struct fst_group *group)
     423             : {
     424             :         struct fst_session *s;
     425             : 
     426         280 :         dl_list_del(&group->global_groups_lentry);
     427             :         WPA_ASSERT(dl_list_empty(&group->ifaces));
     428         280 :         foreach_fst_ctrl_call(on_group_deleted, group);
     429         280 :         fst_printf_group(group, MSG_DEBUG, "instance deleted");
     430         565 :         while ((s = fst_session_global_get_first_by_group(group)) != NULL)
     431           5 :                 fst_session_delete(s);
     432         280 :         os_free(group);
     433         280 : }
     434             : 
     435             : 
     436         970 : Boolean fst_group_delete_if_empty(struct fst_group *group)
     437             : {
     438        1249 :         Boolean is_empty = !fst_group_has_ifaces(group) &&
     439         279 :                 !fst_session_global_get_first_by_group(group);
     440             : 
     441         970 :         if (is_empty)
     442         278 :                 fst_group_delete(group);
     443             : 
     444         970 :         return is_empty;
     445             : }
     446             : 
     447             : 
     448        1591 : void fst_group_update_ie(struct fst_group *g)
     449             : {
     450             :         struct fst_iface *i;
     451             : 
     452        3668 :         foreach_fst_group_iface(g, i) {
     453        2077 :                 struct wpabuf *mbie = fst_group_create_mb_ie(g, i);
     454             : 
     455        2077 :                 if (!mbie)
     456         573 :                         fst_printf_iface(i, MSG_WARNING, "cannot create MB IE");
     457             : 
     458        2077 :                 fst_iface_attach_mbie(i, mbie);
     459        2077 :                 fst_iface_set_ies(i, mbie);
     460        2077 :                 fst_printf_iface(i, MSG_DEBUG, "multi-band IE set to %p", mbie);
     461             :         }
     462        1591 : }

Generated by: LCOV version 1.10