blob: 5ebba764bb817bf439f1e1374ce96669c287f402 [file] [log] [blame]
/*
* hostapd / AP configuration knobs
* Copyright(c) 2017 - Google 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/wpa_debug.h"
#include "common/wpa_ctrl.h"
#include "common/ieee802_11_defs.h"
#include "hostapd.h"
#include "sta_info.h"
#include "ap/sta_policy.h"
#define STR_NCMP(str, cmpstr) os_strncmp(str, cmpstr, strlen(cmpstr))
static const char *config_params[] = {
"sta_id=",
"supp_rates=",
"short_preamble=",
"sgi20=",
"sgi40=",
"ldpc=",
"smps=",
"max_amsdu_len=",
"rifs=",
"max_ampdu_len=",
"rx_ldpc=",
"sgi80=",
"ampdu_subframe_count=",
"pspoll_sta_ko_enabled=",
"min_mpdu_spacing=",
"tx_stbc="
};
/**
* Returns the interface name used for steering this BSS. This corresponds to
* the name of the first BSS on the interface.
*/
static inline char *get_iface_name(const struct hostapd_data *hapd)
{
return hapd->iface->bss[0]->conf->iface;
}
#ifdef CONFIG_STA_POLICY
void sta_policy_begin_assoc_resp(struct hostapd_data *hapd, uint8_t *sta_addr)
{
struct per_interface_config *i_cfg = hapd->iface->i_cfg;
if (!i_cfg)
return;
os_memcpy(i_cfg->associating_sta, sta_addr, ETH_ALEN);
i_cfg->assoc_resp = 1;
}
void sta_policy_end_assoc_resp(struct hostapd_data *hapd)
{
struct per_interface_config *i_cfg = hapd->iface->i_cfg;
if (!i_cfg)
return;
os_memset(i_cfg->associating_sta, 0, ETH_ALEN);
i_cfg->assoc_resp = 0;
}
#endif /* CONFIG_STA_POLICY */
static void sta_policy_dump(const struct sta_policy *cfg)
{
if (cfg != NULL) {
wpa_printf(MSG_INFO, "STA_ID: "MACSTR, MAC2STR(cfg->sta_id));
wpa_printf(MSG_INFO, "num_sup_rates=%d", cfg->num_sup_rates);
wpa_hexdump_ascii(MSG_INFO, "Supp Rates:", cfg->supp_rates,
cfg->num_sup_rates);
wpa_printf(MSG_INFO, "Param read:\n"
"Capab=0x%x [Mask:0x%x]\n"
"ht_capab_info=0x%x [Mask:0x%x]\n"
"ht_ampdu_param=0x%x [Mask:0x%x]\n"
"vht_capab_info=0x%x [Mask:0x%x]\n",
cfg->capability, cfg->capability_mask,
cfg->ht_capab_info, cfg->ht_capab_info_mask,
cfg->ht_ampdu_param, cfg->ht_ampdu_param_mask,
cfg->vht_capab_info, cfg->vht_capab_info_mask);
}
}
static void sta_policy_list_dump(const struct sta_policy **head)
{
struct sta_policy *cfg = *head;
while (cfg != NULL) {
sta_policy_dump(cfg);
cfg = cfg->next;
}
}
/**
* Parse the sta policy rate params and update into sta policy structure
*/
static int parse_rate_string(struct hostapd_data *hapd,
struct sta_policy *cfg, char *rate_str)
{
char *rate, *context = NULL;
int i, cnt = 0, flag = 0;
struct hostapd_hw_modes *hw_features = hapd->iface->hw_features;
if (rate_str) {
while ((rate = str_token(rate_str, ",", &context)) != NULL &&
cnt < MAX_RATES_SUPPORTED) {
sscanf(rate, "%x", &cfg->supp_rates[cnt]);
for (i= 0; i < hw_features->num_rates; i++) {
if (((cfg->supp_rates[cnt] & RATE_MASK)*10)/2 ==
hw_features->rates[i]) {
flag = 1;
break;
}
}
if (!flag) {
wpa_printf (MSG_ERROR, "Error parsing rate"
" 0x%x", cfg->supp_rates[cnt]);
os_memset(cfg->supp_rates, 0,
MAX_RATES_SUPPORTED);
cfg->num_sup_rates = 0;
return -1;
}
flag = 0;
cnt++;
}
}
cfg->num_sup_rates = cnt;
return 0;
}
static int get_param(const char *param)
{
int i;
for(i = 0; i < POLICY_PARAM_MAX; i++) {
if(!STR_NCMP(param, config_params[i])) {
return i;
}
}
return -1;
}
static int populate_sta_policy(struct hostapd_data *hapd,
struct sta_policy *cfg,
char *param, char *value)
{
int val;
switch (get_param(param)) {
case POLICY_PARAM_STA_ID:
return 0;
case POLICY_PARAM_SUPP_RATES:
if (atoi(value) == -1) {
os_memset(cfg->supp_rates, 0, sizeof(cfg->supp_rates));
return 0;
}
if (parse_rate_string(hapd, cfg, value) != 0) {
return -1;
}
break;
case POLICY_PARAM_SHORT_PREAMBLE:
val = atoi(value);
cfg->capability_mask |= WLAN_CAPABILITY_SHORT_PREAMBLE;
if (val == 1) {
cfg->capability |= WLAN_CAPABILITY_SHORT_PREAMBLE;
} else {
cfg->capability &= ~WLAN_CAPABILITY_SHORT_PREAMBLE;
}
break;
case POLICY_PARAM_SGI20:
val = atoi(value);
cfg->ht_capab_info_mask |= HT_CAP_INFO_SHORT_GI20MHZ;
if (val == 1) {
cfg->ht_capab_info |= HT_CAP_INFO_SHORT_GI20MHZ;
} else {
cfg->ht_capab_info &= ~HT_CAP_INFO_SHORT_GI20MHZ;
}
break;
case POLICY_PARAM_SGI40:
val = atoi(value);
cfg->ht_capab_info_mask |= HT_CAP_INFO_SHORT_GI40MHZ;
if (val == 1) {
cfg->ht_capab_info |= HT_CAP_INFO_SHORT_GI40MHZ;
} else {
cfg->ht_capab_info &= ~HT_CAP_INFO_SHORT_GI40MHZ;
}
break;
case POLICY_PARAM_LDPC:
val = atoi(value);
cfg->ht_capab_info_mask |= HT_CAP_INFO_LDPC_CODING_CAP;
if (val == 1) {
cfg->ht_capab_info |= HT_CAP_INFO_LDPC_CODING_CAP;
} else {
cfg->ht_capab_info &= ~HT_CAP_INFO_LDPC_CODING_CAP;
}
break;
case POLICY_PARAM_SMPS:
val = atoi(value);
cfg->ht_capab_info_mask |= HT_CAP_INFO_SMPS_MASK;
/* Clear current SMPS config */
cfg->ht_capab_info &= ~HT_CAP_INFO_SMPS_MASK;
switch (val) {
case 0:
cfg->ht_capab_info |= HT_CAP_INFO_SMPS_STATIC;
break;
case 1:
cfg->ht_capab_info |= HT_CAP_INFO_SMPS_DYNAMIC;
break;
case 3:
cfg->ht_capab_info |= HT_CAP_INFO_SMPS_MASK;
break;
default:
cfg->ht_capab_info_mask &= ~HT_CAP_INFO_SMPS_MASK;
cfg->ht_capab_info &= ~HT_CAP_INFO_SHORT_GI40MHZ;
}
break;
case POLICY_PARAM_MAX_AMSDU_LEN:
val = atoi(value);
cfg->ht_capab_info_mask |= HT_CAP_INFO_MAX_AMSDU_SIZE;
if (val == 1) {
cfg->ht_capab_info |= HT_CAP_INFO_MAX_AMSDU_SIZE;
} else {
cfg->ht_capab_info &= ~HT_CAP_INFO_MAX_AMSDU_SIZE;
}
break;
case POLICY_PARAM_RIFS:
val = atoi(value);
cfg->ht_op_info_mask |= HT_INFO_HT_PARAM_RIFS_MODE;
if (val == 1) {
cfg->ht_op_info |= HT_INFO_HT_PARAM_RIFS_MODE;
} else {
cfg->ht_op_info &= ~HT_INFO_HT_PARAM_RIFS_MODE;
}
break;
case POLICY_PARAM_MAX_AMPDU_LEN:
val = atoi(value);
cfg->vht_capab_info_mask |= VHT_CAP_MAX_MPDU_LENGTH_MASK;
cfg->vht_capab_info &= ~VHT_CAP_MAX_MPDU_LENGTH_MASK;
switch(val) {
case 0:
break;
case 1:
cfg->vht_capab_info |= VHT_CAP_MAX_MPDU_LENGTH_7991;
break;
case 2:
cfg->vht_capab_info |= VHT_CAP_MAX_MPDU_LENGTH_11454;
break;
default:
cfg->vht_capab_info &= ~VHT_CAP_MAX_MPDU_LENGTH_MASK;
cfg->vht_capab_info_mask &= ~VHT_CAP_MAX_MPDU_LENGTH_MASK;
}
break;
case POLICY_PARAM_RX_LDPC:
val = atoi(value);
cfg->vht_capab_info_mask |= VHT_CAP_RXLDPC;
if (val == 1) {
cfg->vht_capab_info |= VHT_CAP_RXLDPC;
} else {
cfg->vht_capab_info &= ~VHT_CAP_RXLDPC;
}
break;
case POLICY_PARAM_SGI80:
val = atoi(value);
cfg->vht_capab_info_mask |= VHT_CAP_SHORT_GI_80;
if (val == 1) {
cfg->vht_capab_info |= VHT_CAP_SHORT_GI_80;
} else {
cfg->vht_capab_info &= ~VHT_CAP_SHORT_GI_80;
}
break;
case POLICY_PARAM_AMPDU_SUBFRAME_COUNT:
val = atoi(value);
cfg->flags |= FLAG_AMPDU_SUBFRAME_COUNT;
if (val >= 0 || val <= 64) {
cfg->ampdu_subframe_count = val;
} else {
wpa_printf (MSG_ERROR, "Wrong AMPDU_SUBFRAME_COUNT,"
"Value ranges from [0-64]");
return -1;
}
break;
case POLICY_PARAM_PSPOLL_STA_KO_ENABLED:
val = atoi(value);
cfg->flags |= FLAG_PSPOLL_STA_KO_ENABLED;
cfg->pspoll_sta_ko_enabled = (val) ? !!val : 0;
break;
case POLICY_PARAM_MIN_MPDU_SPACING:
val = atoi(value);
cfg->ht_ampdu_param_mask |= HT_PARAM_MIN_MPDU_START_SPACING_MASK;
if (val < MAX_MPDU_SPACING || val > 0) {
cfg->ht_ampdu_param = (val << 2) &
HT_PARAM_MIN_MPDU_START_SPACING_MASK;
} else {
cfg->ht_ampdu_param &=
~HT_PARAM_MIN_MPDU_START_SPACING_MASK;
}
break;
case POLICY_PARAM_TXSTBC:
val = atoi(value);
cfg->vht_capab_info_mask |= VHT_CAP_TXSTBC;
if (val == 1) {
cfg->vht_capab_info |= VHT_CAP_TXSTBC;
} else {
cfg->vht_capab_info &= ~VHT_CAP_TXSTBC;
}
break;
default:
wpa_printf(MSG_INFO, "Unknown param: %s\n", param);
return -1;
}
return 0;
}
/**
* DELETE an entry from the sta_policy list
*/
static int sta_policy_node_delete(struct sta_policy **head, uint8_t *sta_id)
{
struct sta_policy *cfg, *tmp;
cfg = tmp = *head;
if (!cfg) {
wpa_printf(MSG_ERROR, "Empty sta_policy list");
return -1;
}
while (cfg != NULL) {
if (os_memcmp(cfg->sta_id, sta_id, ETH_ALEN) == 0) {
if (cfg == *head)
*head = cfg->next;
else
tmp->next = cfg->next;
os_free(cfg);
return 0;
}
tmp = cfg;
cfg = cfg->next;
}
wpa_printf(MSG_INFO, "No " MACSTR " entry present", MAC2STR(sta_id));
return -1;
}
/**
* GET an entry from the sta_policy list
*/
static struct sta_policy *sta_policy_node_get(struct sta_policy **head,
u8 *sta_id)
{
struct sta_policy *cfg = *head;
if (!cfg) {
/* Return Null if sta_policy list is Empty*/
return NULL;
} else {
while (cfg != NULL) {
if (os_memcmp(cfg->sta_id, sta_id, ETH_ALEN) == 0) {
return cfg;
}
cfg = cfg->next;
}
}
return NULL;
}
/**
* ADD an entry to sta_policy list at end
*/
static void sta_policy_node_add_end(struct sta_policy **head,
struct sta_policy *cfg)
{
struct sta_policy *tmp = *head;
if (*head == NULL) {
cfg->next = NULL;
*head = cfg;
} else {
while(tmp->next != NULL) {
tmp = tmp->next;
}
tmp->next = cfg;
}
}
/**
* Free the list of sta_policy
*/
static void sta_policy_node_list_free(struct sta_policy **head)
{
struct sta_policy *cfg;
while ((cfg = (*head)) != NULL) {
*head = (*head)->next;
os_free(cfg);
}
}
/**
* Parse the sta policy string params and update into sta policy structure
*/
static int parse_and_add_sta_policy_entry(struct hostapd_data *hapd,
char *line)
{
char *param, *val, *context = NULL;
uint8_t sta_id[ETH_ALEN];
struct sta_policy *cfg;
struct per_interface_config *i_cfg = hapd->iface->i_cfg;
if (STR_NCMP(line, "sta_id=")) {
wpa_printf(MSG_ERROR, "No sta_id present as"
" first arg in params");
return -1;
}
if ((val = os_strstr(line, "="))) {
if (hwaddr_aton(val+1, &sta_id[0])) {
wpa_printf(MSG_ERROR, "Parsing sta_id failed");
return -1;
}
}
cfg = sta_policy_node_get(&i_cfg->l_sta_policy, sta_id);
if (!cfg) {
cfg = (struct sta_policy *)
os_zalloc(sizeof(struct sta_policy));
if (cfg == NULL) {
wpa_printf(MSG_ERROR, "%s: Failed to alloc"
" mem", __func__);
return -1;
}
os_memcpy(&cfg->sta_id, &sta_id, ETH_ALEN);
sta_policy_node_add_end(&i_cfg->l_sta_policy, cfg);
}
while ((param = str_token(line, " ", &context)) != NULL) {
if (*param == '\n' || *param == '\r')
break;
val = os_strstr(param, "=");
if (val == NULL) {
return -1;
}
val++;
if ((populate_sta_policy(hapd, cfg, param, val)) < 0) {
return -1;
}
}
if (ap_get_sta(hapd, cfg->sta_id))
sta_policy_send_event(hapd, cfg->sta_id);
sta_policy_dump(cfg);
return 0;
}
static int sta_policy_load(struct hostapd_iface *iface)
{
FILE *fp;
char *line = NULL;
size_t len = 0, ret = 0;
struct per_interface_config *i_cfg = iface->i_cfg;
fp = fopen(i_cfg->cfg_file, "r");
if (fp == NULL) {
wpa_printf(MSG_ERROR,"Failed to open file: %s",
i_cfg->cfg_file);
return -1;
}
while (getline(&line, &len, fp) != -1) {
ret = parse_and_add_sta_policy_entry(iface->bss[0], line);
if (ret != 0)
wpa_printf(MSG_ERROR, "Error parsing the line: %s", line);
}
if (line)
os_free(line);
fclose(fp);
return 0;
}
static int construct_supp_rates(struct sta_policy *cfg,
char *rates, int arr_size)
{
int i = 0;
int pos = 0, len = 0;
if (cfg->num_sup_rates == 0)
return -1;
while (cfg->supp_rates[i] && i < cfg->num_sup_rates) {
len = os_snprintf(rates + pos, arr_size - pos, "%02x,",
cfg->supp_rates[i++]);
if (os_snprintf_error(arr_size - pos, len))
return -1;
pos += len;
if (pos >= arr_size) {
wpa_printf(MSG_ERROR, "Insufficient memory");
break;
}
}
/* Remove the last ',' from the rate string */
*(rates + pos - 1) = '\0';
return pos;
}
static int construct_sta_policy(struct sta_policy *cfg,
char *line, unsigned int size)
{
char rates[64] = {0};
int len, pos = 0;
os_memset(line, 0, STA_POLICY_ENTRYSIZE);
len = os_snprintf(line, size, "sta_id="MACSTR, MAC2STR(cfg->sta_id));
if (os_snprintf_error(size - pos, len))
return -1;
pos += len;
if (cfg->num_sup_rates > 0) {
len = construct_supp_rates(cfg, rates, sizeof(rates));
if (len <= 0) {
return -1;
}
len = os_snprintf(line + pos, size - pos,
" supp_rates=%s", rates);
if (os_snprintf_error(size - pos, len))
return -1;
pos += len;
}
if (cfg->capability_mask & WLAN_CAPABILITY_SHORT_PREAMBLE) {
len = os_snprintf(line + pos, size - pos,
" short_preamble=%d",!!(cfg->capability
& WLAN_CAPABILITY_SHORT_PREAMBLE));
if (os_snprintf_error(size - pos, len))
return -1;
pos += len;
}
if (cfg->ht_capab_info_mask & HT_CAP_INFO_SHORT_GI20MHZ) {
len = os_snprintf(line + pos, size - pos,
" sgi20=%d", !!(cfg->ht_capab_info
& HT_CAP_INFO_SHORT_GI20MHZ));
if (os_snprintf_error(size - pos, len))
return -1;
pos += len;
}
if (cfg->ht_capab_info_mask & HT_CAP_INFO_SHORT_GI40MHZ) {
len = os_snprintf(line + pos, size - pos,
" sgi40=%d", !!(cfg->ht_capab_info
& HT_CAP_INFO_SHORT_GI40MHZ));
if (os_snprintf_error(size - pos, len))
return -1;
pos += len;
}
if (cfg->ht_capab_info_mask & HT_CAP_INFO_LDPC_CODING_CAP) {
len = os_snprintf(line + pos, size - pos,
" ldpc=%d", !!(cfg->ht_capab_info
& HT_CAP_INFO_LDPC_CODING_CAP));
if (os_snprintf_error(size - pos, len))
return -1;
pos += len;
}
if (cfg->ht_capab_info_mask & HT_CAP_INFO_SMPS_MASK) {
len = os_snprintf(line + pos, size - pos,
" smps=%d", (cfg->ht_capab_info
& HT_CAP_INFO_SMPS_MASK) >> 2);
if (os_snprintf_error(size - pos, len))
return -1;
pos += len;
}
if (cfg->ht_capab_info_mask & HT_CAP_INFO_MAX_AMSDU_SIZE) {
len = os_snprintf(line + pos, size - pos,
" max_amsdu_len=%d", !!(cfg->ht_capab_info
& HT_CAP_INFO_MAX_AMSDU_SIZE));
if (os_snprintf_error(size - pos, len))
return -1;
pos += len;
}
if (cfg->ht_op_info_mask & HT_INFO_HT_PARAM_RIFS_MODE) {
len = os_snprintf(line + pos, size - pos,
" rifs=%d", !!(cfg->ht_op_info
& HT_INFO_HT_PARAM_RIFS_MODE));
if (os_snprintf_error(size - pos, len))
return -1;
pos += len;
}
if (cfg->vht_capab_info_mask & VHT_CAP_MAX_MPDU_LENGTH_MASK) {
len = os_snprintf(line + pos, size - pos,
" max_ampdu_len=%d", cfg->vht_capab_info
& VHT_CAP_MAX_MPDU_LENGTH_MASK);
if (os_snprintf_error(size - pos, len))
return -1;
pos += len;
}
if (cfg->vht_capab_info_mask & VHT_CAP_RXLDPC) {
len = os_snprintf(line + pos, size - pos,
" rx_ldpc=%d", !!(cfg->vht_capab_info
& VHT_CAP_RXLDPC));
if (os_snprintf_error(size - pos, len))
return -1;
pos += len;
}
if (cfg->vht_capab_info_mask & VHT_CAP_SHORT_GI_80) {
len = os_snprintf(line + pos, size - pos,
" sgi80=%d", !!(cfg->vht_capab_info
& VHT_CAP_SHORT_GI_80));
if (os_snprintf_error(size - pos, len))
return -1;
pos += len;
}
if (cfg->flags & FLAG_AMPDU_SUBFRAME_COUNT) {
len = os_snprintf(line + pos, size - pos,
" ampdu_subframe_count=%d", cfg->ampdu_subframe_count);
if (os_snprintf_error(size - pos, len))
return -1;
pos += len;
}
if (cfg->flags & FLAG_PSPOLL_STA_KO_ENABLED) {
len = os_snprintf(line + pos, size - pos,
" pspoll_sta_ko_enabled=%d", cfg->pspoll_sta_ko_enabled);
if (os_snprintf_error(size - pos, len))
return -1;
pos += len;
}
if (cfg->ht_ampdu_param_mask & HT_PARAM_MIN_MPDU_START_SPACING_MASK) {
len = os_snprintf(line + pos, size - pos,
" min_mpdu_spacing=%d", (cfg->ht_ampdu_param &
HT_PARAM_MIN_MPDU_START_SPACING_MASK) >> 2);
if (os_snprintf_error(size - pos, len))
return -1;
pos += len;
}
if (cfg->vht_capab_info_mask & VHT_CAP_TXSTBC) {
len = os_snprintf(line + pos, size - pos,
" tx_stbc=%d", !!(cfg->vht_capab_info &
VHT_CAP_TXSTBC));
if (os_snprintf_error(size - pos, len))
return -1;
pos += len;
}
strcat(line, "\n");
return pos + 1;
}
/**
* WRITE the sta policy to the file
*/
static int sta_policy_save(struct per_interface_config *i_cfg)
{
struct sta_policy *ptr, *cfg = i_cfg->l_sta_policy;
FILE *fp;
char line_buf[STA_POLICY_ENTRYSIZE];
char *temp = NULL;
int len = 0, pos = 0;
temp = (char *) os_zalloc (MAX_STA_POLICY_SIZE);
if (!temp) {
return -1;
}
while (cfg != NULL) {
len = construct_sta_policy(cfg, line_buf, sizeof(line_buf));
if (len < 0)
return -1;
pos += len;
if (pos > MAX_STA_POLICY_SIZE - 1) {
/* Free the last node added, if we reach the max
* buffer size */
ptr->next = NULL;
os_free(cfg);
os_free(temp);
return -1;
}
strcat(temp, line_buf);
ptr = cfg;
cfg = cfg->next;
}
fp = fopen(i_cfg->cfg_file, "w+");
if (!fp) {
wpa_printf(MSG_ERROR,"Failed to open file: %s",
i_cfg->cfg_file);
goto exit;
}
fwrite(temp, os_strlen(temp), 1, fp);
fclose(fp);
return 0;
exit:
os_free(temp);
return -1;
}
#ifdef CONFIG_STA_POLICY
/**
* Reply with the existing sta policy setting for the given sta
* sta_id=00:00:00:00:00:00 indicates for all STA setting request
* Returns 0 on success, -1 on failure
*/
int sta_policy_get(struct hostapd_data *hapd, char *buf,
char *reply, int reply_size)
{
uint8_t sta_id[ETH_ALEN];
char line_buf[STA_POLICY_ENTRYSIZE];
size_t len = 0, pos = 0;
uint8_t zero_sta_id[] = "sta_id=00:00:00:00:00:00";
char *ptr;
struct sta_policy *cfg, *list;
struct per_interface_config *i_cfg = hapd->iface->i_cfg;
if (!i_cfg) {
wpa_printf(MSG_ERROR, "sta_policy.conf does not exist");
return -1;
}
os_memset(reply, 0, reply_size);
ptr = os_strstr(buf, "sta_id=");
if (ptr == NULL) {
wpa_printf(MSG_ERROR, "No sta_id mentioned");
return -1;
}
ptr = ptr + 7;
if (hwaddr_aton(ptr, sta_id) != 0) {
wpa_printf(MSG_INFO, "Could not parse MAC addr");
return -1;
}
cfg = sta_policy_node_get(&i_cfg->l_sta_policy, sta_id);
if (cfg) {
construct_sta_policy(cfg, line_buf, sizeof(line_buf));
len = os_snprintf(reply, reply_size, "%s", line_buf);
if (os_snprintf_error(reply_size - pos, len))
return -1;
pos += len;
} else if (!os_memcmp(buf, zero_sta_id, strlen(zero_sta_id))) {
list = i_cfg->l_sta_policy;
if (!list) {
return -1;
} else {
while (list != NULL) {
construct_sta_policy(list, line_buf,
sizeof(line_buf));
len = os_snprintf(reply + pos,
reply_size - pos, "%s", line_buf);
if (os_snprintf_error(reply_size - pos, len))
return -1;
pos += len;
list = list->next;
}
}
} else {
wpa_printf(MSG_ERROR, "Error getting the entry");
return -1;
}
return pos;
}
/**
* Delete an Entry in sta_policy file on a given interface
* Returns 0 on success, -1 on failure
*/
int sta_policy_del(struct hostapd_data *hapd, char *buf)
{
int ret = 0;
uint8_t sta_id[ETH_ALEN];
struct sta_policy *cfg;
char *ptr;
struct per_interface_config *i_cfg = hapd->iface->i_cfg;
if (!i_cfg) {
wpa_printf(MSG_ERROR, "sta_policy.conf does not exist");
return -1;
}
ptr = os_strstr(buf, "sta_id=");
if (ptr == NULL) {
wpa_printf(MSG_ERROR, "No sta_id mentioned");
return -1;
}
ptr = ptr + 7;
if (hwaddr_aton(ptr, sta_id) != 0) {
wpa_printf(MSG_INFO, "Could not parse MAC addr");
return -1;
}
/* Delete from the file and sta_policy DS */
ret = sta_policy_node_delete(&i_cfg->l_sta_policy, sta_id);
if (!ret)
ret = sta_policy_save(i_cfg);
return ret;
}
#endif /* CONFIG_STA_POLICY */
/**
* Add supported rate and extended rate IE if sta policy rate settings exists
*/
static u8 *sta_policy_eid_rate(struct hostapd_data *hapd,
struct sta_policy *cfg, u8 *eid)
{
u8 *pos = eid;
int num = (cfg->num_sup_rates > 8) ? 8 : cfg->num_sup_rates;
if (!num)
return NULL;
// Create Supported rate IE
*pos++ = WLAN_EID_SUPP_RATES;
*pos++ = num;
os_memcpy(pos, &cfg->supp_rates[0], num);
pos += num;
//Create extended rate IE
if (cfg->num_sup_rates > 8) {
*pos++ = WLAN_EID_EXT_SUPP_RATES;
*pos++ = (cfg->num_sup_rates - 8);
os_memcpy(pos, &cfg->supp_rates[8], cfg->num_sup_rates - 8);
pos += cfg->num_sup_rates - 8;
}
return pos;
}
#ifdef CONFIG_STA_POLICY
/**
* Copy the Existing STA policy supported rate to the buffer passed,
* if the sta_id matched the list
* Returns value <= 0 on Failure
* No of bytes copied on success
*/
int sta_policy_get_supp_rate(struct hostapd_data *hapd, u8 *sta_addr, u8 *rate)
{
struct per_interface_config *i_cfg = hapd->iface->i_cfg;
struct sta_policy *cfg;
if (!i_cfg)
return -1;
cfg = sta_policy_node_get(&i_cfg->l_sta_policy, sta_addr);
if (!cfg) {
return -1;
}
if (!cfg->num_sup_rates)
return -1;
os_memcpy(rate, cfg->supp_rates, cfg->num_sup_rates);
return cfg->num_sup_rates;
}
/**
* Create the Supported rates EID and Extended rates EID if there
* is a sta policy entry settings available for the given sta_addr
* Return -1 if no EID added,
* 0 incremented eid pointer if EID added
*/
u8 *sta_policy_copy_supp_rate(struct hostapd_data *hapd, u8 *sta_addr,
u8 *eid, size_t *res)
{
struct per_interface_config *i_cfg = hapd->iface->i_cfg;
struct sta_policy *cfg;
u8 *pos;
if (!i_cfg)
goto fail;
cfg = sta_policy_node_get(&i_cfg->l_sta_policy, sta_addr);
if (!cfg) {
goto fail;
}
pos = sta_policy_eid_rate(hapd, cfg, eid);
if (pos == NULL) {
goto fail;
}
return pos;
fail:
*res = -1;
return eid;
}
/**
* Update the capabilities, only if the current associating
* STA mac addr is part of the sta policy list
*/
void sta_policy_update_capab(struct hostapd_data *hapd,
uint16_t *capability)
{
struct per_interface_config *i_cfg = hapd->iface->i_cfg;
struct sta_policy *cfg;
if (!i_cfg)
return;
if (!i_cfg->assoc_resp)
return;
cfg = sta_policy_node_get(&i_cfg->l_sta_policy,
i_cfg->associating_sta);
if (!cfg) {
return;
}
*capability &= ~(cfg->capability_mask & i_cfg->_capability_mask);
*capability |= (cfg->capability & (cfg->capability_mask &
i_cfg->_capability_mask));
}
/**
* Update the HT cap, only if the associating STA mac addr
* is part of the sta policy list
*/
void sta_policy_update_ht_cap(struct hostapd_data *hapd,
struct ieee80211_ht_capabilities *cap)
{
struct per_interface_config *i_cfg = hapd->iface->i_cfg;
struct sta_policy *cfg;
if (!i_cfg)
return;
if (!i_cfg->assoc_resp)
return;
cfg = sta_policy_node_get(&i_cfg->l_sta_policy,
i_cfg->associating_sta);
if (!cfg) {
return;
}
/* Update HT CAP INFO */
cap->ht_capabilities_info &= ~(cfg->ht_capab_info_mask &
i_cfg->_ht_capab_info_mask);
cap->ht_capabilities_info |= (cfg->ht_capab_info &
(cfg->ht_capab_info_mask & i_cfg->_ht_capab_info_mask));
/* Update AMPDU param */
cap->a_mpdu_params &= ~(cfg->ht_ampdu_param_mask &
i_cfg->_ht_ampdu_param_mask);
cap->a_mpdu_params |= (cfg->ht_ampdu_param &
(cfg->ht_ampdu_param_mask ));
}
/**
* Update the HT Operation Info, only if the associating STA mac addr
* is part of the sta policy list
*/
void sta_policy_update_ht_op_info(struct hostapd_data *hapd,
struct ieee80211_ht_operation *op)
{
struct per_interface_config *i_cfg = hapd->iface->i_cfg;
struct sta_policy *cfg;
if (!i_cfg)
return;
if (!i_cfg->assoc_resp)
return;
cfg = sta_policy_node_get(&i_cfg->l_sta_policy,
i_cfg->associating_sta);
if (!cfg) {
return;
}
/* Update HT Operation Info */
op->ht_param &= ~(cfg->ht_op_info_mask &
i_cfg->_ht_op_info_mask);
op->ht_param |= (cfg->ht_op_info &
(cfg->ht_op_info_mask & i_cfg->_ht_op_info_mask));
}
/**
* Update the VHT cap, only if the associating STA mac addr
* is part of the sta policy list
*/
void sta_policy_update_vht_cap(struct hostapd_data *hapd,
struct ieee80211_vht_capabilities *cap)
{
struct per_interface_config *i_cfg = hapd->iface->i_cfg;
struct sta_policy *cfg;
if (!i_cfg)
return;
if (!i_cfg->assoc_resp)
return;
cfg = sta_policy_node_get(&i_cfg->l_sta_policy,
i_cfg->associating_sta);
if (!cfg) {
return;
}
/* Update HT CAP INFO */
cap->vht_capabilities_info &= ~(cfg->vht_capab_info_mask &
i_cfg->_vht_capab_info_mask);
cap->vht_capabilities_info |= (cfg->vht_capab_info &
(cfg->vht_capab_info_mask & i_cfg->_vht_capab_info_mask));
}
/**
* Adds an entry into sta policy file for the given interface
* Retruns 0 if success, -1 if fails
*/
int sta_policy_add(struct hostapd_data *hapd, char *buf)
{
int len;
struct per_interface_config *i_cfg = hapd->iface->i_cfg;
if (!i_cfg) {
wpa_printf(MSG_ERROR, "sta_policy.conf does not exist, "
" cannot add policy");
goto fail;
}
len = strlen(buf);
if (len > STA_POLICY_ENTRYSIZE) {
wpa_printf(MSG_ERROR, "Entry size (%d) > %d",
len, STA_POLICY_ENTRYSIZE);
goto fail;
}
if (parse_and_add_sta_policy_entry(hapd, buf) != 0) {
goto fail;
}
/* Write to file */
if (sta_policy_save(i_cfg)) {
goto fail;
}
return 0;
fail:
return -1;
}
void sta_policy_send_event(struct hostapd_data *hapd, uint8_t *sta_addr)
{
struct per_interface_config *i_cfg = hapd->iface->i_cfg;
struct sta_policy *cfg;
if (!i_cfg)
return;
cfg = sta_policy_node_get(&i_cfg->l_sta_policy, sta_addr);
if (!cfg) {
return;
}
if (cfg->flags & FLAG_AMPDU_SUBFRAME_COUNT) {
wpa_msg(hapd->msg_ctx, MSG_INFO, STA_POLICY_AMPDU_SUBFRAME_COUNT
MACSTR " %d", MAC2STR(cfg->sta_id),
cfg->ampdu_subframe_count);
}
if (cfg->flags & FLAG_PSPOLL_STA_KO_ENABLED) {
wpa_msg(hapd->msg_ctx, MSG_INFO, STA_POLICY_PSPOLL_STA_KO_ENABLED
MACSTR " %d", MAC2STR(cfg->sta_id),
cfg->pspoll_sta_ko_enabled);
}
}
/**
* This function will initilalize the Dynamic Mask values of the
* sta policy params for the given interface
*/
int stapolicy_cfg_init(struct hostapd_iface *iface)
{
struct per_interface_config *i_cfg = iface->i_cfg;
/* NULL check for iface->hw_features to avoid segmentation fault */
if (!iface->hw_features) {
hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO, "hw_features is NULL");
return -1;
}
/* read file and load the params */
if (sta_policy_load(iface)) {
os_free(i_cfg->cfg_file);
os_free(i_cfg);
iface->i_cfg = NULL;
return 0;
}
sta_policy_list_dump(&i_cfg->l_sta_policy);
/* Dynamic MASK Values, based on driver/hw capabilities */
i_cfg->_capability_mask = CONFIGURABLE_BITS_CAP;
i_cfg->_ht_capab_info_mask = iface->hw_features->ht_capab &
CONFIGURABLE_BITS_HT_CAP;
i_cfg->_ht_ampdu_param_mask = iface->hw_features->a_mpdu_params &
CONFIGURABLE_BITS_MPDU;
i_cfg->_ht_op_info_mask = CONFIGURABLE_BITS_HT_OP_INFO;
i_cfg->_vht_capab_info_mask = iface->hw_features->vht_capab &
CONFIGURABLE_BITS_VHT_CAP;
return 0;
}
/**
* Initialization function for STA policy module.
* Should be called for every hostapd_iface init.
* This function allocates the required memory and reads
* the sta policy file based on the interface being
* initialized.
*
* Returns 0 - Success
* 1 - Failure
*/
int stapolicy_interface_init(struct hostapd_iface *iface)
{
struct per_interface_config *i_cfg;
struct hostapd_data *hapd = iface->bss[0];
int file_len;
iface->i_cfg = NULL;
/* Allocate memory for the interface */
i_cfg = (struct per_interface_config *) os_zalloc
(sizeof(struct per_interface_config));
if (!i_cfg)
return -1;
iface->i_cfg = i_cfg;
/* Generate persta config file name */
if (get_iface_name(hapd))
strncpy(i_cfg->iface_name, get_iface_name(hapd), IFNAMSIZ);
file_len = os_strlen(STA_POLICY_DIR) +
os_strlen(i_cfg->iface_name) +
os_strlen(STA_POLICY_FILENAME) + 1;
i_cfg->cfg_file = (char *) os_zalloc(file_len);
if (!i_cfg->cfg_file) {
os_free(i_cfg);
iface->i_cfg = NULL;
return -1;
}
/* Create config file name */
os_snprintf(i_cfg->cfg_file, STA_POLICY_ENTRYSIZE, "%s%s%s",
STA_POLICY_DIR, i_cfg->iface_name, STA_POLICY_FILENAME);
return 0;
}
/**
* Deinitialization function for STA Policy.
* This function deallocates memory/resources allocated
*/
void stapolicy_interface_deinit(struct hostapd_iface *iface)
{
struct per_interface_config *i_cfg = iface->i_cfg;
if (!i_cfg)
return;
/* Delete STA policy, list */
sta_policy_node_list_free(&i_cfg->l_sta_policy);
os_free(i_cfg->cfg_file);
os_free(i_cfg);
iface->i_cfg = NULL;
}
#endif /* CONFIG_STA_POLICY */