#include <signal.h>
#include <getopt.h>
#include <ethernet.h>
#include <bcmeth.h>
#include <bcmevent.h>
#include <802.11.h>
#include <bcmiov.h>
#include "qm_utils.h"
#include "qm_mscs.h"
#ifdef BCM_QOSMGMT_MULTIAP
#include "qm_mesh.h"
#endif /* BCM_QOSMGMT_MULTIAP */
#define MSCS_COMMAND_LEN (SYS_CMD_LEN + 16)
/* Assume by default AC_VO and AC_VI needs mscs processing. Corresponding up bitmap is 0xF0 */
#define QOSMGMT_DEFAULT_UP_BITMAP 0xF0
#define QOSMGMT_DEFAULT_UP_LIMIT 7
static bool qosmgmt_is_mscs_disallowed(qosmgmt_t *qosmgmt, const uint8 *da);
static void
qosmgmt_mscs_reset_kernel()
{
char command[SYS_CMD_LEN];
ASSERT(qm_sta_list.mscs_sta_count == 0);
if (qm_sta_list.scs_sta_count_tclas == 0) {
if (netfilter_module) {
snprintf(command, SYS_CMD_LEN, "rmmod br_netfilter.ko &> /dev/null");
SYSTEM_CALL(command);
ASSERT(netfilter_loaded == TRUE);
netfilter_loaded = FALSE;
}
iptables_deinit();
}
}
static void
qosmgmt_mscs_init_kernel()
{
char command[SYS_CMD_LEN];
ASSERT(qm_sta_list.mscs_sta_count == 0);
if (qm_sta_list.scs_sta_count_tclas == 0) {
if (netfilter_module) {
/* Insert br_nfilter module and set global mscs iptables rule */
snprintf(command, SYS_CMD_LEN, "insmod /lib/modules/$(uname -r)/"
"kernel/net/bridge/br_netfilter.ko &> /dev/null");
SYSTEM_CALL(command);
ASSERT(netfilter_loaded == FALSE);
netfilter_loaded = TRUE;
}
iptables_init();
}
}
static int
qosmgmt_delete_mscs_stalist(qosmgmt_t *qosmgmt, char *ifname, const uint8 *da)
{
actframe_entry_t *entry = NULL;
actframe_entry_t *next = NULL;
entry = qosmgmt->head;
while (entry) {
next = entry->next;
if ((entry->type == QOSMGMT_MSCS) &&
(strncmp(entry->ifname, ifname, IFNAMSIZ) == 0) &&
(!eacmp(entry->stamac.octet, da))) {
/* delete this entry */
qosmgmt_delete_actframe_entry(qosmgmt, ifname,
(char *)&entry->stamac, entry->type);
/* only one mscs entry per sta */
break;
}
entry = next;
}
return BCME_OK;
}
static int
qosmgmt_update_mscs_response_actframe(qosmgmt_t *qosmgmt, actframe_entry_t *entry,
dot11_mscs_req_t *req_act_fr)
{
dot11_mscs_descr_ie_t *mscs_descr_ie = NULL;
dot11_mscs_res_t *mscs_res = NULL;
char *ioctl_buf = NULL;
wl_af_params_t *af_params;
wl_action_frame_t *action_frame;
int buflen;
if (entry == NULL) {
QOSMGMT_ERROR("Error: entry is NULL\n");
return BCME_ERROR;
}
ASSERT(entry->actframe_buflen >= WLC_IOCTL_SMLEN);
ioctl_buf = (char *)entry->actframe_buf;
QOSMGMT_INFO("Action Frame cat: %d rav: %d, dia: %d\n",
req_act_fr->category, req_act_fr->robust_action,
req_act_fr->dialog_token);
mscs_descr_ie = &req_act_fr->mscs_descr;
/* strncpy will pad ioctl_buf with 0 after "actframe" until
* WLC_IOCTL_SMLEN, so no need to memset with 0 and fill last
* chat to '\0'.
*/
strncpy(ioctl_buf, "actframe", WLC_IOCTL_SMLEN);
buflen = strlen(ioctl_buf) + 1;
af_params = (wl_af_params_t *)(ioctl_buf + buflen);
action_frame = &af_params->action_frame;
af_params->channel = 0;
af_params->dwell_time = htod32(-1);
eacopy(entry->stamac.octet, action_frame->da.octet);
action_frame->packetId = entry->packetId;
action_frame->len = htod16(DOT11_MSCS_RES_HDR_LEN);
mscs_res = (dot11_mscs_res_t *)&action_frame->data[0];
mscs_res->category = DOT11_ACTION_CAT_QOSMGMT;
mscs_res->robust_action = DOT11_QOSMGMT_MSCS_RES;
mscs_res->dialog_token = req_act_fr->dialog_token;
if (qosmgmt_is_mscs_disallowed(qosmgmt, entry->stamac.octet)) {
mscs_res->status = DOT11_SC_DECLINED;
} else if ((mscs_descr_ie->req_type == DOT11_MSCS_REQ_TYPE_ADD) ||
(mscs_descr_ie->req_type == DOT11_MSCS_REQ_TYPE_CHANGE)) {
mscs_res->status = DOT11_SC_SUCCESS;
} else {
/* WFA #4.4.3.5 -
* when AP receives MSCS Request with Request Type subfield as 1 (REMOVE)
* MSCS Response should go with Status Code TCLAS_PROCESSING_TERMINATED (97)
*/
mscs_res->status = DOT11_SC_TCLAS_PROCESSING_TERMINATED;
}
return mscs_res->status;
}
static int
qosmgmt_update_mscs_terminate_actframe(qosmgmt_t *qosmgmt, actframe_entry_t *entry)
{
dot11_mscs_res_t *mscs_res = NULL;
char *ioctl_buf = NULL;
wl_af_params_t *af_params;
wl_action_frame_t *action_frame;
int buflen;
if (entry == NULL) {
QOSMGMT_ERROR("%s:Error: entry is NULL\n", __FUNCTION__);
return BCME_ERROR;
}
ASSERT(entry->actframe_buflen >= WLC_IOCTL_SMLEN);
ioctl_buf = (char *)entry->actframe_buf;
strncpy(ioctl_buf, "actframe", WLC_IOCTL_SMLEN);
buflen = strlen(ioctl_buf) + 1;
af_params = (wl_af_params_t *)(ioctl_buf + buflen);
action_frame = &af_params->action_frame;
af_params->channel = 0;
af_params->dwell_time = htod32(-1);
eacopy(entry->stamac.octet, action_frame->da.octet);
action_frame->packetId = entry->packetId;
action_frame->len = htod16(DOT11_MSCS_RES_HDR_LEN);
/* WFA #4.4.2.9 - Trigger APUT to delete (teardown) MSCS for STA2 */
mscs_res = (dot11_mscs_res_t *)&action_frame->data[0];
mscs_res->category = DOT11_ACTION_CAT_QOSMGMT;
mscs_res->robust_action = DOT11_QOSMGMT_MSCS_RES;
mscs_res->dialog_token = 0;
mscs_res->status = DOT11_SC_TCLAS_PROCESSING_TERMINATED;
return BCME_OK;
}
static int
qosmgmt_update_mscs_descr_ie(qosmgmt_t *qosmgmt, qm_sta_entry_t *sta_e,
void *data, uint32 datalen, uint32 req_type)
{
#ifdef BCM_QOSMGMT_MULTIAP
uint8 *descr_ie = NULL;
if (!I5_IS_MULTIAP_CONTROLLER(qosmgmt->multiap_mode)) {
return BCME_OK;
}
if (req_type == DOT11_MSCS_REQ_TYPE_ADD) {
ASSERT(data && datalen);
descr_ie = (uint8 *)calloc(1, datalen);
if (descr_ie == NULL) {
QOSMGMT_ERROR("can't allocate memory\n");
return BCME_NOMEM;
}
memcpy(descr_ie, data, datalen);
sta_e->mscs_param.mscs_ie.descr_ie = descr_ie;
sta_e->mscs_param.mscs_ie.descr_len = datalen;
} else if (req_type == DOT11_MSCS_REQ_TYPE_REMOVE) {
if (sta_e->mscs_param.mscs_ie.descr_ie) {
free(sta_e->mscs_param.mscs_ie.descr_ie);
sta_e->mscs_param.mscs_ie.descr_ie = NULL;
sta_e->mscs_param.mscs_ie.descr_len = 0;
}
} else {
ASSERT(FALSE);
}
#endif /* BCM_QOSMGMT_MULTIAP */
return BCME_OK;
}
int
qosmgmt_iptables_mscs_cmd(qosmgmt_t *qosmgmt, qm_sta_entry_t *e, iptables_entry_type_t type)
{
char macstr[ETHER_ADDR_STR_LEN];
char command[MSCS_COMMAND_LEN];
char cmd_postfix[SYS_CMD_LEN];
int ret = BCME_OK;
ether_etoa((uint8 *)&e->ea, macstr);
if (type == IPTABLES_ADD) {
if (qm_sta_list.mscs_sta_count == 0) {
/* This will be first enrty. Initialize kernel and netfilter */
qosmgmt_mscs_init_kernel();
}
}
snprintf(cmd_postfix, SYS_CMD_LEN, "-t mangle %s MSCS-RULES -m mac --mac-source %s -m mscs"
" --up-bitmap 0x%x --up-limit %d -j RETURN", type == IPTABLES_ADD ? "-A":"-D",
macstr, e->mscs_param.up_bitmap, e->mscs_param.up_limit);
snprintf(command, MSCS_COMMAND_LEN, "iptables %s", cmd_postfix);
SYSTEM_CALL(command);
snprintf(command, MSCS_COMMAND_LEN, "ip6tables %s", cmd_postfix);
SYSTEM_CALL(command);
if (type == IPTABLES_DEL) {
/* delete stored mscs_desc_ie */
qosmgmt_update_mscs_descr_ie(qosmgmt, e, NULL, 0, DOT11_MSCS_REQ_TYPE_REMOVE);
if (qm_sta_list.mscs_sta_count == 0) {
/* Left last MSCS station. Return to default kernel mode */
qosmgmt_mscs_reset_kernel();
}
if (ASR_ENAB(e->sta_cap)) {
if (qosmgmt_mscs_external_add(qosmgmt, e)) {
/* A replacement entry is added by asr auto mscs */
return BCME_OK;
}
}
}
if (COMMAND_VALID("fc") == TRUE) {
/* Evict all existing flows of the station */
snprintf(command, MSCS_COMMAND_LEN, "fc flush --mac %s &> /dev/null", macstr);
SYSTEM_CALL(command);
} else if (DIRECTORY_VALID("/proc/driver/fpi_blog")) {
/* flush all FPI flows */
snprintf(command, MSCS_COMMAND_LEN, "echo 1 > /proc/driver/fpi_blog/flush");
SYSTEM_CALL(command);
}
return ret;
}
bool
qosmgmt_mscs_external_add(qosmgmt_t *qosmgmt, qm_sta_entry_t *e)
{
uint32 qosmgmt_enable;
/* MSCS entry for the station will be necessary on following conditions:
* 1. If the AP's qosmgmt module is enabled for MSCS and ASR_AUTO_MSCS
* 2. If the station does not have already a MSCS entry which may have been
* from ASR session or from a MSCS request
*/
if (MSCS_ENAB(e->sta_cap)) {
/* MSCS is already active in this station. */
return FALSE;
}
qosmgmt_enable = qosmgmt_get_iface_enable(qosmgmt, e->ifname);
if (!(MSCS_ENAB(qosmgmt_enable) && ASR_AUTO_MSCS_ENAB(qosmgmt_enable))) {
/* MSCS is not enabled. OR it is enabled but auto mscs is not enabled */
return FALSE;
}
e->sta_cap |= WL_QOSMGMT_ASR_AUTO_MSCS;
e->sta_cap |= WL_QOSMGMT_MSCS;
e->mscs_param.up_bitmap = QOSMGMT_DEFAULT_UP_BITMAP;
e->mscs_param.up_limit = QOSMGMT_DEFAULT_UP_LIMIT;
e->mscs_param.stream_timeout = -1; /* Not used */
e->mscs_param.external_request = TRUE;
qosmgmt_iptables_mscs_cmd(qosmgmt, e, IPTABLES_ADD);
qm_sta_list.mscs_sta_count++;
return TRUE;
}
bool
qosmgmt_mscs_external_entry_delete(qosmgmt_t *qosmgmt, qm_sta_entry_t *e)
{
if (!MSCS_ENAB(e->sta_cap)) {
return FALSE;
}
if (!e->mscs_param.external_request) {
/* This entry is from MSCS request. Don't delete it */
return FALSE;
}
/* clear MSCS cap */
e->sta_cap &= (~WL_QOSMGMT_MSCS);
e->sta_cap &= (~WL_QOSMGMT_ASR_AUTO_MSCS);
e->mscs_param.external_request = FALSE;
qm_sta_list.mscs_sta_count--;
/* Delete associated iptables entry */
qosmgmt_iptables_mscs_cmd(qosmgmt, e, IPTABLES_DEL);
return TRUE;
}
int
qosmgmt_process_mscs_ie(qosmgmt_t *qosmgmt, char *ifname, void *data,
uint32 datalen, const uint8 *da)
{
dot11_mscs_descr_ie_t *mscs_descr_ie;
uint8 up_bitmap, up_limit;
qm_sta_entry_t *e;
char macstr[ETHER_ADDR_STR_LEN];
if (datalen < sizeof(*mscs_descr_ie)) {
return BCME_BADARG;
}
mscs_descr_ie = (dot11_mscs_descr_ie_t *)data;
ether_etoa(da, macstr);
up_bitmap = (mscs_descr_ie->up_ctl & DOT11_UPC_UP_BITMAP_MASK) >> DOT11_UPC_UP_BITMAP_SHIFT;
up_limit = (mscs_descr_ie->up_ctl & DOT11_UPC_UP_LIMIT_MASK) >> DOT11_UPC_UP_LIMIT_SHIFT;
QOSMGMT_INFO("MSCS IE [%s] type: %d, up_bitmap: 0x%x, up_limit: %d timeout: %d\n", macstr,
mscs_descr_ie->req_type, up_bitmap, up_limit, mscs_descr_ie->stream_timeout);
if (mscs_descr_ie->req_type > DOT11_MSCS_REQ_TYPE_CHANGE) {
QOSMGMT_ERROR("Error: Invalid request type : %d from %s\n",
mscs_descr_ie->req_type, macstr);
return BCME_ERROR;
}
e = qosmgmt_get_sta_entry(da, ifname, TRUE);
if (e == NULL) {
QOSMGMT_ERROR("Out of memory.\n");
return BCME_NOMEM;
}
if (mscs_descr_ie->req_type == DOT11_MSCS_REQ_TYPE_REMOVE) {
if (MSCS_ENAB(e->sta_cap)) {
/* clear MSCS cap */
e->sta_cap &= (~WL_QOSMGMT_MSCS);
qm_sta_list.mscs_sta_count--;
/* Delete associated iptables entry */
qosmgmt_iptables_mscs_cmd(qosmgmt, e, IPTABLES_DEL);
}
} else {
if (MSCS_ENAB(e->sta_cap)) {
/* Delete associated iptable entry */
qosmgmt_iptables_mscs_cmd(qosmgmt, e, IPTABLES_DEL);
}
/* replace param */
e->mscs_param.up_bitmap = up_bitmap;
e->mscs_param.up_limit = up_limit;
e->mscs_param.stream_timeout = mscs_descr_ie->stream_timeout;
e->mscs_param.external_request = FALSE; /* This entry is from a MSCS action frame */
e->sta_cap &= (~WL_QOSMGMT_ASR_AUTO_MSCS);
qosmgmt_update_mscs_descr_ie(qosmgmt, e, data, datalen, DOT11_MSCS_REQ_TYPE_ADD);
/* Add new iptables entry */
qosmgmt_iptables_mscs_cmd(qosmgmt, e, IPTABLES_ADD);
if (!MSCS_ENAB(e->sta_cap)) {
e->sta_cap |= WL_QOSMGMT_MSCS;
qm_sta_list.mscs_sta_count++;
}
}
/* Free station entry if sta_cap is zero */
qosmgmt_free_sta_entry(e->ea.octet);
return BCME_OK;
}
static int
qosmgmt_mscs_iptables_delete(qosmgmt_t *qosmgmt, const uint8 *da)
{
qm_sta_entry_t *e;
char macstr[ETHER_ADDR_STR_LEN];
ether_etoa(da, macstr);
e = qosmgmt_get_sta_entry(da, NULL, FALSE);
if (e == NULL) {
/* No entry for this station */
QOSMGMT_INFO("No entry for STA: %s\n", macstr);
return BCME_OK;
}
if (!MSCS_ENAB(e->sta_cap)) {
QOSMGMT_INFO("STA : %s is not MSCS capable\n", macstr);
return BCME_OK;
}
/* clear MSCS cap */
e->sta_cap &= (~WL_QOSMGMT_MSCS);
qm_sta_list.mscs_sta_count--;
/* Delete associated iptables entry */
qosmgmt_iptables_mscs_cmd(qosmgmt, e, IPTABLES_DEL);
/* Free station entry if sta_cap is zero */
qosmgmt_free_sta_entry(e->ea.octet);
return BCME_OK;
}
int
qosmgmt_process_mscs_request(qosmgmt_t *qosmgmt, char *ifname, void *data,
uint32 datalen, const uint8 *da)
{
dot11_mscs_req_t *req_act_fr;
dot11_mscs_descr_ie_t *mscs_descr_ie;
actframe_entry_t *entry;
int ret = BCME_OK;
if (datalen < sizeof(*req_act_fr)) {
return BCME_BADARG;
}
req_act_fr = (dot11_mscs_req_t *)data;
mscs_descr_ie = &req_act_fr->mscs_descr;
datalen -= OFFSETOF(dot11_mscs_req_t, mscs_descr);
/* delete any pending MSCS resp actframe */
qosmgmt_delete_mscs_stalist(qosmgmt, ifname, da);
/* allocate entry for MSCS resp actframe */
entry = qosmgmt_add_actframe_entry(qosmgmt, ifname, da, QOSMGMT_MSCS);
/* update MSCS resp actframe buffer for each sta */
ret = qosmgmt_update_mscs_response_actframe(qosmgmt, entry, req_act_fr);
if (ret != DOT11_SC_DECLINED) {
/* add mscs iptables rules */
qosmgmt_process_mscs_ie(qosmgmt, ifname, mscs_descr_ie, datalen, da);
#ifdef BCM_QOSMGMT_MULTIAP
qosmgmt_send_wbd_ipc_mscs_success(qosmgmt, mscs_descr_ie, datalen, (uchar *)da);
#endif /* BCM_QOSMGMT_MULTIAP */
}
/* dump all sta in the list of type MSCS */
qosmgmt_dump_actframe_list(qosmgmt, ifname, QOSMGMT_MSCS);
/* transmit MSCS resp actframe */
qosmgmt_send_actionframe_entry(qosmgmt, entry);
return BCME_OK;
}
int
qosmgmt_process_mscs_assoc(qosmgmt_t *qosmgmt, char *ifname, void *data,
uint32 datalen, const uint8 *da)
{
if (qosmgmt_is_mscs_disallowed(qosmgmt, da)) {
actframe_entry_t *entry;
/* delete any pending MSCS actframe */
qosmgmt_delete_mscs_stalist(qosmgmt, ifname, da);
/* allocate entry for MSCS terminate actframe */
entry = qosmgmt_add_actframe_entry(qosmgmt, ifname, da, QOSMGMT_MSCS);
/* update MSCS terminate actframe buffer for sta */
qosmgmt_update_mscs_terminate_actframe(qosmgmt, entry);
/* dump all sta in the list of type MSCS */
qosmgmt_dump_actframe_list(qosmgmt, ifname, QOSMGMT_MSCS);
/* transmit MSCS terminate actframe */
qosmgmt_send_actionframe_entry(qosmgmt, entry);
} else {
/* add mscs iptables rules */
qosmgmt_process_mscs_ie(qosmgmt, ifname, data, datalen, da);
#ifdef BCM_QOSMGMT_MULTIAP
qosmgmt_send_wbd_ipc_mscs_success(qosmgmt, data, datalen, (uchar *)da);
#endif /* BCM_QOSMGMT_MULTIAP */
}
return BCME_OK;
}
int
qosmgmt_process_mscs_terminate(qosmgmt_t *qosmgmt, char *ifname, const uint8 *da)
{
actframe_entry_t *entry;
/* delete any pending MSCS actframe */
qosmgmt_delete_mscs_stalist(qosmgmt, ifname, da);
/* allocate entry for MSCS terminate actframe */
entry = qosmgmt_add_actframe_entry(qosmgmt, ifname, da, QOSMGMT_MSCS);
/* update MSCS terminate actframe buffer for sta */
qosmgmt_update_mscs_terminate_actframe(qosmgmt, entry);
/* delete mscs iptables rules */
qosmgmt_mscs_iptables_delete(qosmgmt, da);
/* dump all sta in the list of type MSCS */
qosmgmt_dump_actframe_list(qosmgmt, ifname, QOSMGMT_MSCS);
/* transmit MSCS terminate actframe */
qosmgmt_send_actionframe_entry(qosmgmt, entry);
return BCME_OK;
}
#ifdef RESTORE_STA_MSCS
static int
qosmgmt_restore_sta_mscs_ie(qosmgmt_t *qosmgmt, char *ifname, const uint8 *mac)
{
int ret = BCME_OK;
bcm_iov_buf_t *iov_buf = NULL;
bcm_iov_buf_t *p_resp = NULL;
uint8 *pxtlv = NULL;
uint16 buflen = 0, buflen_start = 0;
uint16 iovlen = 0;
uint8 *iov_resp = NULL;
int sbuflen;
int hdr_size;
iov_buf = (bcm_iov_buf_t *)calloc(1, WLC_IOCTL_MEDLEN);
if (iov_buf == NULL) {
return BCME_NOMEM;
}
iov_resp = (uint8 *)calloc(1, WLC_IOCTL_MAXLEN);
if (iov_resp == NULL) {
ret = BCME_NOMEM;
goto fail;
}
/* fill header */
iov_buf->version = WL_QOSMGMT_IOV_VERSION;
iov_buf->id = WL_QOSMGMT_CMD_DESC_IE;
pxtlv = (uint8 *)&iov_buf->data[0];
buflen = buflen_start = WLC_IOCTL_MEDLEN - sizeof(bcm_iov_buf_t);
ret = bcm_pack_xtlv_entry(&pxtlv, &buflen, WL_QOSMGMT_XTLV_MACADDR,
ETHER_ADDR_LEN, (uint8*)mac, BCM_XTLV_OPTION_ALIGN32);
if (ret != BCME_OK) {
goto fail;
}
iov_buf->len = buflen_start - buflen;
iovlen = sizeof(bcm_iov_buf_t) + iov_buf->len;
ret = wl_iovar_getbuf(ifname, "qosmgmt", iov_buf, iovlen, iov_resp, WLC_IOCTL_MAXLEN);
if (ret != BCME_OK) {
goto fail;
}
p_resp = (bcm_iov_buf_t *)iov_resp;
if (p_resp->id != WL_QOSMGMT_CMD_DESC_IE) {
goto fail;
}
pxtlv = (uint8 *)p_resp->data;
sbuflen = p_resp->len;
hdr_size = BCM_XTLV_HDR_SIZE_EX(BCM_XTLV_OPTION_ALIGN32);
while (sbuflen >= hdr_size) {
uint16 type, len;
int size;
const bcm_xtlv_t *ptlv;
const uint8 *data;
ptlv = (const bcm_xtlv_t *)pxtlv;
bcm_xtlv_unpack_xtlv(ptlv, &type, &len, &data, BCM_XTLV_OPTION_ALIGN32);
size = bcm_xtlv_size_for_data(len, BCM_XTLV_OPTION_ALIGN32);
sbuflen -= size;
if (sbuflen < 0) /* check for buffer overrun */
break;
if (type == WL_QOSMGMT_XTLV_MSCS_IE) {
/* update iptables based on mscs desc ie */
qosmgmt_process_mscs_ie(qosmgmt, ifname,
data, len, mac);
} else if (type == WL_QOSMGMT_XTLV_SCS_IE) {
/* TODO: update iptables based on scs desc ie */
}
pxtlv += size;
}
fail:
if (iov_buf) {
free(iov_buf);
}
if (iov_resp) {
free(iov_resp);
}
return ret;
}
int
qosmgmt_restore_mscs_ie(qosmgmt_t *qosmgmt, char *ifname)
{
int ret = BCME_OK;
bcm_iov_buf_t *iov_buf = NULL;
bcm_iov_buf_t *p_resp = NULL;
uint8 *pxtlv = NULL;
uint16 buflen = 0, buflen_start = 0;
uint16 iovlen = 0;
uint8 stacap = 0;
uint8 *iov_resp = NULL;
int sbuflen;
int hdr_size;
iov_buf = (bcm_iov_buf_t *)calloc(1, WLC_IOCTL_MEDLEN);
if (iov_buf == NULL) {
return BCME_NOMEM;
}
iov_resp = (uint8 *)calloc(1, WLC_IOCTL_MAXLEN);
if (iov_resp == NULL) {
ret = BCME_NOMEM;
goto fail;
}
/* fill header */
iov_buf->version = WL_QOSMGMT_IOV_VERSION;
iov_buf->id = WL_QOSMGMT_CMD_STALIST;
pxtlv = (uint8 *)&iov_buf->data[0];
buflen = buflen_start = WLC_IOCTL_MEDLEN - sizeof(bcm_iov_buf_t);
stacap = bcm_find_fsb(WL_QOSMGMT_MSCS) - 1;
ret = bcm_pack_xtlv_entry(&pxtlv, &buflen, WL_QOSMGMT_XTLV_STACAP,
sizeof(stacap), &stacap, BCM_XTLV_OPTION_ALIGN32);
if (ret != BCME_OK) {
goto fail;
}
iov_buf->len = buflen_start - buflen;
iovlen = sizeof(bcm_iov_buf_t) + iov_buf->len;
ret = wl_iovar_getbuf(ifname, "qosmgmt", iov_buf, iovlen, iov_resp, WLC_IOCTL_MAXLEN);
if (ret != BCME_OK) {
goto fail;
}
p_resp = (bcm_iov_buf_t *)iov_resp;
if (p_resp->id != WL_QOSMGMT_CMD_STALIST) {
goto fail;
}
pxtlv = (uint8 *)p_resp->data;
sbuflen = p_resp->len;
hdr_size = BCM_XTLV_HDR_SIZE_EX(BCM_XTLV_OPTION_ALIGN32);
while (sbuflen >= hdr_size) {
uint16 type, len;
int size;
const bcm_xtlv_t *ptlv;
const uint8 *data;
ptlv = (const bcm_xtlv_t *)pxtlv;
bcm_xtlv_unpack_xtlv(ptlv, &type, &len, &data, BCM_XTLV_OPTION_ALIGN32);
size = bcm_xtlv_size_for_data(len, BCM_XTLV_OPTION_ALIGN32);
sbuflen -= size;
if (sbuflen < 0) /* check for buffer overrun */
break;
/* restore sta iptables */
qosmgmt_restore_sta_mscs_ie(qosmgmt, ifname, data);
pxtlv += size;
}
fail:
if (iov_buf) {
free(iov_buf);
}
if (iov_resp) {
free(iov_resp);
}
return ret;
}
#endif /* RESTORE_STA_MSCS */
void
qosmgmt_clear_mscs_iptables_all_entries(qosmgmt_t *qosmgmt)
{
int i;
qm_sta_entry_ll_t *sta_ll;
qm_sta_entry_t *e;
if (qm_sta_list.mscs_sta_count == 0) {
/* Nothing to clear */
return;
}
/* Delete all mscs iptables entries created by this app. */
QOSMGMT_INFO("Delete all MSCS iptables entries\n");
for (i = 0; i < QM_HASH_TABLE_SIZE; i++) {
sta_ll = qm_sta_list.hash_table[i];
while (sta_ll != NULL) {
e = sta_ll->sta_e;
if (MSCS_ENAB(e->sta_cap)) {
/* clear MSCS cap */
e->sta_cap &= (~WL_QOSMGMT_MSCS);
qm_sta_list.mscs_sta_count--;
/* Delete MSCS iptables entry */
qosmgmt_iptables_mscs_cmd(qosmgmt, e, IPTABLES_DEL);
}
/* Free station entry if sta_cap is zero */
sta_ll = qosmgmt_free_sta_entry(e->ea.octet);
}
}
if (qm_sta_list.mscs_sta_count != 0) {
QOSMGMT_ERROR("MSCS Station list is corrupted\n");
ASSERT(0);
}
}
static bool
qosmgmt_is_mscs_disallowed(qosmgmt_t *qosmgmt, const uint8 *da)
{
bool ret = FALSE;
char macaddr[ETHER_ADDR_STR_LEN], *macnext;
struct ether_addr mac_addr;
if (qosmgmt->mscs_disallowed_list == NULL) {
return ret;
}
foreach(macaddr, qosmgmt->mscs_disallowed_list, macnext) {
if (!ether_atoe(macaddr, (uchar *)&mac_addr)) {
QOSMGMT_ERROR("Invalid mac address (%s)\n", macaddr);
continue;
}
if (!eacmp(mac_addr.octet, da)) {
QOSMGMT_INFO("MSCS is disallowed for STA:"MACF"\n",
ETHER_TO_MACF(*(struct ether_addr *)da));
ret = TRUE;
break;
}
}
return ret;
}解析一下这段代码
最新发布