blob: 76a4984ceab88afbb109ac7f3281f0250ce0704e [file] [log] [blame]
/*
* hostapd / Interface client monitor
* Copyright (c) 2016 Google, Inc.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "includes.h"
#include "common/defs.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
#include "common.h"
#include "monitor_sta.h"
struct monitor_sta_info_element {
struct monitor_sta_info_element *next;
struct monitor_sta_info_element *hnext;
struct os_time timestamp; /* timestamp of sta entry to list */
struct monitor_sta_info info;
};
struct monitor_sta {
struct monitor_sta_info_element *mon_list; /* STA info list head */
#define STA_HASH_SIZE 256
#define STA_HASH(sta) (sta[5])
struct monitor_sta_info_element *mon_hash[STA_HASH_SIZE];
u16 count; /* number of station entered to list */
u32 hyst; /* configurable parameter, used to generate event */
};
/*
* monitor_sta_get - get pointer to mon station
* mon_sta: poiinter to station monitor structure
* mac: mac address of station to be searched
* return: pointer to monitor_sta_info_element
*/
static struct monitor_sta_info_element *monitor_sta_get(mon_sta_t mon_sta,
const u8 *mac)
{
struct monitor_sta_info_element *sta;
sta = mon_sta->mon_hash[STA_HASH(mac)];
while (sta != NULL && os_memcmp(sta->info.addr, mac, 6) != 0)
sta = sta->next;
return sta;
}
/*
* montor_sta_list_del - delete station entry from list
* mon_sta: pointer to station monitor structure
* sta: pointer to monitor_sta_info_element
* function remove station entry from mon station list
* return: none
*/
static void monitor_sta_list_del(mon_sta_t mon_sta,
struct monitor_sta_info_element *sta)
{
struct monitor_sta_info_element *tmp;
if (mon_sta->mon_list == sta) {
mon_sta->mon_list = sta->next;
mon_sta->count--;
return;
}
tmp = mon_sta->mon_list;
while (tmp != NULL && tmp->next != sta) {
tmp = tmp->next;
}
if (tmp == NULL) {
wpa_printf(MSG_DEBUG, "client monitor: list delete failed");
} else {
tmp->next = sta->next;
mon_sta->count--;
}
}
/*
* monitor_sta_hash_del - delete hash entry
* mon_sta: pointer to station monitor structure
* sta: pointer to monitor_sta_info_element
* function removes entry from hash list
* return: none
*/
static void monitor_sta_hash_del(mon_sta_t mon_sta,
struct monitor_sta_info_element *sta)
{
struct monitor_sta_info_element *tmp;
tmp = mon_sta->mon_hash[STA_HASH(sta->info.addr)];
if (!tmp)
return;
if (os_memcmp(tmp->info.addr, sta->info.addr, 6) == 0) {
mon_sta->mon_hash[STA_HASH(sta->info.addr)] = tmp->hnext;
return;
}
while(tmp->hnext != NULL &&
os_memcmp(tmp->hnext->info.addr,
sta->info.addr, 6) != 0 )
tmp = tmp->hnext;
if (tmp->hnext != NULL)
tmp->hnext = tmp->hnext->hnext;
else
wpa_printf(MSG_DEBUG, "client monitor: hash delete failed");
}
/*
* monitor_sta_deinit - deinitialize client monitor
* mon_sta: pointer to station monitor structure
* function deinitialize client monitor module
* return : none
*/
void monitor_sta_deinit(mon_sta_t mon_sta)
{
struct monitor_sta_info_element *s1, *s2;
if (!mon_sta)
return;
s1 = mon_sta->mon_list;
while(s1) {
s2 = s1;
s1 = s1->next;
monitor_sta_hash_del(mon_sta, s2);
monitor_sta_list_del(mon_sta, s2);
os_free(s2);
}
os_free(mon_sta);
}
/*
* mon_sta_free - search and delete station
* mon_sta: pointer to station monitor structure
* function seaches for the station and delete's entry
* return: none
*/
void monitor_sta_free(mon_sta_t mon_sta, const u8 *mac)
{
struct monitor_sta_info_element *sta;
if (!mon_sta)
return;
sta = monitor_sta_get(mon_sta, mac);
if (sta == NULL) {
wpa_printf(MSG_DEBUG, "client monitor: sta free failed for "
MACSTR, MAC2STR(mac));
return;
}
monitor_sta_hash_del(mon_sta, sta);
monitor_sta_list_del(mon_sta, sta);
os_free(sta);
}
/* monitor_sta_config_hysteresis - configure mon hysteresis
* signal: signal hysteresis to be configured
* return: 0 - Success, -1 - Failure
*/
int monitor_sta_set_hysteresis(mon_sta_t mon_sta, int hyst)
{
if (!mon_sta)
return -1;
if (hyst < 0 || hyst > 100)
return -1;
mon_sta->hyst = hyst;
return 0;
}
/*
* monitor_sta_info_update - update mon structure info
* mon_sta: pointer to station monitor structure
* event: mon event
* mac: mac address from which probe request received
* signal: signal value of probe request
* elems: ieee80211 elements
* nss : number of spatial streams
* function update data to station monitor structure on receiving probe request if
* table has entry for it
* return: TRUE - Success, FALSE - Failure to update/signal not changed
*/
Boolean monitor_sta_info_update(mon_sta_t mon_sta, mon_sta_event event,
const u8 *mac, int signal,
struct ieee802_11_elems elems, u16 *nss)
{
struct monitor_sta_info_element *sta;
struct os_time now;
struct ieee80211_ht_capabilities *ht= NULL;
struct ieee80211_vht_capabilities *vht = NULL;
u32 hyst_diff = 0;
if (!mon_sta)
return FALSE;
sta = monitor_sta_get(mon_sta, mac);
if (sta == NULL) {
wpa_printf(MSG_DEBUG, "client monitor: update failed for "
"station " MACSTR, MAC2STR(mac));
return FALSE;
}
os_get_time(&now);
if (elems.vht_capabilities) {
vht =
(struct ieee80211_vht_capabilities *)elems.vht_capabilities;
*nss = ieee802_11_find_vht_nss(vht);
} else if (elems.ht_capabilities) {
ht = (struct ieee80211_ht_capabilities *)elems.ht_capabilities;
*nss = ieee802_11_find_ht_nss(ht);
}
os_memcpy(sta->info.addr, mac, 6);
sta->info.nss = *nss;
sta->info.mode = elems.vht_capabilities ? MODE_VHT :
elems.ht_capabilities ? MODE_HT : MODE_LEGACY;
sta->info.signal = signal;
os_get_time(&(sta->info.timestamp));
/* Need to do for the first time when sta added to list */
if (!sta->info.last_event_signal) {
sta->info.last_event_signal = signal;
return TRUE;
}
hyst_diff = abs(abs(signal) - abs(sta->info.last_event_signal));
if (hyst_diff >= mon_sta->hyst) {
sta->info.last_event_signal = signal;
return TRUE;
}
return FALSE;
}
/*
* monitor_sta_hash_add - add station to hash list
* mon_sta: pointer to station monitor structure
* sta: pointer to monitor_sta_info_element
* function makes entry to hash list
* return: none
*/
static void monitor_sta_hash_add(mon_sta_t mon_sta,
struct monitor_sta_info_element *sta)
{
sta->hnext = mon_sta->mon_hash[STA_HASH(sta->info.addr)];
mon_sta->mon_hash[STA_HASH(sta->info.addr)] = sta;
}
/*
* monitor_sta_del_oldest - delete oldest station
* mon_sta: pointer to station monitor structure
* function find's the oldest station from list
* return: none
*/
static void monitor_sta_del_oldest(mon_sta_t mon_sta)
{
struct monitor_sta_info_element *s1;
u8 mac[6];
os_time_t largest = 0;
s1 = mon_sta->mon_list;
while(s1) {
if (s1->timestamp.sec > largest) {
largest = s1->timestamp.sec;
os_memcpy(mac, s1->info.addr, 6);
}
s1 = s1->next;
}
monitor_sta_free(mon_sta, mac);
}
/* monitor_sta_add - add station to list
* mon_sta: pointer to station monitor structure
* mac : mac address of station to be entered to the list
* function makes sta entry to sta list on receiving iapp notification
* if sta entry not exists
* return : TRUE - Success, FALSE - Failure
*/
Boolean monitor_sta_add(mon_sta_t mon_sta, const u8 *mac)
{
struct monitor_sta_info_element *sta;
if (!mon_sta)
return FALSE;
sta = monitor_sta_get(mon_sta, mac);
if (sta) {
os_get_time(&(sta->timestamp));
return TRUE;
}
if (mon_sta->count >= MAX_MONITOR_STA)
monitor_sta_del_oldest(mon_sta);
sta = os_zalloc(sizeof(struct monitor_sta_info_element));
if (sta == NULL) {
wpa_printf(MSG_ERROR, "client monitor: malloc failure");
return FALSE;
}
os_memcpy(sta->info.addr, mac, 6);
sta->next = mon_sta->mon_list;
mon_sta->mon_list = sta;
mon_sta->count++;
monitor_sta_hash_add(mon_sta, sta);
os_get_time(&(sta->timestamp));
return TRUE;
}
/*
* monitor_sta_init - initializes client monitor
* function initializes monitor structure for each radio interface
* return : pointer to monitor_sta - Success, NULL - Failure
*/
mon_sta_t monitor_sta_init()
{
struct monitor_sta *mon_sta;
mon_sta = os_zalloc(sizeof(*mon_sta));
if (mon_sta == NULL) {
wpa_printf(MSG_ERROR, "client monitor: malloc failure");
return NULL;
}
mon_sta->hyst = MONITOR_STA_RSSI_HYSTERESIS;
return mon_sta;
}