hostapd/src/fst/fst.c
Anton Nayshtut b47d05aa45 FST: Make FST peer connection check more permissive in hostapd
Modify the FST peer connection check so it won't skip peers without MB
IEs making it more permissive for peers that didn't provide MB IEs
during association request. This can be helpful, e.g., in cases where a
STA's interface connected before it was added to the FST group. This
allows the AP to receive FST Action frames and initiate session with a
STA via STA's interface that doesn't expose MB IEs.

The adjusted FST protocol is still safe, as it protects itself in many
other ways (checking band info and it's accordance to the interfaces,
Setup IDs, connection states of the interfaces involved, etc.)
effectively avoiding all types of invalid situations.

Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com>
2015-11-25 17:30:59 +02:00

225 lines
4.9 KiB
C

/*
* FST module implementation
* Copyright (c) 2014, Qualcomm Atheros, Inc.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "utils/includes.h"
#include "utils/common.h"
#include "utils/eloop.h"
#include "fst/fst.h"
#include "fst/fst_internal.h"
#include "fst/fst_defs.h"
#include "fst/fst_ctrl_iface.h"
struct dl_list fst_global_ctrls_list;
static void fst_ctrl_iface_notify_peer_state_change(struct fst_iface *iface,
Boolean connected,
const u8 *peer_addr)
{
union fst_event_extra extra;
extra.peer_state.connected = connected;
os_strlcpy(extra.peer_state.ifname, fst_iface_get_name(iface),
sizeof(extra.peer_state.ifname));
os_memcpy(extra.peer_state.addr, peer_addr, ETH_ALEN);
foreach_fst_ctrl_call(on_event, EVENT_PEER_STATE_CHANGED,
iface, NULL, &extra);
}
struct fst_iface * fst_attach(const char *ifname, const u8 *own_addr,
const struct fst_wpa_obj *iface_obj,
const struct fst_iface_cfg *cfg)
{
struct fst_group *g;
struct fst_group *group = NULL;
struct fst_iface *iface = NULL;
Boolean new_group = FALSE;
WPA_ASSERT(ifname != NULL);
WPA_ASSERT(iface_obj != NULL);
WPA_ASSERT(cfg != NULL);
foreach_fst_group(g) {
if (os_strcmp(cfg->group_id, fst_group_get_id(g)) == 0) {
group = g;
break;
}
}
if (!group) {
group = fst_group_create(cfg->group_id);
if (!group) {
fst_printf(MSG_ERROR, "%s: FST group cannot be created",
cfg->group_id);
return NULL;
}
new_group = TRUE;
}
iface = fst_iface_create(group, ifname, own_addr, iface_obj, cfg);
if (!iface) {
fst_printf_group(group, MSG_ERROR, "cannot create iface for %s",
ifname);
if (new_group)
fst_group_delete(group);
return NULL;
}
fst_group_attach_iface(group, iface);
fst_group_update_ie(group);
foreach_fst_ctrl_call(on_iface_added, iface);
fst_printf_iface(iface, MSG_DEBUG,
"iface attached to group %s (prio=%d, llt=%d)",
cfg->group_id, cfg->priority, cfg->llt);
return iface;
}
void fst_detach(struct fst_iface *iface)
{
struct fst_group *group = fst_iface_get_group(iface);
fst_printf_iface(iface, MSG_DEBUG, "iface detached from group %s",
fst_group_get_id(group));
fst_session_global_on_iface_detached(iface);
foreach_fst_ctrl_call(on_iface_removed, iface);
fst_group_detach_iface(group, iface);
fst_iface_delete(iface);
fst_group_update_ie(group);
fst_group_delete_if_empty(group);
}
int fst_global_init(void)
{
dl_list_init(&fst_global_groups_list);
dl_list_init(&fst_global_ctrls_list);
fst_session_global_init();
return 0;
}
void fst_global_deinit(void)
{
struct fst_group *group;
struct fst_ctrl_handle *h;
fst_session_global_deinit();
while ((group = fst_first_group()) != NULL)
fst_group_delete(group);
while ((h = dl_list_first(&fst_global_ctrls_list,
struct fst_ctrl_handle,
global_ctrls_lentry)))
fst_global_del_ctrl(h);
}
struct fst_ctrl_handle * fst_global_add_ctrl(const struct fst_ctrl *ctrl)
{
struct fst_ctrl_handle *h;
if (!ctrl)
return NULL;
h = os_zalloc(sizeof(*h));
if (!h)
return NULL;
if (ctrl->init && ctrl->init()) {
os_free(h);
return NULL;
}
h->ctrl = *ctrl;
dl_list_add_tail(&fst_global_ctrls_list, &h->global_ctrls_lentry);
return h;
}
void fst_global_del_ctrl(struct fst_ctrl_handle *h)
{
dl_list_del(&h->global_ctrls_lentry);
if (h->ctrl.deinit)
h->ctrl.deinit();
os_free(h);
}
void fst_rx_action(struct fst_iface *iface, const struct ieee80211_mgmt *mgmt,
size_t len)
{
if (fst_iface_is_connected(iface, mgmt->sa, FALSE))
fst_session_on_action_rx(iface, mgmt, len);
else
wpa_printf(MSG_DEBUG,
"FST: Ignore FST Action frame - no FST connection with "
MACSTR, MAC2STR(mgmt->sa));
}
void fst_notify_peer_connected(struct fst_iface *iface, const u8 *addr)
{
if (is_zero_ether_addr(addr))
return;
#ifndef HOSTAPD
fst_group_update_ie(fst_iface_get_group(iface));
#endif /* HOSTAPD */
fst_printf_iface(iface, MSG_DEBUG, MACSTR " became connected",
MAC2STR(addr));
fst_ctrl_iface_notify_peer_state_change(iface, TRUE, addr);
}
void fst_notify_peer_disconnected(struct fst_iface *iface, const u8 *addr)
{
if (is_zero_ether_addr(addr))
return;
#ifndef HOSTAPD
fst_group_update_ie(fst_iface_get_group(iface));
#endif /* HOSTAPD */
fst_printf_iface(iface, MSG_DEBUG, MACSTR " became disconnected",
MAC2STR(addr));
fst_ctrl_iface_notify_peer_state_change(iface, FALSE, addr);
}
Boolean fst_are_ifaces_aggregated(struct fst_iface *iface1,
struct fst_iface *iface2)
{
return fst_iface_get_group(iface1) == fst_iface_get_group(iface2);
}
enum mb_band_id fst_hw_mode_to_band(enum hostapd_hw_mode mode)
{
switch (mode) {
case HOSTAPD_MODE_IEEE80211B:
case HOSTAPD_MODE_IEEE80211G:
return MB_BAND_ID_WIFI_2_4GHZ;
case HOSTAPD_MODE_IEEE80211A:
return MB_BAND_ID_WIFI_5GHZ;
case HOSTAPD_MODE_IEEE80211AD:
return MB_BAND_ID_WIFI_60GHZ;
default:
WPA_ASSERT(0);
return MB_BAND_ID_WIFI_2_4GHZ;
}
}