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 static Boolean fst_has_fst_peer(struct fst_iface *iface, Boolean *has_peer) 23 { 24 const u8 *bssid; 25 26 bssid = fst_iface_get_bssid(iface); 27 if (!bssid) { 28 *has_peer = FALSE; 29 return FALSE; 30 } 31 32 *has_peer = TRUE; 33 return fst_iface_get_peer_mb_ie(iface, bssid) != NULL; 34 } 35 #endif /* HOSTAPD */ 36 37 38 static void fst_dump_mb_ies(const char *group_id, const char *ifname, 39 struct wpabuf *mbies) 40 { 41 const u8 *p = wpabuf_head(mbies); 42 size_t s = wpabuf_len(mbies); 43 44 while (s >= 2) { 45 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 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 p += 2 + mbie->len; 65 s -= 2 + mbie->len; 66 } 67 } 68 69 70 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 size_t len = sizeof(*mbie); 75 76 if (own_addr) 77 len += ETH_ALEN; 78 79 mbie = wpabuf_put(buf, len); 80 81 os_memset(mbie, 0, len); 82 83 mbie->eid = WLAN_EID_MULTI_BAND; 84 mbie->len = len - 2; 85 #ifdef HOSTAPD 86 mbie->mb_ctrl = MB_STA_ROLE_AP; 87 mbie->mb_connection_capability = MB_CONNECTION_CAPABILITY_AP; 88 #else /* HOSTAPD */ 89 mbie->mb_ctrl = MB_STA_ROLE_NON_PCP_NON_AP; 90 mbie->mb_connection_capability = 0; 91 #endif /* HOSTAPD */ 92 if (bssid) 93 os_memcpy(mbie->bssid, bssid, ETH_ALEN); 94 mbie->band_id = band; 95 mbie->op_class = 0; /* means all */ 96 mbie->chan = channel; 97 mbie->fst_session_tmout = FST_DEFAULT_SESSION_TIMEOUT_TU; 98 99 if (own_addr) { 100 mbie->mb_ctrl |= MB_CTRL_STA_MAC_PRESENT; 101 os_memcpy(&mbie[1], own_addr, ETH_ALEN); 102 } 103 } 104 105 106 static unsigned fst_fill_iface_mb_ies(struct fst_iface *f, struct wpabuf *buf) 107 { 108 const u8 *bssid; 109 110 bssid = fst_iface_get_bssid(f); 111 if (bssid) { 112 enum hostapd_hw_mode hw_mode; 113 u8 channel; 114 115 if (buf) { 116 fst_iface_get_channel_info(f, &hw_mode, &channel); 117 fst_fill_mb_ie(buf, bssid, fst_iface_get_addr(f), 118 fst_hw_mode_to_band(hw_mode), channel); 119 } 120 return 1; 121 } else { 122 unsigned bands[MB_BAND_ID_WIFI_60GHZ + 1] = {}; 123 struct hostapd_hw_modes *modes; 124 enum mb_band_id b; 125 int num_modes = fst_iface_get_hw_modes(f, &modes); 126 int ret = 0; 127 128 while (num_modes--) { 129 b = fst_hw_mode_to_band(modes->mode); 130 modes++; 131 if (b >= ARRAY_SIZE(bands) || bands[b]++) 132 continue; 133 ret++; 134 if (buf) 135 fst_fill_mb_ie(buf, NULL, fst_iface_get_addr(f), 136 b, MB_STA_CHANNEL_ALL); 137 } 138 return ret; 139 } 140 } 141 142 143 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 unsigned int nof_mbies = 0; 149 unsigned int nof_ifaces_added = 0; 150 #ifndef HOSTAPD 151 Boolean has_peer; 152 Boolean has_fst_peer; 153 154 foreach_fst_group_iface(g, f) { 155 has_fst_peer = fst_has_fst_peer(f, &has_peer); 156 if (has_peer && !has_fst_peer) 157 return NULL; 158 } 159 #endif /* HOSTAPD */ 160 161 foreach_fst_group_iface(g, f) { 162 if (f == i) 163 continue; 164 nof_mbies += fst_fill_iface_mb_ies(f, NULL); 165 } 166 167 buf = wpabuf_alloc(nof_mbies * 168 (sizeof(struct multi_band_ie) + ETH_ALEN)); 169 if (!buf) { 170 fst_printf_iface(i, MSG_ERROR, 171 "cannot allocate mem for %u MB IEs", 172 nof_mbies); 173 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 foreach_fst_group_iface(g, f) { 181 if (f == i) 182 continue; 183 184 fst_fill_iface_mb_ies(f, buf); 185 ++nof_ifaces_added; 186 187 fst_printf_iface(i, MSG_DEBUG, "added to MB IE"); 188 } 189 190 if (!nof_ifaces_added) { 191 wpabuf_free(buf); 192 buf = NULL; 193 fst_printf_iface(i, MSG_INFO, 194 "cannot add MB IE: no backup ifaces"); 195 } else { 196 fst_dump_mb_ies(fst_group_get_id(g), fst_iface_get_name(i), 197 buf); 198 } 199 200 return buf; 201 } 202 203 204 static const u8 * fst_mbie_get_peer_addr(const struct multi_band_ie *mbie) 205 { 206 const u8 *peer_addr = NULL; 207 208 switch (MB_CTRL_ROLE(mbie->mb_ctrl)) { 209 case MB_STA_ROLE_AP: 210 peer_addr = mbie->bssid; 211 break; 212 case MB_STA_ROLE_NON_PCP_NON_AP: 213 if (mbie->mb_ctrl & MB_CTRL_STA_MAC_PRESENT && 214 (size_t) 2 + mbie->len >= sizeof(*mbie) + ETH_ALEN) 215 peer_addr = (const u8 *) &mbie[1]; 216 break; 217 default: 218 break; 219 } 220 221 return peer_addr; 222 } 223 224 225 static struct fst_iface * 226 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 while (mb_ies_size >= 2) { 233 const struct multi_band_ie *mbie = 234 (const struct multi_band_ie *) mb_ies_buff; 235 236 if (mbie->eid != WLAN_EID_MULTI_BAND || 237 (size_t) 2 + mbie->len < sizeof(*mbie)) 238 break; 239 240 if (mbie->band_id == band_id) { 241 struct fst_iface *iface; 242 243 foreach_fst_group_iface(g, iface) { 244 const u8 *peer_addr = 245 fst_mbie_get_peer_addr(mbie); 246 247 if (peer_addr && 248 fst_iface_is_connected(iface, peer_addr) && 249 band_id == fst_iface_get_band_id(iface)) { 250 os_memcpy(iface_peer_addr, peer_addr, 251 ETH_ALEN); 252 return iface; 253 } 254 } 255 break; 256 } 257 258 mb_ies_buff += 2 + mbie->len; 259 mb_ies_size -= 2 + mbie->len; 260 } 261 262 return NULL; 263 } 264 265 266 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 foreach_fst_group_iface(g, f) { 272 const char *in = fst_iface_get_name(f); 273 274 if (os_strncmp(in, ifname, os_strlen(in)) == 0) 275 return f; 276 } 277 278 return NULL; 279 } 280 281 282 u8 fst_group_assign_dialog_token(struct fst_group *g) 283 { 284 g->dialog_token++; 285 if (g->dialog_token == 0) 286 g->dialog_token++; 287 return g->dialog_token; 288 } 289 290 291 u32 fst_group_assign_fsts_id(struct fst_group *g) 292 { 293 g->fsts_id++; 294 return g->fsts_id; 295 } 296 297 298 static Boolean 299 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 iface_addr = fst_iface_get_addr(iface); 313 iface_band_id = fst_iface_get_band_id(iface); 314 315 addr = fst_iface_get_peer_first(other, &ctx, TRUE); 316 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 mbies = fst_iface_get_peer_mb_ie(other, addr); 322 if (!mbies) 323 continue; 324 325 other_new_iface = fst_group_get_new_iface_by_mbie_and_band_id( 326 g, wpabuf_head(mbies), wpabuf_len(mbies), 327 iface_band_id, other_iface_peer_addr); 328 if (other_new_iface == iface && 329 os_memcmp(iface_addr, other_iface_peer_addr, 330 ETH_ALEN) != 0) { 331 os_memcpy(peer_addr, addr, ETH_ALEN); 332 return TRUE; 333 } 334 } 335 336 return FALSE; 337 } 338 339 340 struct fst_iface * 341 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 foreach_fst_group_iface(g, i) { 350 if (i == iface || 351 stie->new_band_id != fst_iface_get_band_id(i)) 352 continue; 353 if (fst_group_does_iface_appear_in_other_mbies(g, iface, i, 354 iface_peer_addr)) 355 return i; 356 break; 357 } 358 return NULL; 359 } 360 361 362 struct fst_iface * 363 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 return fst_group_get_new_iface_by_mbie_and_band_id( 368 g, mb_ies_buff, mb_ies_size, stie->new_band_id, 369 iface_peer_addr); 370 } 371 372 373 struct fst_group * fst_group_create(const char *group_id) 374 { 375 struct fst_group *g; 376 377 g = os_zalloc(sizeof(*g)); 378 if (g == NULL) { 379 fst_printf(MSG_ERROR, "%s: Cannot alloc group", group_id); 380 return NULL; 381 } 382 383 dl_list_init(&g->ifaces); 384 os_strlcpy(g->group_id, group_id, sizeof(g->group_id)); 385 386 dl_list_add_tail(&fst_global_groups_list, &g->global_groups_lentry); 387 fst_printf_group(g, MSG_DEBUG, "instance created"); 388 389 foreach_fst_ctrl_call(on_group_created, g); 390 391 return g; 392 } 393 394 395 void fst_group_attach_iface(struct fst_group *g, struct fst_iface *i) 396 { 397 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 foreach_fst_group_iface(g, f) { 408 if (fst_iface_get_priority(f) < fst_iface_get_priority(i)) 409 break; 410 list = &f->group_lentry; 411 } 412 dl_list_add(list, &i->group_lentry); 413 } 414 415 416 void fst_group_detach_iface(struct fst_group *g, struct fst_iface *i) 417 { 418 dl_list_del(&i->group_lentry); 419 } 420 421 422 void fst_group_delete(struct fst_group *group) 423 { 424 struct fst_session *s; 425 426 dl_list_del(&group->global_groups_lentry); 427 WPA_ASSERT(dl_list_empty(&group->ifaces)); 428 foreach_fst_ctrl_call(on_group_deleted, group); 429 fst_printf_group(group, MSG_DEBUG, "instance deleted"); 430 while ((s = fst_session_global_get_first_by_group(group)) != NULL) 431 fst_session_delete(s); 432 os_free(group); 433 } 434 435 436 Boolean fst_group_delete_if_empty(struct fst_group *group) 437 { 438 Boolean is_empty = !fst_group_has_ifaces(group) && 439 !fst_session_global_get_first_by_group(group); 440 441 if (is_empty) 442 fst_group_delete(group); 443 444 return is_empty; 445 } 446 447 448 void fst_group_update_ie(struct fst_group *g) 449 { 450 struct fst_iface *i; 451 452 foreach_fst_group_iface(g, i) { 453 struct wpabuf *mbie = fst_group_create_mb_ie(g, i); 454 455 if (!mbie) 456 fst_printf_iface(i, MSG_WARNING, "cannot create MB IE"); 457 458 fst_iface_attach_mbie(i, mbie); 459 fst_iface_set_ies(i, mbie); 460 fst_printf_iface(i, MSG_DEBUG, "multi-band IE set to %p", mbie); 461 } 462 } 463