Initializing repository
This commit is contained in:
8
src/fst/Makefile
Normal file
8
src/fst/Makefile
Normal file
@@ -0,0 +1,8 @@
|
||||
all:
|
||||
@echo Nothing to be made.
|
||||
|
||||
clean:
|
||||
rm -f *~ *.o *.d
|
||||
|
||||
install:
|
||||
@echo Nothing to be made.
|
||||
240
src/fst/fst.c
Normal file
240
src/fst/fst.c
Normal file
@@ -0,0 +1,240 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
static int fst_global_initialized = 0;
|
||||
struct dl_list fst_global_ctrls_list;
|
||||
|
||||
|
||||
static void fst_ctrl_iface_notify_peer_state_change(struct fst_iface *iface,
|
||||
bool 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;
|
||||
bool 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();
|
||||
fst_global_initialized = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void fst_global_deinit(void)
|
||||
{
|
||||
struct fst_group *group;
|
||||
struct fst_ctrl_handle *h;
|
||||
|
||||
if (!fst_global_initialized)
|
||||
return;
|
||||
|
||||
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);
|
||||
fst_global_initialized = 0;
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
bool fst_are_ifaces_aggregated(struct fst_iface *iface1,
|
||||
struct fst_iface *iface2)
|
||||
{
|
||||
return fst_iface_get_group(iface1) == fst_iface_get_group(iface2);
|
||||
}
|
||||
|
||||
|
||||
void fst_update_mac_addr(struct fst_iface *iface, const u8 *addr)
|
||||
{
|
||||
fst_printf_iface(iface, MSG_DEBUG, "new MAC address " MACSTR,
|
||||
MAC2STR(addr));
|
||||
os_memcpy(iface->own_addr, addr, sizeof(iface->own_addr));
|
||||
fst_group_update_ie(fst_iface_get_group(iface));
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
311
src/fst/fst.h
Normal file
311
src/fst/fst.h
Normal file
@@ -0,0 +1,311 @@
|
||||
/*
|
||||
* FST module - interface definitions
|
||||
* Copyright (c) 2014, Qualcomm Atheros, Inc.
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef FST_H
|
||||
#define FST_H
|
||||
|
||||
#ifdef CONFIG_FST
|
||||
|
||||
#include "common/defs.h"
|
||||
#include "fst/fst_ctrl_iface.h"
|
||||
|
||||
/* FST module hostap integration API */
|
||||
|
||||
#define US_IN_MS 1000
|
||||
#define LLT_UNIT_US 32 /* See 10.32.2.2 Transitioning between states */
|
||||
|
||||
/*
|
||||
* These were originally
|
||||
* #define FST_LLT_MS_TO_VAL(m) (((u32) (m)) * US_IN_MS / LLT_UNIT_US)
|
||||
* #define FST_LLT_VAL_TO_MS(v) (((u32) (v)) * LLT_UNIT_US / US_IN_MS)
|
||||
* #define FST_MAX_LLT_MS FST_LLT_VAL_TO_MS(-1)
|
||||
* but those can overflow 32-bit unsigned integer, so use alternative defines
|
||||
* to avoid undefined behavior with such overflow.
|
||||
* LLT_UNIT_US/US_IN_MS = 32/1000 = 4/125
|
||||
*/
|
||||
#define FST_LLT_MS_TO_VAL(m) (((u32) (m)) * 125 / 4)
|
||||
#define FST_LLT_VAL_TO_MS(v) (((u32) (v)) * 4 / 125)
|
||||
#define FST_MAX_LLT_MS (((u32) -1) / 4)
|
||||
#define FST_MAX_PRIO_VALUE ((u8) -1)
|
||||
#define FST_MAX_GROUP_ID_LEN IFNAMSIZ
|
||||
|
||||
#define FST_DEFAULT_LLT_CFG_VALUE 50
|
||||
|
||||
struct hostapd_hw_modes;
|
||||
struct ieee80211_mgmt;
|
||||
struct fst_iface;
|
||||
struct fst_group;
|
||||
struct fst_session;
|
||||
struct fst_get_peer_ctx;
|
||||
struct fst_ctrl_handle;
|
||||
|
||||
struct fst_wpa_obj {
|
||||
void *ctx;
|
||||
|
||||
/**
|
||||
* get_bssid - Get BSSID of the interface
|
||||
* @ctx: User context %ctx
|
||||
* Returns: BSSID for success, %NULL for failure.
|
||||
*
|
||||
* NOTE: For AP it returns the own BSSID, while for STA - the BSSID of
|
||||
* the associated AP.
|
||||
*/
|
||||
const u8 * (*get_bssid)(void *ctx);
|
||||
|
||||
/**
|
||||
* get_channel_info - Get current channel info
|
||||
* @ctx: User context %ctx
|
||||
* @hw_mode: OUT, current HW mode
|
||||
* @channel: OUT, current channel
|
||||
*/
|
||||
void (*get_channel_info)(void *ctx, enum hostapd_hw_mode *hw_mode,
|
||||
u8 *channel);
|
||||
|
||||
/**
|
||||
* get_hw_modes - Get hardware modes
|
||||
* @ctx: User context %ctx
|
||||
* @modes: OUT, pointer on array of hw modes
|
||||
*
|
||||
* Returns: Number of hw modes available.
|
||||
*/
|
||||
int (*get_hw_modes)(void *ctx, struct hostapd_hw_modes **modes);
|
||||
|
||||
/**
|
||||
* set_ies - Set interface's MB IE
|
||||
* @ctx: User context %ctx
|
||||
* @fst_ies: MB IE buffer (owned by FST module)
|
||||
*/
|
||||
void (*set_ies)(void *ctx, const struct wpabuf *fst_ies);
|
||||
|
||||
/**
|
||||
* send_action - Send FST Action frame via the interface
|
||||
* @ctx: User context %ctx
|
||||
* @addr: Address of the destination STA
|
||||
* @data: Action frame buffer
|
||||
* Returns: 0 for success, negative error code for failure.
|
||||
*/
|
||||
int (*send_action)(void *ctx, const u8 *addr, struct wpabuf *data);
|
||||
|
||||
/**
|
||||
* get_mb_ie - Get last MB IE received from STA
|
||||
* @ctx: User context %ctx
|
||||
* @addr: Address of the STA
|
||||
* Returns: MB IE buffer, %NULL if no MB IE received from the STA
|
||||
*/
|
||||
const struct wpabuf * (*get_mb_ie)(void *ctx, const u8 *addr);
|
||||
|
||||
/**
|
||||
* update_mb_ie - Update last MB IE received from STA
|
||||
* @ctx: User context %ctx
|
||||
* @addr: Address of the STA
|
||||
* @buf: Buffer that contains the MB IEs data
|
||||
* @size: Size of data in %buf
|
||||
*/
|
||||
void (*update_mb_ie)(void *ctx, const u8 *addr,
|
||||
const u8 *buf, size_t size);
|
||||
|
||||
/**
|
||||
* get_peer_first - Get MAC address of the 1st connected STA
|
||||
* @ctx: User context %ctx
|
||||
* @get_ctx: Context to be used for %get_peer_next call
|
||||
* @mb_only: %true if only multi-band capable peer should be reported
|
||||
* Returns: Address of the 1st connected STA, %NULL if no STAs connected
|
||||
*/
|
||||
const u8 * (*get_peer_first)(void *ctx,
|
||||
struct fst_get_peer_ctx **get_ctx,
|
||||
bool mb_only);
|
||||
/**
|
||||
* get_peer_next - Get MAC address of the next connected STA
|
||||
* @ctx: User context %ctx
|
||||
* @get_ctx: Context received from %get_peer_first or previous
|
||||
* %get_peer_next call
|
||||
* @mb_only: %true if only multi-band capable peer should be reported
|
||||
* Returns: Address of the next connected STA, %NULL if no more STAs
|
||||
* connected
|
||||
*/
|
||||
const u8 * (*get_peer_next)(void *ctx,
|
||||
struct fst_get_peer_ctx **get_ctx,
|
||||
bool mb_only);
|
||||
};
|
||||
|
||||
/**
|
||||
* fst_global_init - Global FST module initiator
|
||||
* Returns: 0 for success, negative error code for failure.
|
||||
* Note: The purpose of this function is to allocate and initiate global
|
||||
* FST module data structures (linked lists, static data etc.)
|
||||
* This function should be called prior to the 1st %fst_attach call.
|
||||
*/
|
||||
int fst_global_init(void);
|
||||
|
||||
/**
|
||||
* fst_global_deinit - Global FST module de-initiator
|
||||
* Note: The purpose of this function is to deallocate and de-initiate global
|
||||
* FST module data structures (linked lists, static data etc.)
|
||||
*/
|
||||
void fst_global_deinit(void);
|
||||
|
||||
/**
|
||||
* struct fst_ctrl - Notification interface for FST module
|
||||
*/
|
||||
struct fst_ctrl {
|
||||
/**
|
||||
* init - Initialize the notification interface
|
||||
* Returns: 0 for success, negative error code for failure.
|
||||
*/
|
||||
int (*init)(void);
|
||||
|
||||
/**
|
||||
* deinit - Deinitialize the notification interface
|
||||
*/
|
||||
void (*deinit)(void);
|
||||
|
||||
/**
|
||||
* on_group_created - Notify about FST group creation
|
||||
* Returns: 0 for success, negative error code for failure.
|
||||
*/
|
||||
int (*on_group_created)(struct fst_group *g);
|
||||
|
||||
/**
|
||||
* on_group_deleted - Notify about FST group deletion
|
||||
*/
|
||||
void (*on_group_deleted)(struct fst_group *g);
|
||||
|
||||
/**
|
||||
* on_iface_added - Notify about interface addition
|
||||
* Returns: 0 for success, negative error code for failure.
|
||||
*/
|
||||
int (*on_iface_added)(struct fst_iface *i);
|
||||
|
||||
/**
|
||||
* on_iface_removed - Notify about interface removal
|
||||
*/
|
||||
void (*on_iface_removed)(struct fst_iface *i);
|
||||
|
||||
/**
|
||||
* on_session_added - Notify about FST session addition
|
||||
* Returns: 0 for success, negative error code for failure.
|
||||
*/
|
||||
int (*on_session_added)(struct fst_session *s);
|
||||
|
||||
/**
|
||||
* on_session_removed - Notify about FST session removal
|
||||
*/
|
||||
void (*on_session_removed)(struct fst_session *s);
|
||||
|
||||
/**
|
||||
* on_event - Notify about FST event
|
||||
* @event_type: Event type
|
||||
* @i: Interface object that relates to the event or NULL
|
||||
* @g: Group object that relates to the event or NULL
|
||||
* @extra - Event specific data (see fst_ctrl_iface.h for more info)
|
||||
*/
|
||||
void (*on_event)(enum fst_event_type event_type, struct fst_iface *i,
|
||||
struct fst_session *s,
|
||||
const union fst_event_extra *extra);
|
||||
};
|
||||
|
||||
struct fst_ctrl_handle * fst_global_add_ctrl(const struct fst_ctrl *ctrl);
|
||||
void fst_global_del_ctrl(struct fst_ctrl_handle *h);
|
||||
|
||||
/**
|
||||
* NOTE: These values have to be read from configuration file
|
||||
*/
|
||||
struct fst_iface_cfg {
|
||||
char group_id[FST_MAX_GROUP_ID_LEN + 1];
|
||||
u8 priority;
|
||||
u32 llt;
|
||||
};
|
||||
|
||||
/**
|
||||
* fst_attach - Attach interface to an FST group according to configuration read
|
||||
* @ifname: Interface name
|
||||
* @own_addr: Own interface MAC address
|
||||
* @iface_obj: Callbacks to be used by FST module to communicate with
|
||||
* hostapd/wpa_supplicant
|
||||
* @cfg: FST-related interface configuration read from the configuration file
|
||||
* Returns: FST interface object for success, %NULL for failure.
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* fst_detach - Detach an interface
|
||||
* @iface: FST interface object
|
||||
*/
|
||||
void fst_detach(struct fst_iface *iface);
|
||||
|
||||
/* FST module inputs */
|
||||
/**
|
||||
* fst_rx_action - FST Action frames handler
|
||||
* @iface: FST interface object
|
||||
* @mgmt: Action frame arrived
|
||||
* @len: Action frame length
|
||||
*/
|
||||
void fst_rx_action(struct fst_iface *iface, const struct ieee80211_mgmt *mgmt,
|
||||
size_t len);
|
||||
|
||||
/**
|
||||
* fst_notify_peer_connected - FST STA connect handler
|
||||
* @iface: FST interface object
|
||||
* @addr: Address of the connected STA
|
||||
*/
|
||||
void fst_notify_peer_connected(struct fst_iface *iface, const u8 *addr);
|
||||
|
||||
/**
|
||||
* fst_notify_peer_disconnected - FST STA disconnect handler
|
||||
* @iface: FST interface object
|
||||
* @addr: Address of the disconnected STA
|
||||
*/
|
||||
void fst_notify_peer_disconnected(struct fst_iface *iface, const u8 *addr);
|
||||
|
||||
/* FST module auxiliary routines */
|
||||
|
||||
/**
|
||||
* fst_are_ifaces_aggregated - Determines whether 2 interfaces belong to the
|
||||
* same FST group
|
||||
* @iface1: 1st FST interface object
|
||||
* @iface1: 2nd FST interface object
|
||||
*
|
||||
* Returns: %true if the interfaces belong to the same FST group,
|
||||
* %false otherwise
|
||||
*/
|
||||
bool fst_are_ifaces_aggregated(struct fst_iface *iface1,
|
||||
struct fst_iface *iface2);
|
||||
|
||||
/**
|
||||
* fst_update_mac_addr - Notify FST about MAC address change
|
||||
* @iface: FST interface object
|
||||
* @addr: New MAC address
|
||||
*/
|
||||
void fst_update_mac_addr(struct fst_iface *iface, const u8 *addr);
|
||||
|
||||
#else /* CONFIG_FST */
|
||||
|
||||
static inline int fst_global_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int fst_global_start(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void fst_global_stop(void)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void fst_global_deinit(void)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* CONFIG_FST */
|
||||
|
||||
#endif /* FST_H */
|
||||
70
src/fst/fst_ctrl_aux.c
Normal file
70
src/fst/fst_ctrl_aux.c
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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 "common/defs.h"
|
||||
#include "fst_ctrl_defs.h"
|
||||
#include "fst_ctrl_aux.h"
|
||||
|
||||
|
||||
static const char *session_event_names[] = {
|
||||
[EVENT_FST_ESTABLISHED] = FST_PVAL_EVT_TYPE_ESTABLISHED,
|
||||
[EVENT_FST_SETUP] = FST_PVAL_EVT_TYPE_SETUP,
|
||||
[EVENT_FST_SESSION_STATE_CHANGED] = FST_PVAL_EVT_TYPE_SESSION_STATE,
|
||||
};
|
||||
|
||||
static const char *reason_names[] = {
|
||||
[REASON_TEARDOWN] = FST_CS_PVAL_REASON_TEARDOWN,
|
||||
[REASON_SETUP] = FST_CS_PVAL_REASON_SETUP,
|
||||
[REASON_SWITCH] = FST_CS_PVAL_REASON_SWITCH,
|
||||
[REASON_STT] = FST_CS_PVAL_REASON_STT,
|
||||
[REASON_REJECT] = FST_CS_PVAL_REASON_REJECT,
|
||||
[REASON_ERROR_PARAMS] = FST_CS_PVAL_REASON_ERROR_PARAMS,
|
||||
[REASON_RESET] = FST_CS_PVAL_REASON_RESET,
|
||||
[REASON_DETACH_IFACE] = FST_CS_PVAL_REASON_DETACH_IFACE,
|
||||
};
|
||||
|
||||
static const char *session_state_names[] = {
|
||||
[FST_SESSION_STATE_INITIAL] = FST_CS_PVAL_STATE_INITIAL,
|
||||
[FST_SESSION_STATE_SETUP_COMPLETION] =
|
||||
FST_CS_PVAL_STATE_SETUP_COMPLETION,
|
||||
[FST_SESSION_STATE_TRANSITION_DONE] = FST_CS_PVAL_STATE_TRANSITION_DONE,
|
||||
[FST_SESSION_STATE_TRANSITION_CONFIRMED] =
|
||||
FST_CS_PVAL_STATE_TRANSITION_CONFIRMED,
|
||||
};
|
||||
|
||||
|
||||
/* helpers */
|
||||
const char * fst_get_str_name(unsigned index, const char *names[],
|
||||
size_t names_size)
|
||||
{
|
||||
if (index >= names_size || !names[index])
|
||||
return FST_NAME_UNKNOWN;
|
||||
return names[index];
|
||||
}
|
||||
|
||||
|
||||
const char * fst_session_event_type_name(enum fst_event_type event)
|
||||
{
|
||||
return fst_get_str_name(event, session_event_names,
|
||||
ARRAY_SIZE(session_event_names));
|
||||
}
|
||||
|
||||
|
||||
const char * fst_reason_name(enum fst_reason reason)
|
||||
{
|
||||
return fst_get_str_name(reason, reason_names, ARRAY_SIZE(reason_names));
|
||||
}
|
||||
|
||||
|
||||
const char * fst_session_state_name(enum fst_session_state state)
|
||||
{
|
||||
return fst_get_str_name(state, session_state_names,
|
||||
ARRAY_SIZE(session_state_names));
|
||||
}
|
||||
93
src/fst/fst_ctrl_aux.h
Normal file
93
src/fst/fst_ctrl_aux.h
Normal file
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* FST module - miscellaneous definitions
|
||||
* Copyright (c) 2014, Qualcomm Atheros, Inc.
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef FST_CTRL_AUX_H
|
||||
#define FST_CTRL_AUX_H
|
||||
|
||||
#include "common/defs.h"
|
||||
|
||||
/* FST module control interface API */
|
||||
#define FST_INVALID_SESSION_ID ((u32) -1)
|
||||
#define FST_MAX_GROUP_ID_SIZE 32
|
||||
#define FST_MAX_INTERFACE_SIZE 32
|
||||
|
||||
enum fst_session_state {
|
||||
FST_SESSION_STATE_INITIAL,
|
||||
FST_SESSION_STATE_SETUP_COMPLETION,
|
||||
FST_SESSION_STATE_TRANSITION_DONE,
|
||||
FST_SESSION_STATE_TRANSITION_CONFIRMED,
|
||||
FST_SESSION_STATE_LAST
|
||||
};
|
||||
|
||||
enum fst_event_type {
|
||||
EVENT_FST_IFACE_STATE_CHANGED, /* An interface has been either attached
|
||||
* to or detached from an FST group */
|
||||
EVENT_FST_ESTABLISHED, /* FST Session has been established */
|
||||
EVENT_FST_SETUP, /* FST Session request received */
|
||||
EVENT_FST_SESSION_STATE_CHANGED,/* FST Session state has been changed */
|
||||
EVENT_PEER_STATE_CHANGED /* FST related generic event occurred,
|
||||
* see struct fst_hostap_event_data for
|
||||
* more info */
|
||||
};
|
||||
|
||||
enum fst_reason {
|
||||
REASON_TEARDOWN,
|
||||
REASON_SETUP,
|
||||
REASON_SWITCH,
|
||||
REASON_STT,
|
||||
REASON_REJECT,
|
||||
REASON_ERROR_PARAMS,
|
||||
REASON_RESET,
|
||||
REASON_DETACH_IFACE,
|
||||
};
|
||||
|
||||
enum fst_initiator {
|
||||
FST_INITIATOR_UNDEFINED,
|
||||
FST_INITIATOR_LOCAL,
|
||||
FST_INITIATOR_REMOTE,
|
||||
};
|
||||
|
||||
union fst_event_extra {
|
||||
struct fst_event_extra_iface_state {
|
||||
bool attached;
|
||||
char ifname[FST_MAX_INTERFACE_SIZE];
|
||||
char group_id[FST_MAX_GROUP_ID_SIZE];
|
||||
} iface_state; /* for EVENT_FST_IFACE_STATE_CHANGED */
|
||||
struct fst_event_extra_peer_state {
|
||||
bool connected;
|
||||
char ifname[FST_MAX_INTERFACE_SIZE];
|
||||
u8 addr[ETH_ALEN];
|
||||
} peer_state; /* for EVENT_PEER_STATE_CHANGED */
|
||||
struct fst_event_extra_session_state {
|
||||
enum fst_session_state old_state;
|
||||
enum fst_session_state new_state;
|
||||
union fst_session_state_switch_extra {
|
||||
struct {
|
||||
enum fst_reason reason;
|
||||
u8 reject_code; /* REASON_REJECT */
|
||||
/* REASON_SWITCH,
|
||||
* REASON_TEARDOWN,
|
||||
* REASON_REJECT
|
||||
*/
|
||||
enum fst_initiator initiator;
|
||||
} to_initial;
|
||||
} extra;
|
||||
} session_state; /* for EVENT_FST_SESSION_STATE_CHANGED */
|
||||
};
|
||||
|
||||
/* helpers - prints enum in string form */
|
||||
#define FST_NAME_UNKNOWN "UNKNOWN"
|
||||
|
||||
const char * fst_get_str_name(unsigned index, const char *names[],
|
||||
size_t names_size);
|
||||
|
||||
const char * fst_session_event_type_name(enum fst_event_type);
|
||||
const char * fst_reason_name(enum fst_reason reason);
|
||||
const char * fst_session_state_name(enum fst_session_state state);
|
||||
|
||||
#endif /* FST_CTRL_AUX_H */
|
||||
109
src/fst/fst_ctrl_defs.h
Normal file
109
src/fst/fst_ctrl_defs.h
Normal file
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* FST module - shared Control interface definitions
|
||||
* Copyright (c) 2014, Qualcomm Atheros, Inc.
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef FST_CTRL_DEFS_H
|
||||
#define FST_CTRL_DEFS_H
|
||||
|
||||
/* Undefined value */
|
||||
#define FST_CTRL_PVAL_NONE "NONE"
|
||||
|
||||
/* FST-ATTACH parameters */
|
||||
#define FST_ATTACH_CMD_PNAME_LLT "llt" /* pval = desired LLT */
|
||||
#define FST_ATTACH_CMD_PNAME_PRIORITY "priority" /* pval = desired priority */
|
||||
|
||||
/* FST-MANAGER parameters */
|
||||
/* FST Session states */
|
||||
#define FST_CS_PVAL_STATE_INITIAL "INITIAL"
|
||||
#define FST_CS_PVAL_STATE_SETUP_COMPLETION "SETUP_COMPLETION"
|
||||
#define FST_CS_PVAL_STATE_TRANSITION_DONE "TRANSITION_DONE"
|
||||
#define FST_CS_PVAL_STATE_TRANSITION_CONFIRMED "TRANSITION_CONFIRMED"
|
||||
|
||||
/* FST Session reset reasons */
|
||||
#define FST_CS_PVAL_REASON_TEARDOWN "REASON_TEARDOWN"
|
||||
#define FST_CS_PVAL_REASON_SETUP "REASON_SETUP"
|
||||
#define FST_CS_PVAL_REASON_SWITCH "REASON_SWITCH"
|
||||
#define FST_CS_PVAL_REASON_STT "REASON_STT"
|
||||
#define FST_CS_PVAL_REASON_REJECT "REASON_REJECT"
|
||||
#define FST_CS_PVAL_REASON_ERROR_PARAMS "REASON_ERROR_PARAMS"
|
||||
#define FST_CS_PVAL_REASON_RESET "REASON_RESET"
|
||||
#define FST_CS_PVAL_REASON_DETACH_IFACE "REASON_DETACH_IFACE"
|
||||
|
||||
/* FST Session responses */
|
||||
#define FST_CS_PVAL_RESPONSE_ACCEPT "ACCEPT"
|
||||
#define FST_CS_PVAL_RESPONSE_REJECT "REJECT"
|
||||
|
||||
/* FST Session action initiator */
|
||||
#define FST_CS_PVAL_INITIATOR_LOCAL "LOCAL"
|
||||
#define FST_CS_PVAL_INITIATOR_REMOTE "REMOTE"
|
||||
|
||||
/* FST-CLI subcommands and parameter names */
|
||||
#define FST_CMD_LIST_GROUPS "list_groups"
|
||||
#define FST_CMD_LIST_IFACES "list_ifaces"
|
||||
#define FST_CMD_IFACE_PEERS "iface_peers"
|
||||
#define FST_CMD_GET_PEER_MBIES "get_peer_mbies"
|
||||
#define FST_CMD_LIST_SESSIONS "list_sessions"
|
||||
#define FST_CMD_SESSION_ADD "session_add"
|
||||
#define FST_CMD_SESSION_REMOVE "session_remove"
|
||||
#define FST_CMD_SESSION_GET "session_get"
|
||||
#define FST_CSG_PNAME_OLD_PEER_ADDR "old_peer_addr" /* pval = address string */
|
||||
#define FST_CSG_PNAME_NEW_PEER_ADDR "new_peer_addr" /* pval = address string */
|
||||
#define FST_CSG_PNAME_OLD_IFNAME "old_ifname" /* pval = ifname */
|
||||
#define FST_CSG_PNAME_NEW_IFNAME "new_ifname" /* pval = ifname */
|
||||
#define FST_CSG_PNAME_LLT "llt" /* pval = numeric llt value */
|
||||
#define FST_CSG_PNAME_STATE "state" /* pval = FST_CS_PVAL_STATE_... */
|
||||
#define FST_CMD_SESSION_SET "session_set"
|
||||
#define FST_CSS_PNAME_OLD_PEER_ADDR FST_CSG_PNAME_OLD_PEER_ADDR
|
||||
#define FST_CSS_PNAME_NEW_PEER_ADDR FST_CSG_PNAME_NEW_PEER_ADDR
|
||||
#define FST_CSS_PNAME_OLD_IFNAME FST_CSG_PNAME_OLD_IFNAME
|
||||
#define FST_CSS_PNAME_NEW_IFNAME FST_CSG_PNAME_NEW_IFNAME
|
||||
#define FST_CSS_PNAME_LLT FST_CSG_PNAME_LLT
|
||||
#define FST_CMD_SESSION_INITIATE "session_initiate"
|
||||
#define FST_CMD_SESSION_RESPOND "session_respond"
|
||||
#define FST_CMD_SESSION_TRANSFER "session_transfer"
|
||||
#define FST_CMD_SESSION_TEARDOWN "session_teardown"
|
||||
|
||||
#ifdef CONFIG_FST_TEST
|
||||
#define FST_CTR_PVAL_BAD_NEW_BAND "bad_new_band"
|
||||
|
||||
#define FST_CMD_TEST_REQUEST "test_request"
|
||||
#define FST_CTR_IS_SUPPORTED "is_supported"
|
||||
#define FST_CTR_SEND_SETUP_REQUEST "send_setup_request"
|
||||
#define FST_CTR_SEND_SETUP_RESPONSE "send_setup_response"
|
||||
#define FST_CTR_SEND_ACK_REQUEST "send_ack_request"
|
||||
#define FST_CTR_SEND_ACK_RESPONSE "send_ack_response"
|
||||
#define FST_CTR_SEND_TEAR_DOWN "send_tear_down"
|
||||
#define FST_CTR_GET_FSTS_ID "get_fsts_id"
|
||||
#define FST_CTR_GET_LOCAL_MBIES "get_local_mbies"
|
||||
#endif /* CONFIG_FST_TEST */
|
||||
|
||||
/* Events */
|
||||
#define FST_CTRL_EVENT_IFACE "FST-EVENT-IFACE"
|
||||
#define FST_CEI_PNAME_IFNAME "ifname"
|
||||
#define FST_CEI_PNAME_GROUP "group"
|
||||
#define FST_CEI_PNAME_ATTACHED "attached"
|
||||
#define FST_CEI_PNAME_DETACHED "detached"
|
||||
#define FST_CTRL_EVENT_PEER "FST-EVENT-PEER"
|
||||
#define FST_CEP_PNAME_IFNAME "ifname"
|
||||
#define FST_CEP_PNAME_ADDR "peer_addr"
|
||||
#define FST_CEP_PNAME_CONNECTED "connected"
|
||||
#define FST_CEP_PNAME_DISCONNECTED "disconnected"
|
||||
#define FST_CTRL_EVENT_SESSION "FST-EVENT-SESSION"
|
||||
#define FST_CES_PNAME_SESSION_ID "session_id"
|
||||
#define FST_CES_PNAME_EVT_TYPE "event_type"
|
||||
#define FST_PVAL_EVT_TYPE_SESSION_STATE "EVENT_FST_SESSION_STATE"
|
||||
/* old_state/new_state: pval = FST_CS_PVAL_STATE_... */
|
||||
#define FST_CES_PNAME_OLD_STATE "old_state"
|
||||
#define FST_CES_PNAME_NEW_STATE "new_state"
|
||||
#define FST_CES_PNAME_REASON "reason" /* pval = FST_CS_PVAL_REASON_... */
|
||||
#define FST_CES_PNAME_REJECT_CODE "reject_code" /* pval = u8 code */
|
||||
/* pval = FST_CS_PVAL_INITIATOR_... */
|
||||
#define FST_CES_PNAME_INITIATOR "initiator"
|
||||
#define FST_PVAL_EVT_TYPE_ESTABLISHED "EVENT_FST_ESTABLISHED"
|
||||
#define FST_PVAL_EVT_TYPE_SETUP "EVENT_FST_SETUP"
|
||||
|
||||
#endif /* FST_CTRL_DEFS_H */
|
||||
948
src/fst/fst_ctrl_iface.c
Normal file
948
src/fst/fst_ctrl_iface.c
Normal file
@@ -0,0 +1,948 @@
|
||||
/*
|
||||
* FST module - Control Interface 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 "common/defs.h"
|
||||
#include "list.h"
|
||||
#include "fst/fst.h"
|
||||
#include "fst/fst_internal.h"
|
||||
#include "fst_ctrl_defs.h"
|
||||
#include "fst_ctrl_iface.h"
|
||||
|
||||
|
||||
static struct fst_group * get_fst_group_by_id(const char *id)
|
||||
{
|
||||
struct fst_group *g;
|
||||
|
||||
foreach_fst_group(g) {
|
||||
const char *group_id = fst_group_get_id(g);
|
||||
|
||||
if (os_strncmp(group_id, id, os_strlen(group_id)) == 0)
|
||||
return g;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* notifications */
|
||||
static bool format_session_state_extra(const union fst_event_extra *extra,
|
||||
char *buffer, size_t size)
|
||||
{
|
||||
int len;
|
||||
char reject_str[32] = FST_CTRL_PVAL_NONE;
|
||||
const char *initiator = FST_CTRL_PVAL_NONE;
|
||||
const struct fst_event_extra_session_state *ss;
|
||||
|
||||
ss = &extra->session_state;
|
||||
if (ss->new_state != FST_SESSION_STATE_INITIAL)
|
||||
return true;
|
||||
|
||||
switch (ss->extra.to_initial.reason) {
|
||||
case REASON_REJECT:
|
||||
if (ss->extra.to_initial.reject_code != WLAN_STATUS_SUCCESS)
|
||||
os_snprintf(reject_str, sizeof(reject_str), "%u",
|
||||
ss->extra.to_initial.reject_code);
|
||||
/* fall through */
|
||||
case REASON_TEARDOWN:
|
||||
case REASON_SWITCH:
|
||||
switch (ss->extra.to_initial.initiator) {
|
||||
case FST_INITIATOR_LOCAL:
|
||||
initiator = FST_CS_PVAL_INITIATOR_LOCAL;
|
||||
break;
|
||||
case FST_INITIATOR_REMOTE:
|
||||
initiator = FST_CS_PVAL_INITIATOR_REMOTE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
len = os_snprintf(buffer, size,
|
||||
FST_CES_PNAME_REASON "=%s "
|
||||
FST_CES_PNAME_REJECT_CODE "=%s "
|
||||
FST_CES_PNAME_INITIATOR "=%s",
|
||||
fst_reason_name(ss->extra.to_initial.reason),
|
||||
reject_str, initiator);
|
||||
|
||||
return !os_snprintf_error(size, len);
|
||||
}
|
||||
|
||||
|
||||
static void fst_ctrl_iface_notify(struct fst_iface *f, u32 session_id,
|
||||
enum fst_event_type event_type,
|
||||
const union fst_event_extra *extra)
|
||||
{
|
||||
struct fst_group *g;
|
||||
char extra_str[128] = "";
|
||||
const struct fst_event_extra_session_state *ss;
|
||||
const struct fst_event_extra_iface_state *is;
|
||||
const struct fst_event_extra_peer_state *ps;
|
||||
|
||||
/*
|
||||
* FST can use any of interface objects as it only sends messages
|
||||
* on global Control Interface, so we just pick the 1st one.
|
||||
*/
|
||||
|
||||
if (!f) {
|
||||
foreach_fst_group(g) {
|
||||
f = fst_group_first_iface(g);
|
||||
if (f)
|
||||
break;
|
||||
}
|
||||
if (!f)
|
||||
return;
|
||||
}
|
||||
|
||||
WPA_ASSERT(f->iface_obj.ctx);
|
||||
|
||||
switch (event_type) {
|
||||
case EVENT_FST_IFACE_STATE_CHANGED:
|
||||
if (!extra)
|
||||
return;
|
||||
is = &extra->iface_state;
|
||||
wpa_msg_global_only(f->iface_obj.ctx, MSG_INFO,
|
||||
FST_CTRL_EVENT_IFACE " %s "
|
||||
FST_CEI_PNAME_IFNAME "=%s "
|
||||
FST_CEI_PNAME_GROUP "=%s",
|
||||
is->attached ? FST_CEI_PNAME_ATTACHED :
|
||||
FST_CEI_PNAME_DETACHED,
|
||||
is->ifname, is->group_id);
|
||||
break;
|
||||
case EVENT_PEER_STATE_CHANGED:
|
||||
if (!extra)
|
||||
return;
|
||||
ps = &extra->peer_state;
|
||||
wpa_msg_global_only(fst_iface_get_wpa_obj_ctx(f), MSG_INFO,
|
||||
FST_CTRL_EVENT_PEER " %s "
|
||||
FST_CEP_PNAME_IFNAME "=%s "
|
||||
FST_CEP_PNAME_ADDR "=" MACSTR,
|
||||
ps->connected ? FST_CEP_PNAME_CONNECTED :
|
||||
FST_CEP_PNAME_DISCONNECTED,
|
||||
ps->ifname, MAC2STR(ps->addr));
|
||||
break;
|
||||
case EVENT_FST_SESSION_STATE_CHANGED:
|
||||
if (!extra)
|
||||
return;
|
||||
if (!format_session_state_extra(extra, extra_str,
|
||||
sizeof(extra_str))) {
|
||||
fst_printf(MSG_ERROR,
|
||||
"CTRL: Cannot format STATE_CHANGE extra");
|
||||
extra_str[0] = 0;
|
||||
}
|
||||
ss = &extra->session_state;
|
||||
wpa_msg_global_only(fst_iface_get_wpa_obj_ctx(f), MSG_INFO,
|
||||
FST_CTRL_EVENT_SESSION " "
|
||||
FST_CES_PNAME_SESSION_ID "=%u "
|
||||
FST_CES_PNAME_EVT_TYPE "=%s "
|
||||
FST_CES_PNAME_OLD_STATE "=%s "
|
||||
FST_CES_PNAME_NEW_STATE "=%s %s",
|
||||
session_id,
|
||||
fst_session_event_type_name(event_type),
|
||||
fst_session_state_name(ss->old_state),
|
||||
fst_session_state_name(ss->new_state),
|
||||
extra_str);
|
||||
break;
|
||||
case EVENT_FST_ESTABLISHED:
|
||||
case EVENT_FST_SETUP:
|
||||
wpa_msg_global_only(fst_iface_get_wpa_obj_ctx(f), MSG_INFO,
|
||||
FST_CTRL_EVENT_SESSION " "
|
||||
FST_CES_PNAME_SESSION_ID "=%u "
|
||||
FST_CES_PNAME_EVT_TYPE "=%s",
|
||||
session_id,
|
||||
fst_session_event_type_name(event_type));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* command processors */
|
||||
|
||||
/* fst session_get */
|
||||
static int session_get(const char *session_id, char *buf, size_t buflen)
|
||||
{
|
||||
struct fst_session *s;
|
||||
struct fst_iface *new_iface, *old_iface;
|
||||
const u8 *old_peer_addr, *new_peer_addr;
|
||||
u32 id;
|
||||
|
||||
id = strtoul(session_id, NULL, 0);
|
||||
|
||||
s = fst_session_get_by_id(id);
|
||||
if (!s) {
|
||||
fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
|
||||
return os_snprintf(buf, buflen, "FAIL\n");
|
||||
}
|
||||
|
||||
old_peer_addr = fst_session_get_peer_addr(s, true);
|
||||
new_peer_addr = fst_session_get_peer_addr(s, false);
|
||||
new_iface = fst_session_get_iface(s, false);
|
||||
old_iface = fst_session_get_iface(s, true);
|
||||
|
||||
return os_snprintf(buf, buflen,
|
||||
FST_CSG_PNAME_OLD_PEER_ADDR "=" MACSTR "\n"
|
||||
FST_CSG_PNAME_NEW_PEER_ADDR "=" MACSTR "\n"
|
||||
FST_CSG_PNAME_NEW_IFNAME "=%s\n"
|
||||
FST_CSG_PNAME_OLD_IFNAME "=%s\n"
|
||||
FST_CSG_PNAME_LLT "=%u\n"
|
||||
FST_CSG_PNAME_STATE "=%s\n",
|
||||
MAC2STR(old_peer_addr),
|
||||
MAC2STR(new_peer_addr),
|
||||
new_iface ? fst_iface_get_name(new_iface) :
|
||||
FST_CTRL_PVAL_NONE,
|
||||
old_iface ? fst_iface_get_name(old_iface) :
|
||||
FST_CTRL_PVAL_NONE,
|
||||
fst_session_get_llt(s),
|
||||
fst_session_state_name(fst_session_get_state(s)));
|
||||
}
|
||||
|
||||
|
||||
/* fst session_set */
|
||||
static int session_set(const char *session_id, char *buf, size_t buflen)
|
||||
{
|
||||
struct fst_session *s;
|
||||
char *p, *q;
|
||||
u32 id;
|
||||
int ret;
|
||||
|
||||
id = strtoul(session_id, &p, 0);
|
||||
|
||||
s = fst_session_get_by_id(id);
|
||||
if (!s) {
|
||||
fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
|
||||
return os_snprintf(buf, buflen, "FAIL\n");
|
||||
}
|
||||
|
||||
if (*p != ' ' || !(q = os_strchr(p + 1, '=')))
|
||||
return os_snprintf(buf, buflen, "FAIL\n");
|
||||
p++;
|
||||
|
||||
if (os_strncasecmp(p, FST_CSS_PNAME_OLD_IFNAME, q - p) == 0) {
|
||||
ret = fst_session_set_str_ifname(s, q + 1, true);
|
||||
} else if (os_strncasecmp(p, FST_CSS_PNAME_NEW_IFNAME, q - p) == 0) {
|
||||
ret = fst_session_set_str_ifname(s, q + 1, false);
|
||||
} else if (os_strncasecmp(p, FST_CSS_PNAME_OLD_PEER_ADDR, q - p) == 0) {
|
||||
ret = fst_session_set_str_peer_addr(s, q + 1, true);
|
||||
} else if (os_strncasecmp(p, FST_CSS_PNAME_NEW_PEER_ADDR, q - p) == 0) {
|
||||
ret = fst_session_set_str_peer_addr(s, q + 1, false);
|
||||
} else if (os_strncasecmp(p, FST_CSS_PNAME_LLT, q - p) == 0) {
|
||||
ret = fst_session_set_str_llt(s, q + 1);
|
||||
} else {
|
||||
fst_printf(MSG_ERROR, "CTRL: Unknown parameter: %s", p);
|
||||
return os_snprintf(buf, buflen, "FAIL\n");
|
||||
}
|
||||
|
||||
return os_snprintf(buf, buflen, "%s\n", ret ? "FAIL" : "OK");
|
||||
}
|
||||
|
||||
|
||||
/* fst session_add/remove */
|
||||
static int session_add(const char *group_id, char *buf, size_t buflen)
|
||||
{
|
||||
struct fst_group *g;
|
||||
struct fst_session *s;
|
||||
|
||||
g = get_fst_group_by_id(group_id);
|
||||
if (!g) {
|
||||
fst_printf(MSG_WARNING, "CTRL: Cannot find group '%s'",
|
||||
group_id);
|
||||
return os_snprintf(buf, buflen, "FAIL\n");
|
||||
}
|
||||
|
||||
s = fst_session_create(g);
|
||||
if (!s) {
|
||||
fst_printf(MSG_ERROR,
|
||||
"CTRL: Cannot create session for group '%s'",
|
||||
group_id);
|
||||
return os_snprintf(buf, buflen, "FAIL\n");
|
||||
}
|
||||
|
||||
return os_snprintf(buf, buflen, "%u\n", fst_session_get_id(s));
|
||||
}
|
||||
|
||||
|
||||
static int session_remove(const char *session_id, char *buf, size_t buflen)
|
||||
{
|
||||
struct fst_session *s;
|
||||
struct fst_group *g;
|
||||
u32 id;
|
||||
|
||||
id = strtoul(session_id, NULL, 0);
|
||||
|
||||
s = fst_session_get_by_id(id);
|
||||
if (!s) {
|
||||
fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
|
||||
return os_snprintf(buf, buflen, "FAIL\n");
|
||||
}
|
||||
|
||||
g = fst_session_get_group(s);
|
||||
fst_session_reset(s);
|
||||
fst_session_delete(s);
|
||||
fst_group_delete_if_empty(g);
|
||||
|
||||
return os_snprintf(buf, buflen, "OK\n");
|
||||
}
|
||||
|
||||
|
||||
/* fst session_initiate */
|
||||
static int session_initiate(const char *session_id, char *buf, size_t buflen)
|
||||
{
|
||||
struct fst_session *s;
|
||||
u32 id;
|
||||
|
||||
id = strtoul(session_id, NULL, 0);
|
||||
|
||||
s = fst_session_get_by_id(id);
|
||||
if (!s) {
|
||||
fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
|
||||
return os_snprintf(buf, buflen, "FAIL\n");
|
||||
}
|
||||
|
||||
if (fst_session_initiate_setup(s)) {
|
||||
fst_printf(MSG_WARNING, "CTRL: Cannot initiate session %u", id);
|
||||
return os_snprintf(buf, buflen, "FAIL\n");
|
||||
}
|
||||
|
||||
return os_snprintf(buf, buflen, "OK\n");
|
||||
}
|
||||
|
||||
|
||||
/* fst session_respond */
|
||||
static int session_respond(const char *session_id, char *buf, size_t buflen)
|
||||
{
|
||||
struct fst_session *s;
|
||||
char *p;
|
||||
u32 id;
|
||||
u8 status_code;
|
||||
|
||||
id = strtoul(session_id, &p, 0);
|
||||
|
||||
s = fst_session_get_by_id(id);
|
||||
if (!s) {
|
||||
fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
|
||||
return os_snprintf(buf, buflen, "FAIL\n");
|
||||
}
|
||||
|
||||
if (*p != ' ')
|
||||
return os_snprintf(buf, buflen, "FAIL\n");
|
||||
p++;
|
||||
|
||||
if (!os_strcasecmp(p, FST_CS_PVAL_RESPONSE_ACCEPT)) {
|
||||
status_code = WLAN_STATUS_SUCCESS;
|
||||
} else if (!os_strcasecmp(p, FST_CS_PVAL_RESPONSE_REJECT)) {
|
||||
status_code = WLAN_STATUS_PENDING_ADMITTING_FST_SESSION;
|
||||
} else {
|
||||
fst_printf(MSG_WARNING,
|
||||
"CTRL: session %u: unknown response status: %s",
|
||||
id, p);
|
||||
return os_snprintf(buf, buflen, "FAIL\n");
|
||||
}
|
||||
|
||||
if (fst_session_respond(s, status_code)) {
|
||||
fst_printf(MSG_WARNING, "CTRL: Cannot respond to session %u",
|
||||
id);
|
||||
return os_snprintf(buf, buflen, "FAIL\n");
|
||||
}
|
||||
|
||||
fst_printf(MSG_INFO, "CTRL: session %u responded", id);
|
||||
|
||||
return os_snprintf(buf, buflen, "OK\n");
|
||||
}
|
||||
|
||||
|
||||
/* fst session_transfer */
|
||||
static int session_transfer(const char *session_id, char *buf, size_t buflen)
|
||||
{
|
||||
struct fst_session *s;
|
||||
u32 id;
|
||||
|
||||
id = strtoul(session_id, NULL, 0);
|
||||
|
||||
s = fst_session_get_by_id(id);
|
||||
if (!s) {
|
||||
fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
|
||||
return os_snprintf(buf, buflen, "FAIL\n");
|
||||
}
|
||||
|
||||
if (fst_session_initiate_switch(s)) {
|
||||
fst_printf(MSG_WARNING,
|
||||
"CTRL: Cannot initiate ST for session %u", id);
|
||||
return os_snprintf(buf, buflen, "FAIL\n");
|
||||
}
|
||||
|
||||
return os_snprintf(buf, buflen, "OK\n");
|
||||
}
|
||||
|
||||
|
||||
/* fst session_teardown */
|
||||
static int session_teardown(const char *session_id, char *buf, size_t buflen)
|
||||
{
|
||||
struct fst_session *s;
|
||||
u32 id;
|
||||
|
||||
id = strtoul(session_id, NULL, 0);
|
||||
|
||||
s = fst_session_get_by_id(id);
|
||||
if (!s) {
|
||||
fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
|
||||
return os_snprintf(buf, buflen, "FAIL\n");
|
||||
}
|
||||
|
||||
if (fst_session_tear_down_setup(s)) {
|
||||
fst_printf(MSG_WARNING, "CTRL: Cannot tear down session %u",
|
||||
id);
|
||||
return os_snprintf(buf, buflen, "FAIL\n");
|
||||
}
|
||||
|
||||
return os_snprintf(buf, buflen, "OK\n");
|
||||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_FST_TEST
|
||||
/* fst test_request */
|
||||
static int test_request(const char *request, char *buf, size_t buflen)
|
||||
{
|
||||
const char *p = request;
|
||||
int ret;
|
||||
|
||||
if (!os_strncasecmp(p, FST_CTR_SEND_SETUP_REQUEST,
|
||||
os_strlen(FST_CTR_SEND_SETUP_REQUEST))) {
|
||||
ret = fst_test_req_send_fst_request(
|
||||
p + os_strlen(FST_CTR_SEND_SETUP_REQUEST));
|
||||
} else if (!os_strncasecmp(p, FST_CTR_SEND_SETUP_RESPONSE,
|
||||
os_strlen(FST_CTR_SEND_SETUP_RESPONSE))) {
|
||||
ret = fst_test_req_send_fst_response(
|
||||
p + os_strlen(FST_CTR_SEND_SETUP_RESPONSE));
|
||||
} else if (!os_strncasecmp(p, FST_CTR_SEND_ACK_REQUEST,
|
||||
os_strlen(FST_CTR_SEND_ACK_REQUEST))) {
|
||||
ret = fst_test_req_send_ack_request(
|
||||
p + os_strlen(FST_CTR_SEND_ACK_REQUEST));
|
||||
} else if (!os_strncasecmp(p, FST_CTR_SEND_ACK_RESPONSE,
|
||||
os_strlen(FST_CTR_SEND_ACK_RESPONSE))) {
|
||||
ret = fst_test_req_send_ack_response(
|
||||
p + os_strlen(FST_CTR_SEND_ACK_RESPONSE));
|
||||
} else if (!os_strncasecmp(p, FST_CTR_SEND_TEAR_DOWN,
|
||||
os_strlen(FST_CTR_SEND_TEAR_DOWN))) {
|
||||
ret = fst_test_req_send_tear_down(
|
||||
p + os_strlen(FST_CTR_SEND_TEAR_DOWN));
|
||||
} else if (!os_strncasecmp(p, FST_CTR_GET_FSTS_ID,
|
||||
os_strlen(FST_CTR_GET_FSTS_ID))) {
|
||||
u32 fsts_id = fst_test_req_get_fsts_id(
|
||||
p + os_strlen(FST_CTR_GET_FSTS_ID));
|
||||
if (fsts_id != FST_FSTS_ID_NOT_FOUND)
|
||||
return os_snprintf(buf, buflen, "%u\n", fsts_id);
|
||||
return os_snprintf(buf, buflen, "FAIL\n");
|
||||
} else if (!os_strncasecmp(p, FST_CTR_GET_LOCAL_MBIES,
|
||||
os_strlen(FST_CTR_GET_LOCAL_MBIES))) {
|
||||
return fst_test_req_get_local_mbies(
|
||||
p + os_strlen(FST_CTR_GET_LOCAL_MBIES), buf, buflen);
|
||||
} else if (!os_strncasecmp(p, FST_CTR_IS_SUPPORTED,
|
||||
os_strlen(FST_CTR_IS_SUPPORTED))) {
|
||||
ret = 0;
|
||||
} else {
|
||||
fst_printf(MSG_ERROR, "CTRL: Unknown parameter: %s", p);
|
||||
return os_snprintf(buf, buflen, "FAIL\n");
|
||||
}
|
||||
|
||||
return os_snprintf(buf, buflen, "%s\n", ret ? "FAIL" : "OK");
|
||||
}
|
||||
#endif /* CONFIG_FST_TEST */
|
||||
|
||||
|
||||
/* fst list_sessions */
|
||||
struct list_sessions_cb_ctx {
|
||||
char *buf;
|
||||
size_t buflen;
|
||||
size_t reply_len;
|
||||
};
|
||||
|
||||
|
||||
static void list_session_enum_cb(struct fst_group *g, struct fst_session *s,
|
||||
void *ctx)
|
||||
{
|
||||
struct list_sessions_cb_ctx *c = ctx;
|
||||
int ret;
|
||||
|
||||
ret = os_snprintf(c->buf, c->buflen, " %u", fst_session_get_id(s));
|
||||
|
||||
c->buf += ret;
|
||||
c->buflen -= ret;
|
||||
c->reply_len += ret;
|
||||
}
|
||||
|
||||
|
||||
static int list_sessions(const char *group_id, char *buf, size_t buflen)
|
||||
{
|
||||
struct list_sessions_cb_ctx ctx;
|
||||
struct fst_group *g;
|
||||
|
||||
g = get_fst_group_by_id(group_id);
|
||||
if (!g) {
|
||||
fst_printf(MSG_WARNING, "CTRL: Cannot find group '%s'",
|
||||
group_id);
|
||||
return os_snprintf(buf, buflen, "FAIL\n");
|
||||
}
|
||||
|
||||
ctx.buf = buf;
|
||||
ctx.buflen = buflen;
|
||||
ctx.reply_len = 0;
|
||||
|
||||
fst_session_enum(g, list_session_enum_cb, &ctx);
|
||||
|
||||
ctx.reply_len += os_snprintf(buf + ctx.reply_len, ctx.buflen, "\n");
|
||||
|
||||
return ctx.reply_len;
|
||||
}
|
||||
|
||||
|
||||
/* fst iface_peers */
|
||||
static int iface_peers(const char *group_id, char *buf, size_t buflen)
|
||||
{
|
||||
const char *ifname;
|
||||
struct fst_group *g;
|
||||
struct fst_iface *f;
|
||||
struct fst_get_peer_ctx *ctx;
|
||||
const u8 *addr;
|
||||
unsigned found = 0;
|
||||
int ret = 0;
|
||||
|
||||
g = get_fst_group_by_id(group_id);
|
||||
if (!g) {
|
||||
fst_printf(MSG_WARNING, "CTRL: Cannot find group '%s'",
|
||||
group_id);
|
||||
return os_snprintf(buf, buflen, "FAIL\n");
|
||||
}
|
||||
|
||||
ifname = os_strchr(group_id, ' ');
|
||||
if (!ifname)
|
||||
return os_snprintf(buf, buflen, "FAIL\n");
|
||||
ifname++;
|
||||
|
||||
foreach_fst_group_iface(g, f) {
|
||||
const char *in = fst_iface_get_name(f);
|
||||
|
||||
if (os_strncmp(ifname, in, os_strlen(in)) == 0) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
return os_snprintf(buf, buflen, "FAIL\n");
|
||||
|
||||
addr = fst_iface_get_peer_first(f, &ctx, false);
|
||||
for (; addr != NULL; addr = fst_iface_get_peer_next(f, &ctx, false)) {
|
||||
int res;
|
||||
|
||||
res = os_snprintf(buf + ret, buflen - ret, MACSTR "\n",
|
||||
MAC2STR(addr));
|
||||
if (os_snprintf_error(buflen - ret, res))
|
||||
break;
|
||||
ret += res;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int get_peer_mbies(const char *params, char *buf, size_t buflen)
|
||||
{
|
||||
char *endp;
|
||||
char ifname[FST_MAX_INTERFACE_SIZE];
|
||||
u8 peer_addr[ETH_ALEN];
|
||||
struct fst_group *g;
|
||||
struct fst_iface *iface = NULL;
|
||||
const struct wpabuf *mbies;
|
||||
|
||||
if (fst_read_next_text_param(params, ifname, sizeof(ifname), &endp) ||
|
||||
!*ifname)
|
||||
goto problem;
|
||||
|
||||
while (isspace(*endp))
|
||||
endp++;
|
||||
if (fst_read_peer_addr(endp, peer_addr))
|
||||
goto problem;
|
||||
|
||||
foreach_fst_group(g) {
|
||||
iface = fst_group_get_iface_by_name(g, ifname);
|
||||
if (iface)
|
||||
break;
|
||||
}
|
||||
if (!iface)
|
||||
goto problem;
|
||||
|
||||
mbies = fst_iface_get_peer_mb_ie(iface, peer_addr);
|
||||
if (!mbies)
|
||||
goto problem;
|
||||
|
||||
return wpa_snprintf_hex(buf, buflen, wpabuf_head(mbies),
|
||||
wpabuf_len(mbies));
|
||||
|
||||
problem:
|
||||
return os_snprintf(buf, buflen, "FAIL\n");
|
||||
}
|
||||
|
||||
|
||||
/* fst list_ifaces */
|
||||
static int list_ifaces(const char *group_id, char *buf, size_t buflen)
|
||||
{
|
||||
struct fst_group *g;
|
||||
struct fst_iface *f;
|
||||
int ret = 0;
|
||||
|
||||
g = get_fst_group_by_id(group_id);
|
||||
if (!g) {
|
||||
fst_printf(MSG_WARNING, "CTRL: Cannot find group '%s'",
|
||||
group_id);
|
||||
return os_snprintf(buf, buflen, "FAIL\n");
|
||||
}
|
||||
|
||||
foreach_fst_group_iface(g, f) {
|
||||
int res;
|
||||
const u8 *iface_addr = fst_iface_get_addr(f);
|
||||
|
||||
res = os_snprintf(buf + ret, buflen - ret,
|
||||
"%s|" MACSTR "|%u|%u\n",
|
||||
fst_iface_get_name(f),
|
||||
MAC2STR(iface_addr),
|
||||
fst_iface_get_priority(f),
|
||||
fst_iface_get_llt(f));
|
||||
if (os_snprintf_error(buflen - ret, res))
|
||||
break;
|
||||
ret += res;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* fst list_groups */
|
||||
static int list_groups(const char *cmd, char *buf, size_t buflen)
|
||||
{
|
||||
struct fst_group *g;
|
||||
int ret = 0;
|
||||
|
||||
foreach_fst_group(g) {
|
||||
int res;
|
||||
|
||||
res = os_snprintf(buf + ret, buflen - ret, "%s\n",
|
||||
fst_group_get_id(g));
|
||||
if (os_snprintf_error(buflen - ret, res))
|
||||
break;
|
||||
ret += res;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static const char * band_freq(enum mb_band_id band)
|
||||
{
|
||||
static const char *band_names[] = {
|
||||
[MB_BAND_ID_WIFI_2_4GHZ] = "2.4GHZ",
|
||||
[MB_BAND_ID_WIFI_5GHZ] = "5GHZ",
|
||||
[MB_BAND_ID_WIFI_60GHZ] = "60GHZ",
|
||||
};
|
||||
|
||||
return fst_get_str_name(band, band_names, ARRAY_SIZE(band_names));
|
||||
}
|
||||
|
||||
|
||||
static int print_band(unsigned num, struct fst_iface *iface, const u8 *addr,
|
||||
char *buf, size_t buflen)
|
||||
{
|
||||
const struct wpabuf *wpabuf;
|
||||
enum hostapd_hw_mode hw_mode;
|
||||
u8 channel;
|
||||
int ret = 0;
|
||||
|
||||
fst_iface_get_channel_info(iface, &hw_mode, &channel);
|
||||
|
||||
ret += os_snprintf(buf + ret, buflen - ret, "band%u_frequency=%s\n",
|
||||
num, band_freq(fst_hw_mode_to_band(hw_mode)));
|
||||
ret += os_snprintf(buf + ret, buflen - ret, "band%u_iface=%s\n",
|
||||
num, fst_iface_get_name(iface));
|
||||
wpabuf = fst_iface_get_peer_mb_ie(iface, addr);
|
||||
if (wpabuf) {
|
||||
ret += os_snprintf(buf + ret, buflen - ret, "band%u_mb_ies=",
|
||||
num);
|
||||
ret += wpa_snprintf_hex(buf + ret, buflen - ret,
|
||||
wpabuf_head(wpabuf),
|
||||
wpabuf_len(wpabuf));
|
||||
ret += os_snprintf(buf + ret, buflen - ret, "\n");
|
||||
}
|
||||
ret += os_snprintf(buf + ret, buflen - ret, "band%u_fst_group_id=%s\n",
|
||||
num, fst_iface_get_group_id(iface));
|
||||
ret += os_snprintf(buf + ret, buflen - ret, "band%u_fst_priority=%u\n",
|
||||
num, fst_iface_get_priority(iface));
|
||||
ret += os_snprintf(buf + ret, buflen - ret, "band%u_fst_llt=%u\n",
|
||||
num, fst_iface_get_llt(iface));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static void fst_ctrl_iface_on_iface_state_changed(struct fst_iface *i,
|
||||
bool attached)
|
||||
{
|
||||
union fst_event_extra extra;
|
||||
|
||||
os_memset(&extra, 0, sizeof(extra));
|
||||
extra.iface_state.attached = attached;
|
||||
os_strlcpy(extra.iface_state.ifname, fst_iface_get_name(i),
|
||||
sizeof(extra.iface_state.ifname));
|
||||
os_strlcpy(extra.iface_state.group_id, fst_iface_get_group_id(i),
|
||||
sizeof(extra.iface_state.group_id));
|
||||
|
||||
fst_ctrl_iface_notify(i, FST_INVALID_SESSION_ID,
|
||||
EVENT_FST_IFACE_STATE_CHANGED, &extra);
|
||||
}
|
||||
|
||||
|
||||
static int fst_ctrl_iface_on_iface_added(struct fst_iface *i)
|
||||
{
|
||||
fst_ctrl_iface_on_iface_state_changed(i, true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void fst_ctrl_iface_on_iface_removed(struct fst_iface *i)
|
||||
{
|
||||
fst_ctrl_iface_on_iface_state_changed(i, false);
|
||||
}
|
||||
|
||||
|
||||
static void fst_ctrl_iface_on_event(enum fst_event_type event_type,
|
||||
struct fst_iface *i, struct fst_session *s,
|
||||
const union fst_event_extra *extra)
|
||||
{
|
||||
u32 session_id = s ? fst_session_get_id(s) : FST_INVALID_SESSION_ID;
|
||||
|
||||
fst_ctrl_iface_notify(i, session_id, event_type, extra);
|
||||
}
|
||||
|
||||
|
||||
static const struct fst_ctrl ctrl_cli = {
|
||||
.on_iface_added = fst_ctrl_iface_on_iface_added,
|
||||
.on_iface_removed = fst_ctrl_iface_on_iface_removed,
|
||||
.on_event = fst_ctrl_iface_on_event,
|
||||
};
|
||||
|
||||
const struct fst_ctrl *fst_ctrl_cli = &ctrl_cli;
|
||||
|
||||
|
||||
int fst_ctrl_iface_mb_info(const u8 *addr, char *buf, size_t buflen)
|
||||
{
|
||||
struct fst_group *g;
|
||||
struct fst_iface *f;
|
||||
unsigned num = 0;
|
||||
int ret = 0;
|
||||
|
||||
foreach_fst_group(g) {
|
||||
foreach_fst_group_iface(g, f) {
|
||||
if (fst_iface_is_connected(f, addr, true)) {
|
||||
ret += print_band(num++, f, addr,
|
||||
buf + ret, buflen - ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* fst ctrl processor */
|
||||
int fst_ctrl_iface_receive(const char *cmd, char *reply, size_t reply_size)
|
||||
{
|
||||
static const struct fst_command {
|
||||
const char *name;
|
||||
unsigned has_param;
|
||||
int (*process)(const char *group_id, char *buf, size_t buflen);
|
||||
} commands[] = {
|
||||
{ FST_CMD_LIST_GROUPS, 0, list_groups},
|
||||
{ FST_CMD_LIST_IFACES, 1, list_ifaces},
|
||||
{ FST_CMD_IFACE_PEERS, 1, iface_peers},
|
||||
{ FST_CMD_GET_PEER_MBIES, 1, get_peer_mbies},
|
||||
{ FST_CMD_LIST_SESSIONS, 1, list_sessions},
|
||||
{ FST_CMD_SESSION_ADD, 1, session_add},
|
||||
{ FST_CMD_SESSION_REMOVE, 1, session_remove},
|
||||
{ FST_CMD_SESSION_GET, 1, session_get},
|
||||
{ FST_CMD_SESSION_SET, 1, session_set},
|
||||
{ FST_CMD_SESSION_INITIATE, 1, session_initiate},
|
||||
{ FST_CMD_SESSION_RESPOND, 1, session_respond},
|
||||
{ FST_CMD_SESSION_TRANSFER, 1, session_transfer},
|
||||
{ FST_CMD_SESSION_TEARDOWN, 1, session_teardown},
|
||||
#ifdef CONFIG_FST_TEST
|
||||
{ FST_CMD_TEST_REQUEST, 1, test_request },
|
||||
#endif /* CONFIG_FST_TEST */
|
||||
{ NULL, 0, NULL }
|
||||
};
|
||||
const struct fst_command *c;
|
||||
const char *p;
|
||||
const char *temp;
|
||||
bool non_spaces_found;
|
||||
|
||||
for (c = commands; c->name; c++) {
|
||||
if (os_strncasecmp(cmd, c->name, os_strlen(c->name)) != 0)
|
||||
continue;
|
||||
p = cmd + os_strlen(c->name);
|
||||
if (c->has_param) {
|
||||
if (!isspace(p[0]))
|
||||
return os_snprintf(reply, reply_size, "FAIL\n");
|
||||
p++;
|
||||
temp = p;
|
||||
non_spaces_found = false;
|
||||
while (*temp) {
|
||||
if (!isspace(*temp)) {
|
||||
non_spaces_found = true;
|
||||
break;
|
||||
}
|
||||
temp++;
|
||||
}
|
||||
if (!non_spaces_found)
|
||||
return os_snprintf(reply, reply_size, "FAIL\n");
|
||||
}
|
||||
return c->process(p, reply, reply_size);
|
||||
}
|
||||
|
||||
return os_snprintf(reply, reply_size, "UNKNOWN FST COMMAND\n");
|
||||
}
|
||||
|
||||
|
||||
int fst_read_next_int_param(const char *params, bool *valid, char **endp)
|
||||
{
|
||||
int ret = -1;
|
||||
const char *curp;
|
||||
|
||||
*valid = false;
|
||||
*endp = (char *) params;
|
||||
curp = params;
|
||||
if (*curp) {
|
||||
ret = (int) strtol(curp, endp, 0);
|
||||
if (!**endp || isspace(**endp))
|
||||
*valid = true;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int fst_read_next_text_param(const char *params, char *buf, size_t buflen,
|
||||
char **endp)
|
||||
{
|
||||
size_t max_chars_to_copy;
|
||||
char *cur_dest;
|
||||
|
||||
*endp = (char *) params;
|
||||
while (isspace(**endp))
|
||||
(*endp)++;
|
||||
if (!**endp || buflen <= 1)
|
||||
return -EINVAL;
|
||||
|
||||
max_chars_to_copy = buflen - 1;
|
||||
/* We need 1 byte for the terminating zero */
|
||||
cur_dest = buf;
|
||||
while (**endp && !isspace(**endp) && max_chars_to_copy > 0) {
|
||||
*cur_dest = **endp;
|
||||
(*endp)++;
|
||||
cur_dest++;
|
||||
max_chars_to_copy--;
|
||||
}
|
||||
*cur_dest = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int fst_read_peer_addr(const char *mac, u8 *peer_addr)
|
||||
{
|
||||
if (hwaddr_aton(mac, peer_addr)) {
|
||||
fst_printf(MSG_WARNING, "Bad peer_mac %s: invalid addr string",
|
||||
mac);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (is_zero_ether_addr(peer_addr) ||
|
||||
is_multicast_ether_addr(peer_addr)) {
|
||||
fst_printf(MSG_WARNING, "Bad peer_mac %s: not a unicast addr",
|
||||
mac);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int fst_parse_attach_command(const char *cmd, char *ifname, size_t ifname_size,
|
||||
struct fst_iface_cfg *cfg)
|
||||
{
|
||||
char *pos;
|
||||
char *endp;
|
||||
bool is_valid;
|
||||
int val;
|
||||
|
||||
if (fst_read_next_text_param(cmd, ifname, ifname_size, &endp) ||
|
||||
fst_read_next_text_param(endp, cfg->group_id, sizeof(cfg->group_id),
|
||||
&endp))
|
||||
return -EINVAL;
|
||||
|
||||
cfg->llt = FST_DEFAULT_LLT_CFG_VALUE;
|
||||
cfg->priority = 0;
|
||||
pos = os_strstr(endp, FST_ATTACH_CMD_PNAME_LLT);
|
||||
if (pos) {
|
||||
pos += os_strlen(FST_ATTACH_CMD_PNAME_LLT);
|
||||
if (*pos == '=') {
|
||||
val = fst_read_next_int_param(pos + 1, &is_valid,
|
||||
&endp);
|
||||
if (is_valid)
|
||||
cfg->llt = val;
|
||||
}
|
||||
}
|
||||
pos = os_strstr(endp, FST_ATTACH_CMD_PNAME_PRIORITY);
|
||||
if (pos) {
|
||||
pos += os_strlen(FST_ATTACH_CMD_PNAME_PRIORITY);
|
||||
if (*pos == '=') {
|
||||
val = fst_read_next_int_param(pos + 1, &is_valid,
|
||||
&endp);
|
||||
if (is_valid)
|
||||
cfg->priority = (u8) val;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int fst_parse_detach_command(const char *cmd, char *ifname, size_t ifname_size)
|
||||
{
|
||||
char *endp;
|
||||
|
||||
return fst_read_next_text_param(cmd, ifname, ifname_size, &endp);
|
||||
}
|
||||
|
||||
|
||||
int fst_iface_detach(const char *ifname)
|
||||
{
|
||||
struct fst_group *g;
|
||||
|
||||
foreach_fst_group(g) {
|
||||
struct fst_iface *f;
|
||||
|
||||
f = fst_group_get_iface_by_name(g, ifname);
|
||||
if (f) {
|
||||
fst_detach(f);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
45
src/fst/fst_ctrl_iface.h
Normal file
45
src/fst/fst_ctrl_iface.h
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* FST module - internal Control interface definitions
|
||||
* Copyright (c) 2014, Qualcomm Atheros, Inc.
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef FST_CTRL_IFACE_H
|
||||
#define FST_CTRL_IFACE_H
|
||||
|
||||
#include "fst/fst_ctrl_aux.h"
|
||||
|
||||
#ifdef CONFIG_FST
|
||||
|
||||
/* receiver */
|
||||
int fst_ctrl_iface_mb_info(const u8 *addr, char *buf, size_t buflen);
|
||||
|
||||
int fst_ctrl_iface_receive(const char *txtaddr, char *buf, size_t buflen);
|
||||
|
||||
extern const struct fst_ctrl *fst_ctrl_cli;
|
||||
|
||||
#else /* CONFIG_FST */
|
||||
|
||||
static inline int
|
||||
fst_ctrl_iface_mb_info(const u8 *addr, char *buf, size_t buflen)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_FST */
|
||||
|
||||
int fst_read_next_int_param(const char *params, bool *valid, char **endp);
|
||||
int fst_read_next_text_param(const char *params, char *buf, size_t buflen,
|
||||
char **endp);
|
||||
int fst_read_peer_addr(const char *mac, u8 *peer_addr);
|
||||
|
||||
struct fst_iface_cfg;
|
||||
|
||||
int fst_parse_attach_command(const char *cmd, char *ifname, size_t ifname_size,
|
||||
struct fst_iface_cfg *cfg);
|
||||
int fst_parse_detach_command(const char *cmd, char *ifname, size_t ifname_size);
|
||||
int fst_iface_detach(const char *ifname);
|
||||
|
||||
#endif /* CTRL_IFACE_FST_H */
|
||||
87
src/fst/fst_defs.h
Normal file
87
src/fst/fst_defs.h
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* FST module - FST related definitions
|
||||
* Copyright (c) 2014, Qualcomm Atheros, Inc.
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef IEEE_80211_FST_DEFS_H
|
||||
#define IEEE_80211_FST_DEFS_H
|
||||
|
||||
/* IEEE Std 802.11ad */
|
||||
|
||||
#define MB_STA_CHANNEL_ALL 0
|
||||
|
||||
enum session_type {
|
||||
SESSION_TYPE_BSS = 0, /* Infrastructure BSS */
|
||||
SESSION_TYPE_IBSS = 1,
|
||||
SESSION_TYPE_DLS = 2,
|
||||
SESSION_TYPE_TDLS = 3,
|
||||
SESSION_TYPE_PBSS = 4
|
||||
};
|
||||
|
||||
#define SESSION_CONTROL(session_type, switch_intent) \
|
||||
(((u8) ((session_type) & 0x7)) | ((switch_intent) ? 0x10 : 0x00))
|
||||
|
||||
#define GET_SESSION_CONTROL_TYPE(session_control) \
|
||||
((u8) ((session_control) & 0x7))
|
||||
|
||||
#define GET_SESSION_CONTROL_SWITCH_INTENT(session_control) \
|
||||
(((session_control) & 0x10) >> 4)
|
||||
|
||||
/* 8.4.2.147 Session Transition element */
|
||||
struct session_transition_ie {
|
||||
u8 element_id;
|
||||
u8 length;
|
||||
le32 fsts_id;
|
||||
u8 session_control;
|
||||
u8 new_band_id;
|
||||
u8 new_band_setup;
|
||||
u8 new_band_op;
|
||||
u8 old_band_id;
|
||||
u8 old_band_setup;
|
||||
u8 old_band_op;
|
||||
} STRUCT_PACKED;
|
||||
|
||||
struct fst_setup_req {
|
||||
u8 action;
|
||||
u8 dialog_token;
|
||||
le32 llt;
|
||||
struct session_transition_ie stie;
|
||||
/* Multi-band (optional) */
|
||||
/* Wakeup Schedule (optional) */
|
||||
/* Awake Window (optional) */
|
||||
/* Switching Stream (optional) */
|
||||
} STRUCT_PACKED;
|
||||
|
||||
struct fst_setup_res {
|
||||
u8 action;
|
||||
u8 dialog_token;
|
||||
u8 status_code;
|
||||
struct session_transition_ie stie;
|
||||
/* Multi-band (optional) */
|
||||
/* Wakeup Schedule (optional) */
|
||||
/* Awake Window (optional) */
|
||||
/* Switching Stream (optional) */
|
||||
/* Timeout Interval (optional) */
|
||||
} STRUCT_PACKED;
|
||||
|
||||
struct fst_ack_req {
|
||||
u8 action;
|
||||
u8 dialog_token;
|
||||
le32 fsts_id;
|
||||
} STRUCT_PACKED;
|
||||
|
||||
struct fst_ack_res {
|
||||
u8 action;
|
||||
u8 dialog_token;
|
||||
le32 fsts_id;
|
||||
} STRUCT_PACKED;
|
||||
|
||||
struct fst_tear_down {
|
||||
u8 action;
|
||||
le32 fsts_id;
|
||||
} STRUCT_PACKED;
|
||||
|
||||
#endif /* IEEE_80211_FST_DEFS_H */
|
||||
526
src/fst/fst_group.c
Normal file
526
src/fst/fst_group.c
Normal file
@@ -0,0 +1,526 @@
|
||||
/*
|
||||
* FST module - FST group object 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 "common/defs.h"
|
||||
#include "common/ieee802_11_defs.h"
|
||||
#include "common/ieee802_11_common.h"
|
||||
#include "drivers/driver.h"
|
||||
#include "fst/fst_internal.h"
|
||||
#include "fst/fst_defs.h"
|
||||
|
||||
|
||||
struct dl_list fst_global_groups_list;
|
||||
|
||||
|
||||
static void fst_dump_mb_ies(const char *group_id, const char *ifname,
|
||||
struct wpabuf *mbies)
|
||||
{
|
||||
const u8 *p = wpabuf_head(mbies);
|
||||
size_t s = wpabuf_len(mbies);
|
||||
|
||||
while (s >= 2) {
|
||||
const struct multi_band_ie *mbie =
|
||||
(const struct multi_band_ie *) p;
|
||||
size_t len;
|
||||
|
||||
WPA_ASSERT(mbie->eid == WLAN_EID_MULTI_BAND);
|
||||
WPA_ASSERT(2U + mbie->len >= sizeof(*mbie));
|
||||
len = 2 + mbie->len;
|
||||
if (len > s)
|
||||
break;
|
||||
|
||||
fst_printf(MSG_WARNING,
|
||||
"%s: %s: mb_ctrl=%u band_id=%u op_class=%u chan=%u bssid="
|
||||
MACSTR
|
||||
" beacon_int=%u tsf_offs=[%u %u %u %u %u %u %u %u] mb_cc=0x%02x tmout=%u",
|
||||
group_id, ifname,
|
||||
mbie->mb_ctrl, mbie->band_id, mbie->op_class,
|
||||
mbie->chan, MAC2STR(mbie->bssid), mbie->beacon_int,
|
||||
mbie->tsf_offs[0], mbie->tsf_offs[1],
|
||||
mbie->tsf_offs[2], mbie->tsf_offs[3],
|
||||
mbie->tsf_offs[4], mbie->tsf_offs[5],
|
||||
mbie->tsf_offs[6], mbie->tsf_offs[7],
|
||||
mbie->mb_connection_capability,
|
||||
mbie->fst_session_tmout);
|
||||
|
||||
p += len;
|
||||
s -= len;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void fst_fill_mb_ie(struct wpabuf *buf, const u8 *bssid,
|
||||
const u8 *own_addr, enum mb_band_id band, u8 channel)
|
||||
{
|
||||
struct multi_band_ie *mbie;
|
||||
size_t len = sizeof(*mbie);
|
||||
|
||||
if (own_addr)
|
||||
len += ETH_ALEN;
|
||||
|
||||
mbie = wpabuf_put(buf, len);
|
||||
|
||||
os_memset(mbie, 0, len);
|
||||
|
||||
mbie->eid = WLAN_EID_MULTI_BAND;
|
||||
mbie->len = len - 2;
|
||||
#ifdef HOSTAPD
|
||||
mbie->mb_ctrl = MB_STA_ROLE_AP;
|
||||
mbie->mb_connection_capability = MB_CONNECTION_CAPABILITY_AP;
|
||||
#else /* HOSTAPD */
|
||||
mbie->mb_ctrl = MB_STA_ROLE_NON_PCP_NON_AP;
|
||||
mbie->mb_connection_capability = 0;
|
||||
#endif /* HOSTAPD */
|
||||
if (bssid)
|
||||
os_memcpy(mbie->bssid, bssid, ETH_ALEN);
|
||||
mbie->band_id = band;
|
||||
mbie->op_class = 0; /* means all */
|
||||
mbie->chan = channel;
|
||||
mbie->fst_session_tmout = FST_DEFAULT_SESSION_TIMEOUT_TU;
|
||||
|
||||
if (own_addr) {
|
||||
mbie->mb_ctrl |= MB_CTRL_STA_MAC_PRESENT;
|
||||
os_memcpy(&mbie[1], own_addr, ETH_ALEN);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static unsigned fst_fill_iface_mb_ies(struct fst_iface *f, struct wpabuf *buf)
|
||||
{
|
||||
const u8 *bssid;
|
||||
|
||||
bssid = fst_iface_get_bssid(f);
|
||||
if (bssid) {
|
||||
enum hostapd_hw_mode hw_mode;
|
||||
u8 channel;
|
||||
|
||||
if (buf) {
|
||||
fst_iface_get_channel_info(f, &hw_mode, &channel);
|
||||
fst_fill_mb_ie(buf, bssid, fst_iface_get_addr(f),
|
||||
fst_hw_mode_to_band(hw_mode), channel);
|
||||
}
|
||||
return 1;
|
||||
} else {
|
||||
unsigned bands[MB_BAND_ID_WIFI_60GHZ + 1] = {};
|
||||
struct hostapd_hw_modes *modes;
|
||||
enum mb_band_id b;
|
||||
int num_modes = fst_iface_get_hw_modes(f, &modes);
|
||||
int ret = 0;
|
||||
|
||||
while (num_modes--) {
|
||||
b = fst_hw_mode_to_band(modes->mode);
|
||||
modes++;
|
||||
if (b >= ARRAY_SIZE(bands) || bands[b]++)
|
||||
continue;
|
||||
ret++;
|
||||
if (buf)
|
||||
fst_fill_mb_ie(buf, NULL, fst_iface_get_addr(f),
|
||||
b, MB_STA_CHANNEL_ALL);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static struct wpabuf * fst_group_create_mb_ie(struct fst_group *g,
|
||||
struct fst_iface *i)
|
||||
{
|
||||
struct wpabuf *buf;
|
||||
struct fst_iface *f;
|
||||
unsigned int nof_mbies = 0;
|
||||
unsigned int nof_ifaces_added = 0;
|
||||
|
||||
foreach_fst_group_iface(g, f) {
|
||||
if (f == i)
|
||||
continue;
|
||||
nof_mbies += fst_fill_iface_mb_ies(f, NULL);
|
||||
}
|
||||
|
||||
buf = wpabuf_alloc(nof_mbies *
|
||||
(sizeof(struct multi_band_ie) + ETH_ALEN));
|
||||
if (!buf) {
|
||||
fst_printf_iface(i, MSG_ERROR,
|
||||
"cannot allocate mem for %u MB IEs",
|
||||
nof_mbies);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* The list is sorted in descending order by priorities, so MB IEs will
|
||||
* be arranged in the same order, as required by spec (see corresponding
|
||||
* comment in.fst_attach().
|
||||
*/
|
||||
foreach_fst_group_iface(g, f) {
|
||||
if (f == i)
|
||||
continue;
|
||||
|
||||
fst_fill_iface_mb_ies(f, buf);
|
||||
++nof_ifaces_added;
|
||||
|
||||
fst_printf_iface(i, MSG_DEBUG, "added to MB IE");
|
||||
}
|
||||
|
||||
if (!nof_ifaces_added) {
|
||||
wpabuf_free(buf);
|
||||
buf = NULL;
|
||||
fst_printf_iface(i, MSG_INFO,
|
||||
"cannot add MB IE: no backup ifaces");
|
||||
} else {
|
||||
fst_dump_mb_ies(fst_group_get_id(g), fst_iface_get_name(i),
|
||||
buf);
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
||||
static const u8 * fst_mbie_get_peer_addr(const struct multi_band_ie *mbie)
|
||||
{
|
||||
const u8 *peer_addr = NULL;
|
||||
|
||||
switch (MB_CTRL_ROLE(mbie->mb_ctrl)) {
|
||||
case MB_STA_ROLE_AP:
|
||||
peer_addr = mbie->bssid;
|
||||
break;
|
||||
case MB_STA_ROLE_NON_PCP_NON_AP:
|
||||
if (mbie->mb_ctrl & MB_CTRL_STA_MAC_PRESENT &&
|
||||
(size_t) 2 + mbie->len >= sizeof(*mbie) + ETH_ALEN)
|
||||
peer_addr = (const u8 *) &mbie[1];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return peer_addr;
|
||||
}
|
||||
|
||||
|
||||
static const u8 * fst_mbie_get_peer_addr_for_band(const struct wpabuf *mbies,
|
||||
u8 band_id)
|
||||
{
|
||||
const u8 *p = wpabuf_head(mbies);
|
||||
size_t s = wpabuf_len(mbies);
|
||||
|
||||
while (s >= 2) {
|
||||
const struct multi_band_ie *mbie =
|
||||
(const struct multi_band_ie *) p;
|
||||
|
||||
if (mbie->eid != WLAN_EID_MULTI_BAND) {
|
||||
fst_printf(MSG_INFO, "unexpected eid %d", mbie->eid);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (mbie->len < sizeof(*mbie) - 2 || mbie->len > s - 2) {
|
||||
fst_printf(MSG_INFO, "invalid mbie len %d",
|
||||
mbie->len);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (mbie->band_id == band_id)
|
||||
return fst_mbie_get_peer_addr(mbie);
|
||||
|
||||
p += 2 + mbie->len;
|
||||
s -= 2 + mbie->len;
|
||||
}
|
||||
|
||||
fst_printf(MSG_INFO, "mbie doesn't contain band %d", band_id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
struct fst_iface * fst_group_get_iface_by_name(struct fst_group *g,
|
||||
const char *ifname)
|
||||
{
|
||||
struct fst_iface *f;
|
||||
|
||||
foreach_fst_group_iface(g, f) {
|
||||
const char *in = fst_iface_get_name(f);
|
||||
|
||||
if (os_strncmp(in, ifname, os_strlen(in)) == 0)
|
||||
return f;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
u8 fst_group_assign_dialog_token(struct fst_group *g)
|
||||
{
|
||||
g->dialog_token++;
|
||||
if (g->dialog_token == 0)
|
||||
g->dialog_token++;
|
||||
return g->dialog_token;
|
||||
}
|
||||
|
||||
|
||||
u32 fst_group_assign_fsts_id(struct fst_group *g)
|
||||
{
|
||||
g->fsts_id++;
|
||||
return g->fsts_id;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* fst_group_get_peer_other_connection_1 - Find peer's "other" connection
|
||||
* (iface, MAC tuple) by using peer's MB IE on iface.
|
||||
*
|
||||
* @iface: iface on which FST Setup Request was received
|
||||
* @peer_addr: Peer address on iface
|
||||
* @band_id: "other" connection band id
|
||||
* @other_peer_addr (out): Peer's MAC address on the "other" connection (on the
|
||||
* "other" iface)
|
||||
*
|
||||
* This function parses peer's MB IE on iface. It looks for peer's MAC address
|
||||
* on band_id (tmp_peer_addr). Next all interfaces are iterated to find an
|
||||
* interface which correlates with band_id. If such interface is found, peer
|
||||
* database is iterated to see if tmp_peer_addr is connected over it.
|
||||
*/
|
||||
static struct fst_iface *
|
||||
fst_group_get_peer_other_connection_1(struct fst_iface *iface,
|
||||
const u8 *peer_addr, u8 band_id,
|
||||
u8 *other_peer_addr)
|
||||
{
|
||||
const struct wpabuf *mbies;
|
||||
struct fst_iface *other_iface;
|
||||
const u8 *tmp_peer_addr;
|
||||
|
||||
/* Get peer's MB IEs on iface */
|
||||
mbies = fst_iface_get_peer_mb_ie(iface, peer_addr);
|
||||
if (!mbies)
|
||||
return NULL;
|
||||
|
||||
/* Get peer's MAC address on the "other" interface */
|
||||
tmp_peer_addr = fst_mbie_get_peer_addr_for_band(mbies, band_id);
|
||||
if (!tmp_peer_addr) {
|
||||
fst_printf(MSG_INFO,
|
||||
"couldn't extract other peer addr from mbies");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fst_printf(MSG_DEBUG, "found other peer addr from mbies: " MACSTR,
|
||||
MAC2STR(tmp_peer_addr));
|
||||
|
||||
foreach_fst_group_iface(fst_iface_get_group(iface), other_iface) {
|
||||
if (other_iface == iface ||
|
||||
band_id != fst_iface_get_band_id(other_iface))
|
||||
continue;
|
||||
if (fst_iface_is_connected(other_iface, tmp_peer_addr, false)) {
|
||||
os_memcpy(other_peer_addr, tmp_peer_addr, ETH_ALEN);
|
||||
return other_iface;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* fst_group_get_peer_other_connection_2 - Find peer's "other" connection
|
||||
* (iface, MAC tuple) by using MB IEs of other peers.
|
||||
*
|
||||
* @iface: iface on which FST Setup Request was received
|
||||
* @peer_addr: Peer address on iface
|
||||
* @band_id: "other" connection band id
|
||||
* @other_peer_addr (out): Peer's MAC address on the "other" connection (on the
|
||||
* "other" iface)
|
||||
*
|
||||
* This function iterates all connection (other_iface, cur_peer_addr tuples).
|
||||
* For each connection, MB IE (of cur_peer_addr on other_iface) is parsed and
|
||||
* MAC address on iface's band_id is extracted (this_peer_addr).
|
||||
* this_peer_addr is then compared to peer_addr. A match indicates we have
|
||||
* found the "other" connection.
|
||||
*/
|
||||
static struct fst_iface *
|
||||
fst_group_get_peer_other_connection_2(struct fst_iface *iface,
|
||||
const u8 *peer_addr, u8 band_id,
|
||||
u8 *other_peer_addr)
|
||||
{
|
||||
u8 this_band_id = fst_iface_get_band_id(iface);
|
||||
const u8 *cur_peer_addr, *this_peer_addr;
|
||||
struct fst_get_peer_ctx *ctx;
|
||||
struct fst_iface *other_iface;
|
||||
const struct wpabuf *cur_mbie;
|
||||
|
||||
foreach_fst_group_iface(fst_iface_get_group(iface), other_iface) {
|
||||
if (other_iface == iface ||
|
||||
band_id != fst_iface_get_band_id(other_iface))
|
||||
continue;
|
||||
cur_peer_addr = fst_iface_get_peer_first(other_iface, &ctx,
|
||||
true);
|
||||
for (; cur_peer_addr;
|
||||
cur_peer_addr = fst_iface_get_peer_next(other_iface, &ctx,
|
||||
true)) {
|
||||
cur_mbie = fst_iface_get_peer_mb_ie(other_iface,
|
||||
cur_peer_addr);
|
||||
if (!cur_mbie)
|
||||
continue;
|
||||
this_peer_addr = fst_mbie_get_peer_addr_for_band(
|
||||
cur_mbie, this_band_id);
|
||||
if (!this_peer_addr)
|
||||
continue;
|
||||
if (ether_addr_equal(this_peer_addr, peer_addr)) {
|
||||
os_memcpy(other_peer_addr, cur_peer_addr,
|
||||
ETH_ALEN);
|
||||
return other_iface;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* fst_group_get_peer_other_connection - Find peer's "other" connection (iface,
|
||||
* MAC tuple).
|
||||
*
|
||||
* @iface: iface on which FST Setup Request was received
|
||||
* @peer_addr: Peer address on iface
|
||||
* @band_id: "other" connection band id
|
||||
* @other_peer_addr (out): Peer's MAC address on the "other" connection (on the
|
||||
* "other" iface)
|
||||
*
|
||||
* This function is called upon receiving FST Setup Request from some peer who
|
||||
* has peer_addr on iface. It searches for another connection of the same peer
|
||||
* on different interface which correlates with band_id. MB IEs received from
|
||||
* peer (on the two different interfaces) are used to identify same peer.
|
||||
*/
|
||||
struct fst_iface *
|
||||
fst_group_get_peer_other_connection(struct fst_iface *iface,
|
||||
const u8 *peer_addr, u8 band_id,
|
||||
u8 *other_peer_addr)
|
||||
{
|
||||
struct fst_iface *other_iface;
|
||||
|
||||
fst_printf(MSG_DEBUG, "%s: %s:" MACSTR ", %d", __func__,
|
||||
fst_iface_get_name(iface), MAC2STR(peer_addr), band_id);
|
||||
|
||||
/*
|
||||
* Two search methods are used:
|
||||
* 1. Use peer's MB IE on iface to extract peer's MAC address on
|
||||
* "other" connection. Then check if such "other" connection exists.
|
||||
* 2. Iterate peer database, examine each MB IE to see if it points to
|
||||
* (iface, peer_addr) tuple
|
||||
*/
|
||||
|
||||
other_iface = fst_group_get_peer_other_connection_1(iface, peer_addr,
|
||||
band_id,
|
||||
other_peer_addr);
|
||||
if (other_iface) {
|
||||
fst_printf(MSG_DEBUG, "found by method #1. %s:" MACSTR,
|
||||
fst_iface_get_name(other_iface),
|
||||
MAC2STR(other_peer_addr));
|
||||
return other_iface;
|
||||
}
|
||||
|
||||
other_iface = fst_group_get_peer_other_connection_2(iface, peer_addr,
|
||||
band_id,
|
||||
other_peer_addr);
|
||||
if (other_iface) {
|
||||
fst_printf(MSG_DEBUG, "found by method #2. %s:" MACSTR,
|
||||
fst_iface_get_name(other_iface),
|
||||
MAC2STR(other_peer_addr));
|
||||
return other_iface;
|
||||
}
|
||||
|
||||
fst_printf(MSG_INFO, "%s: other connection not found", __func__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
struct fst_group * fst_group_create(const char *group_id)
|
||||
{
|
||||
struct fst_group *g;
|
||||
|
||||
g = os_zalloc(sizeof(*g));
|
||||
if (g == NULL) {
|
||||
fst_printf(MSG_ERROR, "%s: Cannot alloc group", group_id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dl_list_init(&g->ifaces);
|
||||
os_strlcpy(g->group_id, group_id, sizeof(g->group_id));
|
||||
|
||||
dl_list_add_tail(&fst_global_groups_list, &g->global_groups_lentry);
|
||||
fst_printf_group(g, MSG_DEBUG, "instance created");
|
||||
|
||||
foreach_fst_ctrl_call(on_group_created, g);
|
||||
|
||||
return g;
|
||||
}
|
||||
|
||||
|
||||
void fst_group_attach_iface(struct fst_group *g, struct fst_iface *i)
|
||||
{
|
||||
struct dl_list *list = &g->ifaces;
|
||||
struct fst_iface *f;
|
||||
|
||||
/*
|
||||
* Add new interface to the list.
|
||||
* The list is sorted in descending order by priority to allow
|
||||
* multiple MB IEs creation according to the spec (see 10.32 Multi-band
|
||||
* operation, 10.32.1 General), as they should be ordered according to
|
||||
* priorities.
|
||||
*/
|
||||
foreach_fst_group_iface(g, f) {
|
||||
if (fst_iface_get_priority(f) < fst_iface_get_priority(i))
|
||||
break;
|
||||
list = &f->group_lentry;
|
||||
}
|
||||
dl_list_add(list, &i->group_lentry);
|
||||
}
|
||||
|
||||
|
||||
void fst_group_detach_iface(struct fst_group *g, struct fst_iface *i)
|
||||
{
|
||||
dl_list_del(&i->group_lentry);
|
||||
}
|
||||
|
||||
|
||||
void fst_group_delete(struct fst_group *group)
|
||||
{
|
||||
struct fst_session *s;
|
||||
|
||||
dl_list_del(&group->global_groups_lentry);
|
||||
WPA_ASSERT(dl_list_empty(&group->ifaces));
|
||||
foreach_fst_ctrl_call(on_group_deleted, group);
|
||||
fst_printf_group(group, MSG_DEBUG, "instance deleted");
|
||||
while ((s = fst_session_global_get_first_by_group(group)) != NULL)
|
||||
fst_session_delete(s);
|
||||
os_free(group);
|
||||
}
|
||||
|
||||
|
||||
bool fst_group_delete_if_empty(struct fst_group *group)
|
||||
{
|
||||
bool is_empty = !fst_group_has_ifaces(group) &&
|
||||
!fst_session_global_get_first_by_group(group);
|
||||
|
||||
if (is_empty)
|
||||
fst_group_delete(group);
|
||||
|
||||
return is_empty;
|
||||
}
|
||||
|
||||
|
||||
void fst_group_update_ie(struct fst_group *g)
|
||||
{
|
||||
struct fst_iface *i;
|
||||
|
||||
foreach_fst_group_iface(g, i) {
|
||||
struct wpabuf *mbie = fst_group_create_mb_ie(g, i);
|
||||
|
||||
if (!mbie)
|
||||
fst_printf_iface(i, MSG_WARNING, "cannot create MB IE");
|
||||
|
||||
fst_iface_attach_mbie(i, mbie);
|
||||
fst_iface_set_ies(i, mbie);
|
||||
fst_printf_iface(i, MSG_DEBUG, "multi-band IE set to %p", mbie);
|
||||
}
|
||||
}
|
||||
69
src/fst/fst_group.h
Normal file
69
src/fst/fst_group.h
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* FST module - FST group object definitions
|
||||
* Copyright (c) 2014, Qualcomm Atheros, Inc.
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef FST_GROUP_H
|
||||
#define FST_GROUP_H
|
||||
|
||||
struct fst_group {
|
||||
char group_id[IFNAMSIZ + 1];
|
||||
struct dl_list ifaces;
|
||||
u8 dialog_token;
|
||||
u32 fsts_id;
|
||||
struct dl_list global_groups_lentry;
|
||||
};
|
||||
|
||||
struct session_transition_ie;
|
||||
|
||||
#define foreach_fst_group_iface(g, i) \
|
||||
dl_list_for_each((i), &(g)->ifaces, struct fst_iface, group_lentry)
|
||||
|
||||
struct fst_group * fst_group_create(const char *group_id);
|
||||
void fst_group_attach_iface(struct fst_group *g, struct fst_iface *i);
|
||||
void fst_group_detach_iface(struct fst_group *g, struct fst_iface *i);
|
||||
void fst_group_delete(struct fst_group *g);
|
||||
|
||||
void fst_group_update_ie(struct fst_group *g);
|
||||
|
||||
static inline bool fst_group_has_ifaces(struct fst_group *g)
|
||||
{
|
||||
return !dl_list_empty(&g->ifaces);
|
||||
}
|
||||
|
||||
static inline struct fst_iface * fst_group_first_iface(struct fst_group *g)
|
||||
{
|
||||
return dl_list_first(&g->ifaces, struct fst_iface, group_lentry);
|
||||
}
|
||||
|
||||
static inline const char * fst_group_get_id(struct fst_group *g)
|
||||
{
|
||||
return g->group_id;
|
||||
}
|
||||
|
||||
bool fst_group_delete_if_empty(struct fst_group *group);
|
||||
struct fst_iface * fst_group_get_iface_by_name(struct fst_group *g,
|
||||
const char *ifname);
|
||||
struct fst_iface *
|
||||
fst_group_get_peer_other_connection(struct fst_iface *iface,
|
||||
const u8 *peer_addr, u8 band_id,
|
||||
u8 *other_peer_addr);
|
||||
u8 fst_group_assign_dialog_token(struct fst_group *g);
|
||||
u32 fst_group_assign_fsts_id(struct fst_group *g);
|
||||
|
||||
extern struct dl_list fst_global_groups_list;
|
||||
|
||||
#define foreach_fst_group(g) \
|
||||
dl_list_for_each((g), &fst_global_groups_list, \
|
||||
struct fst_group, global_groups_lentry)
|
||||
|
||||
static inline struct fst_group * fst_first_group(void)
|
||||
{
|
||||
return dl_list_first(&fst_global_groups_list, struct fst_group,
|
||||
global_groups_lentry);
|
||||
}
|
||||
|
||||
#endif /* FST_GROUP_H */
|
||||
80
src/fst/fst_iface.c
Normal file
80
src/fst/fst_iface.c
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* FST module - FST interface object 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 "fst/fst_internal.h"
|
||||
#include "fst/fst_defs.h"
|
||||
|
||||
|
||||
struct fst_iface * fst_iface_create(struct fst_group *g, const char *ifname,
|
||||
const u8 *own_addr,
|
||||
const struct fst_wpa_obj *iface_obj,
|
||||
const struct fst_iface_cfg *cfg)
|
||||
{
|
||||
struct fst_iface *i;
|
||||
|
||||
i = os_zalloc(sizeof(*i));
|
||||
if (!i) {
|
||||
fst_printf_group(g, MSG_ERROR, "cannot allocate iface for %s",
|
||||
ifname);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
i->cfg = *cfg;
|
||||
i->iface_obj = *iface_obj;
|
||||
i->group = g;
|
||||
os_strlcpy(i->ifname, ifname, sizeof(i->ifname));
|
||||
os_memcpy(i->own_addr, own_addr, sizeof(i->own_addr));
|
||||
|
||||
if (!i->cfg.llt) {
|
||||
fst_printf_iface(i, MSG_WARNING, "Zero llt adjusted");
|
||||
i->cfg.llt = FST_DEFAULT_LLT_CFG_VALUE;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
void fst_iface_delete(struct fst_iface *i)
|
||||
{
|
||||
fst_iface_set_ies(i, NULL);
|
||||
wpabuf_free(i->mb_ie);
|
||||
os_free(i);
|
||||
}
|
||||
|
||||
|
||||
bool fst_iface_is_connected(struct fst_iface *iface, const u8 *addr,
|
||||
bool mb_only)
|
||||
{
|
||||
struct fst_get_peer_ctx *ctx;
|
||||
const u8 *a = fst_iface_get_peer_first(iface, &ctx, mb_only);
|
||||
|
||||
for (; a != NULL; a = fst_iface_get_peer_next(iface, &ctx, mb_only))
|
||||
if (ether_addr_equal(addr, a))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void fst_iface_attach_mbie(struct fst_iface *i, struct wpabuf *mbie)
|
||||
{
|
||||
wpabuf_free(i->mb_ie);
|
||||
i->mb_ie = mbie;
|
||||
}
|
||||
|
||||
|
||||
enum mb_band_id fst_iface_get_band_id(struct fst_iface *i)
|
||||
{
|
||||
enum hostapd_hw_mode hw_mode;
|
||||
u8 channel;
|
||||
|
||||
fst_iface_get_channel_info(i, &hw_mode, &channel);
|
||||
return fst_hw_mode_to_band(hw_mode);
|
||||
}
|
||||
136
src/fst/fst_iface.h
Normal file
136
src/fst/fst_iface.h
Normal file
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* FST module - FST interface object definitions
|
||||
* Copyright (c) 2014, Qualcomm Atheros, Inc.
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef FST_IFACE_H
|
||||
#define FST_IFACE_H
|
||||
|
||||
#include "utils/includes.h"
|
||||
#include "utils/common.h"
|
||||
#include "list.h"
|
||||
#include "fst.h"
|
||||
|
||||
struct fst_iface {
|
||||
struct fst_group *group;
|
||||
struct fst_wpa_obj iface_obj;
|
||||
u8 own_addr[ETH_ALEN];
|
||||
struct wpabuf *mb_ie;
|
||||
char ifname[IFNAMSIZ + 1];
|
||||
struct fst_iface_cfg cfg;
|
||||
struct dl_list group_lentry;
|
||||
};
|
||||
|
||||
struct fst_iface * fst_iface_create(struct fst_group *g, const char *ifname,
|
||||
const u8 *own_addr,
|
||||
const struct fst_wpa_obj *iface_obj,
|
||||
const struct fst_iface_cfg *cfg);
|
||||
void fst_iface_delete(struct fst_iface *i);
|
||||
|
||||
static inline struct fst_group * fst_iface_get_group(struct fst_iface *i)
|
||||
{
|
||||
return i->group;
|
||||
}
|
||||
|
||||
static inline const char * fst_iface_get_name(struct fst_iface *i)
|
||||
{
|
||||
return i->ifname;
|
||||
}
|
||||
|
||||
static inline const u8 * fst_iface_get_addr(struct fst_iface *i)
|
||||
{
|
||||
return i->own_addr;
|
||||
}
|
||||
|
||||
static inline const char * fst_iface_get_group_id(struct fst_iface *i)
|
||||
{
|
||||
return i->cfg.group_id;
|
||||
}
|
||||
|
||||
static inline u8 fst_iface_get_priority(struct fst_iface *i)
|
||||
{
|
||||
return i->cfg.priority;
|
||||
}
|
||||
|
||||
static inline u32 fst_iface_get_llt(struct fst_iface *i)
|
||||
{
|
||||
return i->cfg.llt;
|
||||
}
|
||||
|
||||
static inline const struct wpabuf * fst_iface_get_mbie(struct fst_iface *i)
|
||||
{
|
||||
return i->mb_ie;
|
||||
}
|
||||
|
||||
static inline const u8 * fst_iface_get_bssid(struct fst_iface *i)
|
||||
{
|
||||
return i->iface_obj.get_bssid(i->iface_obj.ctx);
|
||||
}
|
||||
|
||||
static inline void fst_iface_get_channel_info(struct fst_iface *i,
|
||||
enum hostapd_hw_mode *hw_mode,
|
||||
u8 *channel)
|
||||
{
|
||||
i->iface_obj.get_channel_info(i->iface_obj.ctx, hw_mode, channel);
|
||||
}
|
||||
|
||||
static inline int fst_iface_get_hw_modes(struct fst_iface *i,
|
||||
struct hostapd_hw_modes **modes)
|
||||
{
|
||||
return i->iface_obj.get_hw_modes(i->iface_obj.ctx, modes);
|
||||
}
|
||||
|
||||
static inline void fst_iface_set_ies(struct fst_iface *i,
|
||||
const struct wpabuf *fst_ies)
|
||||
{
|
||||
i->iface_obj.set_ies(i->iface_obj.ctx, fst_ies);
|
||||
}
|
||||
|
||||
static inline int fst_iface_send_action(struct fst_iface *i,
|
||||
const u8 *addr, struct wpabuf *data)
|
||||
{
|
||||
return i->iface_obj.send_action(i->iface_obj.ctx, addr, data);
|
||||
}
|
||||
|
||||
static inline const struct wpabuf *
|
||||
fst_iface_get_peer_mb_ie(struct fst_iface *i, const u8 *addr)
|
||||
{
|
||||
return i->iface_obj.get_mb_ie(i->iface_obj.ctx, addr);
|
||||
}
|
||||
|
||||
static inline void fst_iface_update_mb_ie(struct fst_iface *i,
|
||||
const u8 *addr,
|
||||
const u8 *buf, size_t size)
|
||||
{
|
||||
i->iface_obj.update_mb_ie(i->iface_obj.ctx, addr, buf, size);
|
||||
}
|
||||
|
||||
static inline const u8 * fst_iface_get_peer_first(struct fst_iface *i,
|
||||
struct fst_get_peer_ctx **ctx,
|
||||
bool mb_only)
|
||||
{
|
||||
return i->iface_obj.get_peer_first(i->iface_obj.ctx, ctx, mb_only);
|
||||
}
|
||||
|
||||
static inline const u8 * fst_iface_get_peer_next(struct fst_iface *i,
|
||||
struct fst_get_peer_ctx **ctx,
|
||||
bool mb_only)
|
||||
{
|
||||
return i->iface_obj.get_peer_next(i->iface_obj.ctx, ctx, mb_only);
|
||||
}
|
||||
|
||||
bool fst_iface_is_connected(struct fst_iface *iface, const u8 *addr,
|
||||
bool mb_only);
|
||||
void fst_iface_attach_mbie(struct fst_iface *i, struct wpabuf *mbie);
|
||||
enum mb_band_id fst_iface_get_band_id(struct fst_iface *i);
|
||||
|
||||
static inline void * fst_iface_get_wpa_obj_ctx(struct fst_iface *i)
|
||||
{
|
||||
return i->iface_obj.ctx;
|
||||
}
|
||||
|
||||
#endif /* FST_IFACE_H */
|
||||
49
src/fst/fst_internal.h
Normal file
49
src/fst/fst_internal.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* FST module - auxiliary definitions
|
||||
* Copyright (c) 2014, Qualcomm Atheros, Inc.
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef FST_INTERNAL_H
|
||||
#define FST_INTERNAL_H
|
||||
|
||||
#include "utils/includes.h"
|
||||
#include "utils/common.h"
|
||||
#include "common/defs.h"
|
||||
#include "common/ieee802_11_defs.h"
|
||||
#include "fst/fst_iface.h"
|
||||
#include "fst/fst_group.h"
|
||||
#include "fst/fst_session.h"
|
||||
|
||||
#define fst_printf(level, format, ...) \
|
||||
wpa_printf((level), "FST: " format, ##__VA_ARGS__)
|
||||
|
||||
#define fst_printf_group(group, level, format, ...) \
|
||||
wpa_printf((level), "FST: %s: " format, \
|
||||
fst_group_get_id(group), ##__VA_ARGS__)
|
||||
|
||||
#define fst_printf_iface(iface, level, format, ...) \
|
||||
fst_printf_group(fst_iface_get_group(iface), (level), "%s: " format, \
|
||||
fst_iface_get_name(iface), ##__VA_ARGS__)
|
||||
|
||||
enum mb_band_id fst_hw_mode_to_band(enum hostapd_hw_mode mode);
|
||||
|
||||
struct fst_ctrl_handle {
|
||||
struct fst_ctrl ctrl;
|
||||
struct dl_list global_ctrls_lentry;
|
||||
};
|
||||
|
||||
extern struct dl_list fst_global_ctrls_list;
|
||||
|
||||
#define foreach_fst_ctrl_call(clb, ...) \
|
||||
do { \
|
||||
struct fst_ctrl_handle *__fst_ctrl_h; \
|
||||
dl_list_for_each(__fst_ctrl_h, &fst_global_ctrls_list, \
|
||||
struct fst_ctrl_handle, global_ctrls_lentry) \
|
||||
if (__fst_ctrl_h->ctrl.clb) \
|
||||
__fst_ctrl_h->ctrl.clb(__VA_ARGS__);\
|
||||
} while (0)
|
||||
|
||||
#endif /* FST_INTERNAL_H */
|
||||
1607
src/fst/fst_session.c
Normal file
1607
src/fst/fst_session.c
Normal file
File diff suppressed because it is too large
Load Diff
80
src/fst/fst_session.h
Normal file
80
src/fst/fst_session.h
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* FST module - FST Session related definitions
|
||||
* Copyright (c) 2014, Qualcomm Atheros, Inc.
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef FST_SESSION_H
|
||||
#define FST_SESSION_H
|
||||
|
||||
#define FST_DEFAULT_SESSION_TIMEOUT_TU 255 /* u8 */
|
||||
|
||||
struct fst_iface;
|
||||
struct fst_group;
|
||||
struct fst_session;
|
||||
enum fst_session_state;
|
||||
|
||||
int fst_session_global_init(void);
|
||||
void fst_session_global_deinit(void);
|
||||
void fst_session_global_on_iface_detached(struct fst_iface *iface);
|
||||
struct fst_session *
|
||||
fst_session_global_get_first_by_group(struct fst_group *g);
|
||||
|
||||
struct fst_session * fst_session_create(struct fst_group *g);
|
||||
void fst_session_set_iface(struct fst_session *s, struct fst_iface *iface,
|
||||
bool is_old);
|
||||
void fst_session_set_llt(struct fst_session *s, u32 llt);
|
||||
void fst_session_set_peer_addr(struct fst_session *s, const u8 *addr,
|
||||
bool is_old);
|
||||
int fst_session_initiate_setup(struct fst_session *s);
|
||||
int fst_session_respond(struct fst_session *s, u8 status_code);
|
||||
int fst_session_initiate_switch(struct fst_session *s);
|
||||
void fst_session_handle_action(struct fst_session *s, struct fst_iface *iface,
|
||||
const struct ieee80211_mgmt *mgmt,
|
||||
size_t frame_len);
|
||||
int fst_session_tear_down_setup(struct fst_session *s);
|
||||
void fst_session_reset(struct fst_session *s);
|
||||
void fst_session_delete(struct fst_session *s);
|
||||
|
||||
struct fst_group * fst_session_get_group(struct fst_session *s);
|
||||
struct fst_iface * fst_session_get_iface(struct fst_session *s, bool is_old);
|
||||
const u8 * fst_session_get_peer_addr(struct fst_session *s, bool is_old);
|
||||
u32 fst_session_get_id(struct fst_session *s);
|
||||
u32 fst_session_get_llt(struct fst_session *s);
|
||||
enum fst_session_state fst_session_get_state(struct fst_session *s);
|
||||
|
||||
struct fst_session *fst_session_get_by_id(u32 id);
|
||||
|
||||
typedef void (*fst_session_enum_clb)(struct fst_group *g, struct fst_session *s,
|
||||
void *ctx);
|
||||
|
||||
void fst_session_enum(struct fst_group *g, fst_session_enum_clb clb, void *ctx);
|
||||
|
||||
void fst_session_on_action_rx(struct fst_iface *iface,
|
||||
const struct ieee80211_mgmt *mgmt, size_t len);
|
||||
|
||||
|
||||
int fst_session_set_str_ifname(struct fst_session *s, const char *ifname,
|
||||
bool is_old);
|
||||
int fst_session_set_str_peer_addr(struct fst_session *s, const char *mac,
|
||||
bool is_old);
|
||||
int fst_session_set_str_llt(struct fst_session *s, const char *llt_str);
|
||||
|
||||
#ifdef CONFIG_FST_TEST
|
||||
|
||||
#define FST_FSTS_ID_NOT_FOUND ((u32) -1)
|
||||
|
||||
int fst_test_req_send_fst_request(const char *params);
|
||||
int fst_test_req_send_fst_response(const char *params);
|
||||
int fst_test_req_send_ack_request(const char *params);
|
||||
int fst_test_req_send_ack_response(const char *params);
|
||||
int fst_test_req_send_tear_down(const char *params);
|
||||
u32 fst_test_req_get_fsts_id(const char *params);
|
||||
int fst_test_req_get_local_mbies(const char *request, char *buf,
|
||||
size_t buflen);
|
||||
|
||||
#endif /* CONFIG_FST_TEST */
|
||||
|
||||
#endif /* FST_SESSION_H */
|
||||
Reference in New Issue
Block a user