diff --git a/src/controller.conf b/src/controller.conf index 1cb46cfb8c0af03c1d6c4ab8e82b9d0277652059..e34cb611d0acd13770af031c34d48c64c22f62b5 100644 --- a/src/controller.conf +++ b/src/controller.conf @@ -48,6 +48,7 @@ config agent-policy option rcpi_threshold '30' # 0 - 220 valid range option report_scan '0' # 0 or 1 for independent scans option report_sta_assocfails '1' # 0 or 1 - stas assoc failure + option report_sta_assocfails_rate '2' # reporting rate for STA assoc fails (attempts per minute) option report_metric_periodic '0' # 0, or 1 - 255 in secs option report_rcpi_threshold '0' # 0, or 1 - 220 option report_util_threshold '0' # 0, or channel-util value @@ -67,6 +68,7 @@ config agent-policy option rcpi_threshold '30' # 0 - 220 valid range option report_scan '0' # 0 or 1 for independent scans option report_sta_assocfails '1' # 0 or 1 - stas assoc failure + option report_sta_assocfails_rate '2' # reporting rate for STA assoc fails (attempts per minute) option report_metric_periodic '0' # 0, or 1 - 255 in secs option report_rcpi_threshold '0' # 0, or 1 - 220 option report_util_threshold '0' # 0, or channel-util value diff --git a/src/core/cntlr_tlv_generator.c b/src/core/cntlr_tlv_generator.c index 7d6cfab0e79f0ee835e0a2296c3ef3da6e7ca9d8..f5a9f9f64c8f5e67b70f4af9dd80f2694c6f347e 100644 --- a/src/core/cntlr_tlv_generator.c +++ b/src/core/cntlr_tlv_generator.c @@ -261,3 +261,161 @@ struct tlv_map_profile *cntlr_gen_map_profile(struct controller *c, return p; } + +struct tlv_ch_scan_rep_policy *cntlr_gen_ch_scan_rep_policy( + struct controller *c, struct agent_policy *a, + struct cmdu_cstruct *cmdu) +{ + struct tlv_ch_scan_rep_policy *p; + + p = calloc(1, sizeof(struct tlv_ch_scan_rep_policy)); + if (!p) + return NULL; + + p->tlv_type = MAP_TLV_CHANNEL_SCAN_REPORTING_POLICY; + p->ch_scans = a->report_scan; + + return p; +} + +struct tlv_unsuccess_assoc_policy *cntlr_gen_unsuccess_assoc_policy( + struct controller *c, struct agent_policy *a, + struct cmdu_cstruct *cmdu) +{ + struct tlv_unsuccess_assoc_policy *p; + + p = calloc(1, sizeof(struct tlv_unsuccess_assoc_policy)); + if (!p) + return NULL; + + p->tlv_type = MAP_TLV_UNSUCCESS_ASSOCIATION_POLICY; + p->report = a->report_sta_assocfails; + p->max_reporting_rate = a->report_sta_assocfails_rate; + + return p; +} + +struct tlv_backhaul_bss_config *cntlr_gen_backhaul_bss_config( + struct controller *c, struct agent_policy *a, + struct cmdu_cstruct *cmdu, const uint8_t *bssid) +{ + struct tlv_backhaul_bss_config *p; + + p = calloc(1, sizeof(struct tlv_backhaul_bss_config)); + if (!p) + return NULL; + + p->tlv_type = MAP_TLV_BACKHAUL_BSS_CONFIG; + memcpy(p->bssid, bssid, 6); + p->p1 = a->disallow_bsta_p1; + p->p2 = a->disallow_bsta_p2; + + return p; +} + +struct tlv_steering_policy *cntlr_gen_steering_policy( + struct controller *c, struct agent_policy *a, + struct cmdu_cstruct *cmdu, int num_radio, uint8_t *radiolist) +{ + int i = 0, index; + struct tlv_steering_policy *p; + struct stax *x; + uint8_t sta_mac[6] = {0}; + + p = calloc(1, sizeof(struct tlv_steering_policy)); + if (!p) + return NULL; + + p->tlv_type = MAP_TLV_STEERING_POLICY; + + p->local_disallowed_sta_nr = 0; + list_for_each_entry(x, &a->steer_exlist, list) { + p->local_disallowed_sta_nr++; + index = p->local_disallowed_sta_nr - 1; + p->local_disallowed_sta_macs = realloc(p->local_disallowed_sta_macs, + p->local_disallowed_sta_nr * sizeof(*p->local_disallowed_sta_macs)); + if (!p->local_disallowed_sta_macs) + goto fail_alloc; + + hwaddr_aton(x->macstring, sta_mac); + memcpy(p->local_disallowed_sta_macs[index].addr, sta_mac, 6); + } + + p->btm_disallowed_sta_nr = 0; + list_for_each_entry(x, &a->btmsteer_exlist, list) { + p->btm_disallowed_sta_nr++; + index = p->btm_disallowed_sta_nr - 1; + p->btm_disallowed_sta_macs = realloc(p->local_disallowed_sta_macs, + p->btm_disallowed_sta_nr * sizeof(*p->local_disallowed_sta_macs)); + if (!p->btm_disallowed_sta_macs) + goto fail_alloc; + + hwaddr_aton(x->macstring, sta_mac); + memcpy(p->local_disallowed_sta_macs[index].addr, sta_mac, 6); + } + + p->control_policy_radio_nr = num_radio; + p->control_policy = calloc(p->control_policy_radio_nr, + sizeof(*p->control_policy)); + if (!p->control_policy) + goto fail_alloc; + + for (i = 0; i < p->control_policy_radio_nr; i++) { + memcpy(p->control_policy[i].radio_id, &radiolist[i*6], 6); + p->control_policy[i].steering_policy = a->policy; + p->control_policy[i].channel_utilization_thres = + a->util_threshold; + p->control_policy[i].rcpi_steering_thres = + a->rcpi_threshold; + } + + return p; + +fail_alloc: + if (p->local_disallowed_sta_macs) + free(p->local_disallowed_sta_macs); + if (p->btm_disallowed_sta_macs) + free(p->btm_disallowed_sta_macs); + + free(p); + + return NULL; +} + +struct tlv_metric_report_policy *cntlr_gen_metric_report_policy( + struct controller *c, struct agent_policy *a, + struct cmdu_cstruct *cmdu, int num_radio, uint8_t *radiolist) +{ + int i = 0; + struct tlv_metric_report_policy *p; + + p = calloc(1, sizeof(struct tlv_metric_report_policy)); + if (!p) + return NULL; + + p->tlv_type = MAP_TLV_METRIC_REPORTING_POLICY; + p->interval = a->report_metric_periodic; + p->metric_reporting_policy_radio_nr = num_radio; + p->metric_reporting_policy = calloc(p->metric_reporting_policy_radio_nr, + sizeof(*p->metric_reporting_policy)); + + if (!p->metric_reporting_policy) { + free(p); + return NULL; + } + + for (i = 0; i < p->metric_reporting_policy_radio_nr; i++) { + memcpy(p->metric_reporting_policy[i].radio_id, &radiolist[i*6], 6); + p->metric_reporting_policy[i].rcpi_thres = + a->report_rcpi_threshold; + p->metric_reporting_policy[i].rcpi_hysteresis_margin; //FIXME + p->metric_reporting_policy[i].channel_utilization_thres = + a->report_util_threshold; + p->metric_reporting_policy[i].is_assoc_sta_traffic_stats = + a->include_sta_stats; + p->metric_reporting_policy[i].is_assoc_sta_link_metrics = + a->include_sta_metric; + } + + return p; +} diff --git a/src/core/cntlr_tlv_generator.h b/src/core/cntlr_tlv_generator.h index 399f92bc4334cccb72068480aae0efb98e48970b..042fc98842c84db4fe2a9854e1acf7541f854ecc 100644 --- a/src/core/cntlr_tlv_generator.h +++ b/src/core/cntlr_tlv_generator.h @@ -23,4 +23,18 @@ struct tlv_supp_service *cntlr_gen_supp_service(struct controller *c, struct cmdu_cstruct *cmdu); struct tlv_map_profile *cntlr_gen_map_profile(struct controller *c, struct cmdu_cstruct *cmdu); +struct tlv_steering_policy *cntlr_gen_steering_policy(struct controller *c, + struct agent_policy *a, struct cmdu_cstruct *cmdu, + int num_radio, uint8_t *radiolist); +struct tlv_metric_report_policy *cntlr_gen_metric_report_policy(struct controller *c, + struct agent_policy *a, struct cmdu_cstruct *cmdu, + int num_radio, uint8_t *radiolist); +struct tlv_ch_scan_rep_policy *cntlr_gen_ch_scan_rep_policy(struct controller *c, + struct agent_policy *a, struct cmdu_cstruct *cmdu); +struct tlv_unsuccess_assoc_policy *cntlr_gen_unsuccess_assoc_policy( + struct controller *c, struct agent_policy *a, + struct cmdu_cstruct *cmdu); +struct tlv_backhaul_bss_config *cntlr_gen_backhaul_bss_config( + struct controller *c, struct agent_policy *a, + struct cmdu_cstruct *cmdu, const uint8_t *bssid); #endif diff --git a/src/core/cntlr_ubus.c b/src/core/cntlr_ubus.c index cd4110b5ab334243a8551089c5bbec661c5b9e47..1f98a939521f7a02555a32ad111f4c9704273d00 100644 --- a/src/core/cntlr_ubus.c +++ b/src/core/cntlr_ubus.c @@ -31,6 +31,7 @@ #include "hlist.h" #include "allsta.h" #include "cntlr_ubus.h" +#include "cntlr_tlv_generator.h" #include "map_module.h" @@ -109,6 +110,19 @@ static const struct blobmsg_policy bk_steer_policy_params[__BK_STEER_POLICY_MAX] [BK_STEER_POLICY_STA_MAC] = { .name = "bksta", .type = BLOBMSG_TYPE_STRING }, }; +enum { + AP_POLICY_CONFIG_AGENT, + AP_POLICY_CONFIG_RADIOS, + AP_POLICY_CONFIG_BSS, + __AP_POLICY_CONFIG_MAX, +}; + +static const struct blobmsg_policy ap_policy_config_params[__AP_POLICY_CONFIG_MAX] = { + [AP_POLICY_CONFIG_AGENT] = { .name = "agent", .type = BLOBMSG_TYPE_STRING }, + [AP_POLICY_CONFIG_RADIOS] = { .name = "radiolist", .type = BLOBMSG_TYPE_ARRAY }, + [AP_POLICY_CONFIG_BSS] = { .name = "bsslist", .type = BLOBMSG_TYPE_ARRAY }, +}; + void send_cmdu_cb(struct ubus_request *req, int type, struct blob_attr *msg) { @@ -764,18 +778,216 @@ fail_cmdu: return UBUS_STATUS_UNKNOWN_ERROR; } +static int cntlr_ap_policy_config(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + char agent[18] = {0}; + char radio[18] = {0}; + char bss[18] = {0}; + uint8_t hwaddr[6] = {0}; + int tlv_index = 0; + struct cmdu_cstruct *cmdu_data; + struct agent_policy *a, *found = NULL; + struct blob_attr *attr = NULL; + uint8_t *radiolist = NULL; + uint8_t *bsslist = NULL; + int rem, num_radio = 0, num_bss = 0; + int i = 0; + struct blob_attr *tb[__AP_POLICY_CONFIG_MAX]; + struct controller *c = container_of(obj, struct controller, obj); + /* TLVs */ + struct tlv_steering_policy *p1 = NULL; + struct tlv_metric_report_policy *p2 = NULL; + /* Map-2 tlvs */ +// #ifdef PROFILE2 + struct tlv_default_8021q_settings *p3 = NULL; + struct tlv_traffic_sep_policy *p4 = NULL; + struct tlv_ch_scan_rep_policy *p5 = NULL; + struct tlv_unsuccess_assoc_policy *p6 = NULL; + struct tlv_backhaul_bss_config *p7 = NULL; +// #endif + + blobmsg_parse(ap_policy_config_params, __AP_POLICY_CONFIG_MAX, tb, + blob_data(msg), blob_len(msg)); + + if (!tb[AP_POLICY_CONFIG_AGENT] || !tb[AP_POLICY_CONFIG_RADIOS]) { + fprintf(stderr, "Agent policy config: provide Agent or Radio " \ + "address in format 11:22:33...\n"); + return UBUS_STATUS_INVALID_ARGUMENT; + } + + strncpy(agent, blobmsg_data(tb[AP_POLICY_CONFIG_AGENT]), sizeof(agent) - 1); + if (!hwaddr_aton(agent, hwaddr)) + return UBUS_STATUS_UNKNOWN_ERROR; + + list_for_each_entry(a, &c->cfg.policylist, list) { + if (!memcmp(hwaddr, a->agent_id, sizeof(hwaddr))) { + found = a; + break; + } + } + + if (!found) + return UBUS_STATUS_UNKNOWN_ERROR; + + /* fetch radio id's */ + blobmsg_for_each_attr(attr, tb[AP_POLICY_CONFIG_RADIOS], rem) { + uint8_t bssid[6] = {0}; + + if (blobmsg_type(attr) != BLOBMSG_TYPE_STRING) + continue; + + memset(radio, 0, sizeof(radio)); + strncpy(radio, blobmsg_data(attr), sizeof(radio)-1); + if (!hwaddr_aton(radio, bssid)) { + fprintf(stderr, "Agent policy config: provide radio " \ + "address in format 11:22:33...\n"); + if (radiolist) + free(radiolist); + + return UBUS_STATUS_UNKNOWN_ERROR; + } + + num_radio++; + radiolist = (uint8_t *)realloc(radiolist, 6 * num_radio * sizeof(uint8_t)); + memcpy(&radiolist[(num_radio-1)*6], bssid, 6); + } + + if (num_radio == 0) + return UBUS_STATUS_UNKNOWN_ERROR; + + /* fetch BSS list */ + blobmsg_for_each_attr(attr, tb[AP_POLICY_CONFIG_BSS], rem) { + uint8_t bssid[6] = {0}; + + if (blobmsg_type(attr) != BLOBMSG_TYPE_STRING) + continue; + + memset(bss, 0, sizeof(bss)); + strncpy(bss, blobmsg_data(attr), sizeof(bss)-1); + if (!hwaddr_aton(bss, bssid)) { + fprintf(stderr, "Agent policy config: provide bssid " \ + "address in format 11:22:33...\n"); + goto fail_bss_parsing; + } + + num_bss++; + bsslist = (uint8_t *)realloc(bsslist, 6 * num_bss * sizeof(uint8_t)); + memcpy(&bsslist[(num_bss-1)*6], bssid, 6); + } + + cmdu_data = (struct cmdu_cstruct *)calloc(1, sizeof(struct cmdu_cstruct)); + if (!cmdu_data) { + fprintf(stderr, "failed to malloc cmdu\n"); + goto fail_bss_parsing; + } + + cmdu_data->message_type = CMDU_POLICY_CONFIG_REQ; + memcpy(cmdu_data->origin, hwaddr, 6); + + cmdu_data->num_tlvs = 2; +// #ifdef PROFILE2 + cmdu_data->num_tlvs += 4 + num_bss; +// #endif + + cmdu_data->tlvs = (uint8_t **)calloc(cmdu_data->num_tlvs, sizeof(uint8_t *)); + if (!cmdu_data->tlvs) { + cmdu_data->num_tlvs = 0; + goto fail_cmdu; + } + + /* Steering Policy TLV */ + p1 = cntlr_gen_steering_policy(c, found, cmdu_data, num_radio, radiolist); + if (!p1) + goto fail_cmdu; + + cmdu_data->tlvs[tlv_index++] = (uint8_t *)p1; + + /* Metric Reporting Policy TLV */ + p2 = cntlr_gen_metric_report_policy(c, found, cmdu_data, num_radio, radiolist); + if (!p2) + goto fail_cmdu; + + cmdu_data->tlvs[tlv_index++] = (uint8_t *)p2; + + /* Default 802.1Q setting TLV */ +// #ifdef PROFILE2 + p3 = cntlr_gen_8021q_settings(c, NULL, cmdu_data); + if (!p3) + goto fail_cmdu; + + cmdu_data->tlvs[tlv_index++] = (uint8_t *)p3; + + /* Traffic Seperation Policy TLV */ + p4 = cntlr_gen_traffic_sep_policy(c, cmdu_data); + if (!p4) + goto fail_cmdu; + + cmdu_data->tlvs[tlv_index++] = (uint8_t *)p4; + + /* Channel Scan Reporting Policy TLV */ + p5 = cntlr_gen_ch_scan_rep_policy(c, found, cmdu_data); + if (!p5) + goto fail_cmdu; + + cmdu_data->tlvs[tlv_index++] = (uint8_t *)p5; + + /* Unsuccessful Association Policy TLV */ + p6 = cntlr_gen_unsuccess_assoc_policy(c, found, cmdu_data); + if (!p6) + goto fail_cmdu; + + cmdu_data->tlvs[tlv_index++] = (uint8_t *)p6; + + /* Backhaul BSS Configuration TLV */ + for (i = 0; i < num_bss; i++) { + uint8_t bssid[6] = {0}; + + memcpy(bssid, &bsslist[i*6], 6); + p7 = cntlr_gen_backhaul_bss_config(c, found, cmdu_data, bssid); + if (!p7) + goto fail_cmdu; + + cmdu_data->tlvs[tlv_index++] = (uint8_t *)p7; + } +// #endif + + // TODO: ff:ff:ff:ff:ff:ff = send to all agents + + send_cmdu(c, cmdu_data); + + if (bsslist) + free(bsslist); + + free(radiolist); + + return 0; + +fail_cmdu: + free(cmdu_data); +fail_bss_parsing: + if (bsslist) + free(bsslist); + + free(radiolist); + + return UBUS_STATUS_UNKNOWN_ERROR; +} + int cntlr_publish_object(struct controller *c, const char *objname) { struct ubus_object *obj; struct ubus_object_type *obj_type; struct ubus_method *obj_methods; - struct ubus_method m[5] = { + struct ubus_method m[6] = { UBUS_METHOD_NOARG("status", cntlr_status), UBUS_METHOD("ap_caps", cntlr_ap_caps, ap_caps_policy_params), UBUS_METHOD("sta_caps", cntlr_sta_caps, sta_caps_policy_params), UBUS_METHOD("channels", cntlr_channel_pref, channel_pref_policy_params), - UBUS_METHOD("bk_steer", cntlr_bk_steer, bk_steer_policy_params) + UBUS_METHOD("bk_steer", cntlr_bk_steer, bk_steer_policy_params), + UBUS_METHOD("agent_policy", cntlr_ap_policy_config, ap_policy_config_params) /* UBUS_METHOD("teardown_ap", cntlr_teardown_ap, config_policy_params), diff --git a/src/core/config.c b/src/core/config.c index bb72a65b3b619cd8b878fea94c0827cc0eb7bf2d..e6e6f82288fc5ebc9c2d0c0ff8b1b105aa5a342f 100644 --- a/src/core/config.c +++ b/src/core/config.c @@ -41,9 +41,21 @@ int verbose; +static void stax_add_entry(struct list_head *h, char *sta_macstr) +{ + struct stax *n; + + n = calloc(1, sizeof(struct stax)); + if (n) { + snprintf(n->macstring, 18, "%s", sta_macstr); + list_add(&n->list, h); + } +} + void cntlr_config_dump(struct controller_config *c) { int i; + struct stax *x = NULL; dbg("Controller config ---------\n"); dbg("Enabled: %d\n", c->enabled); @@ -75,6 +87,7 @@ void cntlr_config_dump(struct controller_config *c) dbg(" RCPI-threshold : %d\n", c->apolicy.rcpi_threshold); dbg(" Report scan : %d\n", c->apolicy.report_scan); dbg(" Report assocfails : %d\n", c->apolicy.report_sta_assocfails); + dbg(" Report assocfails rate: %d\n", c->apolicy.report_sta_assocfails_rate); dbg(" Report metric : %d\n", c->apolicy.report_metric_periodic); dbg(" Report RCPI-thresh : %d\n", c->apolicy.report_rcpi_threshold); dbg(" Report Util-thresh : %d\n", c->apolicy.report_util_threshold); @@ -85,6 +98,17 @@ void cntlr_config_dump(struct controller_config *c) dbg(" Disallow bSTA P1 : %d\n", c->apolicy.disallow_bsta_p1); dbg(" Disallow bSTA P2 : %d\n", c->apolicy.disallow_bsta_p2); +#if 0 + // INIT_LIST_HEAD(&c->apolicy.steer_exlist); // TODO: remove INIT_LIST_HEAD + // INIT_LIST_HEAD(&c->apolicy.btmsteer_exlist); + list_for_each_entry(x, &c->apolicy.steer_exlist, list) { + dbg(" Disallowed STA : %s\n", x->macstring); + } + list_for_each_entry(x, &c->apolicy.btmsteer_exlist, list) { + dbg(" Disallowed BTM STA : %s\n", x->macstring); + } +#endif + dbg("---------------------------\n"); } @@ -224,11 +248,14 @@ static int cntlr_config_get_agent_policy(struct controller_config *c, { enum { POL_AGENT_ID, + POL_STEER_EXCLUDE, + POL_STEER_EXCLUDE_BTM, POL_STEER, POL_UTIL_TH, POL_RCPI_TH, POL_RPT_SCAN, POL_RPT_ASSOC_FAILS, + POL_RPT_ASSOC_FAILS_RATE, POL_RPT_METRIC_PERIODIC, POL_RPT_RCPI_TH, POL_RPT_UTIL_TH, @@ -242,11 +269,14 @@ static int cntlr_config_get_agent_policy(struct controller_config *c, }; const struct uci_parse_option opts[] = { { .name = "agent_id", .type = UCI_TYPE_STRING }, + { .name = "steer_exclude", .type = UCI_TYPE_LIST }, + { .name = "steer_exclude_btm", .type = UCI_TYPE_LIST }, { .name = "steer_policy", .type = UCI_TYPE_STRING }, { .name = "util_threshold", .type = UCI_TYPE_STRING }, { .name = "rcpi_threshold", .type = UCI_TYPE_STRING }, { .name = "report_scan", .type = UCI_TYPE_STRING }, { .name = "report_sta_assocfails", .type = UCI_TYPE_STRING }, + { .name = "report_sta_assocfails_rate", .type = UCI_TYPE_STRING }, { .name = "report_metric_periodic", .type = UCI_TYPE_STRING }, { .name = "report_rcpi_threshold", .type = UCI_TYPE_STRING }, { .name = "report_util_threshold", .type = UCI_TYPE_STRING }, @@ -259,11 +289,15 @@ static int cntlr_config_get_agent_policy(struct controller_config *c, }; struct uci_option *tb[NUM_POLICIES]; struct agent_policy *a; + struct uci_element *x; a = calloc(1, sizeof(*a)); if (!a) return -1; + INIT_LIST_HEAD(&a->steer_exlist); + INIT_LIST_HEAD(&a->btmsteer_exlist); + uci_parse_section(s, opts, NUM_POLICIES, tb); if (tb[POL_AGENT_ID]) { @@ -272,6 +306,18 @@ static int cntlr_config_get_agent_policy(struct controller_config *c, hwaddr_aton(val, a->agent_id); } + if (tb[POL_STEER_EXCLUDE]) { + uci_foreach_element(&tb[POL_STEER_EXCLUDE]->v.list, x) { + stax_add_entry(&a->steer_exlist, x->name); + } + } + + if (tb[POL_STEER_EXCLUDE_BTM]) { + uci_foreach_element(&tb[POL_STEER_EXCLUDE_BTM]->v.list, x) { + stax_add_entry(&a->btmsteer_exlist, x->name); + } + } + if (tb[POL_STEER]) a->policy = atoi(tb[POL_STEER]->v.string); @@ -292,6 +338,11 @@ static int cntlr_config_get_agent_policy(struct controller_config *c, true : false; } + if (tb[POL_RPT_ASSOC_FAILS_RATE]) { + a->report_sta_assocfails_rate = + atoi(tb[POL_RPT_ASSOC_FAILS_RATE]->v.string); + } + if (tb[POL_RPT_METRIC_PERIODIC]) { a->report_metric_periodic = atoi(tb[POL_RPT_METRIC_PERIODIC]->v.string); diff --git a/src/core/config.h b/src/core/config.h index 792e25bd2732711cbe089a2893755c3c05b743f5..75c0af2a5d12516f5134b9ea1719801d04aba909 100644 --- a/src/core/config.h +++ b/src/core/config.h @@ -37,6 +37,11 @@ enum wifi_security { }; */ +struct stax { + char macstring[32]; /* ':' separated mac address string */ + struct list_head list; +}; + struct iface_credential { enum wifi_band band; enum wifi_security sec; @@ -63,6 +68,7 @@ struct agent_policy { uint8_t rcpi_threshold; /* 0 - 220 */ bool report_scan; /* report independent scans */ bool report_sta_assocfails; /* report STA assoc fails */ + uint32_t report_sta_assocfails_rate; /* reporting rate for STA assoc fails (attempts per minute) */ uint8_t report_metric_periodic; /* 0 = disable, else 1 - 255 in secs */ uint8_t report_rcpi_threshold; /* 0, or 1 - 220 */ uint8_t report_util_threshold; /* 0, or channel utilization value */