diff --git a/README.md b/README.md index f98aa31ebb7f5637fe80f97b9eafe5a460e15079..d5bea69793de14b667669e08673a1a9d98dd7bdd 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,8 @@ config controller 'controller' option debug '0' option enable_sta_steer '0' option enable_bsta_steer '0' + option use_bcn_metrics '0' + option use_usta_metrics '0' option primary_vid '1' option primary_pcp '0' @@ -277,20 +279,25 @@ all agents to be reconfigured. ``` root@iopsys:~# ubus -v list map.controller -'map.controller' @2d8adaa3 +'map.controller' @601d80a0 "status":{} "ap_caps":{"agent":"String"} "sta_caps":{"agent":"String","sta":"String","bssid":"String"} - "channels":{"agent":"String"} + "channel_pref":{"agent":"String"} "bk_steer":{"agent":"String","bssid":"String","channel":"Integer","op_class":"Integer","bksta":"String"} "agent_policy":{"agent":"String","radiolist":"Array","bsslist":"Array"} - "channel":{"agent":"String","radio_id":"String","class_id":"Integer","channel":"Array","preference":"Integer","transmit_power":"Integer"} + "channel_select":{"agent":"String","radio_id":"String","class_id":"Integer","channel":"Array","preference":"Integer","transmit_power":"Integer"} "reconfig_ap":{"agent":"String"} - "client_steering":{"agent":"String","src_bssid":"String","sta":"Array","target_bssid":"Array","steer_timeout":"Integer","btm_timeout":"Integer","steer_req_mode":"Boolean"} + "steer":{"agent":"String","src_bssid":"String","sta":"Array","target_bssid":"Array","steer_timeout":"Integer","btm_timeout":"Integer","steer_req_mode":"Boolean"} "client_assoc_cntlr":{"agent":"String","bssid":"String","assoc_cntl_mode":"Integer","assoc_valid_timeout":"Integer","stalist":"Array"} "ap_metric_query":{"agent":"String","bsslist":"Array","radiolist":"Array"} "scan":{"agent":"String","radio":"Array","channel":"Array"} "sta_metric_query":{"agent":"String","sta":"String"} + "unassoc_sta_lm_query":{"agent":"String","opclass":"Integer","metrics":"Array"} + "bcn_metrics_query":{"agent":"String","sta":"String","opclass":"Integer","channel":"Integer","bssid":"String","reporting_detail":"Integer","ssid":"String","channel_report":"Array","request_element":"Array"} "bk_caps":{"agent":"String"} "topology_query":{"agent":"String"} + "cac_req":{"agent":"String","radiolist":"Array"} + "cac_term":{"agent":"String","radiolist":"Array"} + "higher_layer_data":{"agent":"String","protocol":"Integer","data":"String"} ``` diff --git a/src/cntlr.c b/src/cntlr.c index 3679adedac5315c7ad6342272138f42b4360897d..17cfda6fb283cfdaa85f10069420d1db0fb4eabe 100644 --- a/src/cntlr.c +++ b/src/cntlr.c @@ -81,7 +81,7 @@ static void cntlr_terminate(struct controller *c) exit(0); } -/* find node by macaddress */ +/* find netif by macaddress */ struct netif_iface *find_interface_by_mac(struct controller *c, struct netif_radio *r, uint8_t *hwaddr) { @@ -95,7 +95,45 @@ struct netif_iface *find_interface_by_mac(struct controller *c, return NULL; } -/* find node by macaddress */ +/* find radio by ssid */ +struct netif_radio *find_radio_by_ssid(struct controller *c, + struct node *n, char *ssid) +{ + struct netif_radio *r; + + list_for_each_entry(r, &n->radiolist, list) { + + struct netif_iface *p; + + list_for_each_entry(p, &r->iflist, list) { + if (!memcmp(p->ssid, ssid, 33)) + return r; + } + } + + return NULL; +} + +/* find netif by ssid */ +struct netif_iface *find_interface_by_ssid(struct controller *c, + struct node *n, char *ssid) +{ + struct netif_radio *r; + + list_for_each_entry(r, &n->radiolist, list) { + + struct netif_iface *p; + + list_for_each_entry(p, &r->iflist, list) { + if (!memcmp(p->ssid, ssid, 33)) + return p; + } + } + + return NULL; +} + +/* find radio by node */ struct netif_radio *find_radio_by_node(struct controller *c, struct node *n, uint8_t *radio) { @@ -122,7 +160,7 @@ struct node *find_node_by_mac(struct controller *c, uint8_t *mac) return NULL; } -/* find node by macaddress */ +/* find sta by macaddress */ struct sta *cntlr_find_sta(struct controller *c, uint8_t *mac) { struct sta *s; @@ -236,7 +274,6 @@ static void cntlr_bcn_metrics_parse(struct uloop_timeout *t) struct bcn_metrics *best = NULL, *b; struct node *n = s->fh->agent; struct controller *c = n->cntlr; - int i; dbg("%s %d for "MACFMT" attached to bssid " MACFMT "\n", __func__, __LINE__, MAC2STR(s->macaddr), MAC2STR(s->bssid)); dbg("%s %d node = "MACFMT"\n", __func__, __LINE__, MAC2STR(n->alid)); @@ -309,6 +346,7 @@ struct sta *cntlr_add_sta(struct controller *c, uint8_t *macaddr) return NULL; INIT_LIST_HEAD(&s->bcnlist); + INIT_LIST_HEAD(&s->unassoclist); memcpy(s->macaddr, macaddr, 6); list_add(&s->list, &c->stalist); s->bcn_metrics_timer.cb = cntlr_bcn_metrics_parse; @@ -342,6 +380,27 @@ static int cntlr_monitor_sta(struct controller *c, uint8_t *sta, } #endif +#if 0 +static void forall_node_get_usta_metrics(struct controller *c) +{ + struct sta *s; + + list_for_each_entry(s, &c->stalist, list) { + ; + struct cmdu_buff *cmdu; + + /* TODO: */ + //cmdu = cntlr_gen_unassoc_sta_metric_query(c, s->fh->agent->alid, s->macaddr, + // opclass, num_metrics, metrics); + if (!cmdu) + continue; + + send_cmdu(c, cmdu); + cmdu_free(cmdu); + } +} +#endif + static void forall_node_get_bcn_metrics(struct controller *c) { struct sta *s; @@ -646,6 +705,16 @@ void free_bcn_metrics(struct controller *c, struct sta *s) } } +void free_usta_metrics(struct controller *c, struct sta *s) +{ + struct una_sta_metrics *u, *tmp; + + list_for_each_entry_safe(u, tmp, &s->unassoclist, list) { + list_del(&u->list); + free(u); + } +} + void free_watchnode_cleanup(struct controller *c, struct watchnode *wn) { uloop_timeout_cancel(&wn->scan_timer); @@ -1016,6 +1085,9 @@ static void cntlr_periodic_run(struct uloop_timeout *t) forall_node_get_bcn_metrics(c); + /* TODO: */ + //forall_node_get_usta_metrics(c); + /* TODO: update only when a node is added or removed */ //forall_node_update_neighbors(c); @@ -1151,6 +1223,7 @@ static void controller_subscribe_for_cmdus(struct controller *c) CMDU_BEACON_METRICS_RESPONSE, CMDU_AP_METRICS_RESPONSE, CMDU_ASSOC_STA_LINK_METRICS_RESPONSE, + CMDU_UNASSOC_STA_LINK_METRIC_RESPONSE, CMDU_CHANNEL_SCAN_REQUEST, CMDU_CHANNEL_SCAN_REPORT, CMDU_CLIENT_DISASSOCIATION_STATS, diff --git a/src/cntlr.h b/src/cntlr.h index 3cb30b5c748ad96b7fed28287d652f14f0ebf7d4..fc469573a7ab96148d483271ad8b08c5432365b1 100644 --- a/src/cntlr.h +++ b/src/cntlr.h @@ -40,6 +40,16 @@ struct bcn_metrics { struct list_head list; }; +struct una_sta_metrics { + /* node the measurement has been done on */ + struct node *agent; + uint8_t channel; + uint32_t time_delta; + uint8_t ul_rcpi; + + struct list_head list; +}; + /* {"sta_macaddr":"ec:6c:9a:52:ac:bb","num_element":1,"element":0,"op_class":115,"channel":36,"rcpi":162,"rsni":90,"bssid":"0a:10:00:00:00:03"} */ @@ -58,6 +68,8 @@ struct sta { struct list_head bcnlist; + struct list_head unassoclist; + struct netif_iface *fh; /* the AP interface the sta is connected at */ enum device_type type; @@ -166,6 +178,7 @@ struct node { struct uloop_timeout refresh_timer; struct ubus_event_handler evh; struct agent_policy *ap; + uint8_t ap_cap; struct list_head radiolist; /** list of netif_radio */ //struct list_head stalist; struct list_head list; @@ -215,6 +228,15 @@ struct sta_channel_report { uint8_t channel[128]; }; +#define MAX_UNASSOC_STAMACS 10 +struct unassoc_sta_metric { + uint8_t channel; + uint8_t num_sta; + struct { + uint8_t macaddr[6]; + } sta[MAX_UNASSOC_STAMACS]; +}; + struct sta_error_response { uint8_t sta_mac[6]; uint8_t response; @@ -224,6 +246,10 @@ struct sta_error_response { extern int start_controller(void); struct node *alloc_node_init(struct controller *c, uint8_t *hwaddr); +struct netif_iface *find_interface_by_ssid(struct controller *c, + struct node *n, char *ssid); +struct netif_radio *find_radio_by_ssid(struct controller *c, + struct node *n, char *ssid); struct netif_radio *find_radio_by_node(struct controller *c, struct node *n, uint8_t *radio); struct node *find_node_by_mac(struct controller *c, uint8_t *mac); @@ -237,4 +263,5 @@ struct sta *cntlr_find_sta(struct controller *c, uint8_t *mac); struct netif_iface *cntlr_get_fbss_by_mac(struct controller *c, struct node *n, uint8_t *mac); void free_bcn_metrics(struct controller *c, struct sta *s); +void free_usta_metrics(struct controller *c, struct sta *s); #endif /* CNTLR_H */ diff --git a/src/cntlr_cmdu.c b/src/cntlr_cmdu.c index 699499779335ef3ad70380125748dba70a75f44b..fee8257f5005f1013eeb0954a3699f364be9a69a 100644 --- a/src/cntlr_cmdu.c +++ b/src/cntlr_cmdu.c @@ -333,6 +333,49 @@ out: return NULL; } +struct cmdu_buff *cntlr_gen_unassoc_sta_metric_query(struct controller *c, + uint8_t *origin, uint8_t opclass, + uint8_t num_metrics, struct unassoc_sta_metric *metrics) +{ + int ret; + uint16_t mid = 0; + struct cmdu_buff *frm; + struct node *n; + + /* A Multi-AP Controller shall not send an Unassociated + * STA Link Metrics Query message to a Multi-AP Agent that + * does not indicate support for Unassociated STA Link + * Metrics in the AP Capability TLV. + */ + n = find_node_by_mac(c, origin); + dbg("%s %d ap_cap = 0x%02x\n", __func__, __LINE__, n->ap_cap); + if (!(n->ap_cap & (UNASSOC_STA_REPORTING_ONCHAN | UNASSOC_STA_REPORTING_OFFCHAN))) { + dbg("%s: Unassoc STA metric not supported by " MACFMT "\n", + __func__, MAC2STR(origin)); + return NULL; + } + + frm = cmdu_alloc_simple(CMDU_UNASSOC_STA_LINK_METRIC_QUERY, &mid); + if (!frm) { + dbg("%s: -ENOMEM\n", __func__); + return NULL; + } + + memcpy(frm->origin, origin, 6); + + /* Unassociated STA link metrics query TLV */ + ret = cntlr_gen_unassociated_sta_link_metrics(c, frm, + opclass, num_metrics, metrics); + if (ret) + goto out; + + cmdu_put_eom(frm); + return frm; + +out: + return NULL; +} + struct cmdu_buff *cntlr_gen_bk_caps_query(struct controller *c, uint8_t *origin) { diff --git a/src/cntlr_cmdu.h b/src/cntlr_cmdu.h index 16f785d442e1341b22a651609a1d97b06ac652b0..bc718bf530aa7e47437205ad01c4d1c0704d9f7d 100644 --- a/src/cntlr_cmdu.h +++ b/src/cntlr_cmdu.h @@ -34,6 +34,9 @@ struct cmdu_buff *cntlr_gen_policy_config_req(struct controller *c, int num_bss, uint8_t *bsslist); struct cmdu_buff *cntlr_gen_sta_metric_query(struct controller *c, uint8_t *origin, uint8_t *sta); +struct cmdu_buff *cntlr_gen_unassoc_sta_metric_query(struct controller *c, + uint8_t *origin, uint8_t opclass, + uint8_t num_metrics, struct unassoc_sta_metric *metrics); struct cmdu_buff *cntlr_gen_ap_autoconfig_search(struct controller *c, uint8_t profile, uint8_t band); struct cmdu_buff *cntlr_gen_ap_autoconfig_response(struct controller *c, diff --git a/src/cntlr_map.c b/src/cntlr_map.c index 40e52bc9409af00df266a7cf8822a47d71beda97..d20a2b962be8e030774f8ffbd80082659bf2b4c3 100644 --- a/src/cntlr_map.c +++ b/src/cntlr_map.c @@ -649,6 +649,13 @@ int handle_ap_caps_report(void *cntlr, struct cmdu_buff *cmdu) if (!n) return -1; + /* AP Capability TLV */ + if (tv[0][0]) { + struct tlv_ap_cap *p = (struct tlv_ap_cap *)tv[0][0]->data; + + dbg("%s %d AP capability is 0x%02x\n", __func__, __LINE__, p->cap); + n->ap_cap = p->cap; + } index = 0; /* Parse AP Radio Basic Capabilities TLV */ @@ -758,6 +765,109 @@ int handle_ap_metrics_response(void *cntlr, struct cmdu_buff *cmdu) return 0; } +static int cntlr_request_usta_metrics(struct controller *c, + struct node *n, struct sta *s) +{ + struct cmdu_buff *usta_cmdu = NULL; + struct unassoc_sta_metric metrics[1] = {}; + struct netif_radio *r; + struct netif_iface *fh; + int j; + + trace("%s: --->\n", __func__); + + if (!n || !s) + return -1; + + trace("%s %d ap_cap = 0x%02x\n", __func__, __LINE__, n->ap_cap); + + if (n->ap_cap & UNASSOC_STA_REPORTING_OFFCHAN) + /* TODO: off channel measurements */ + dbg("%s: Offchannel Unassoc STA metric not supported"); + + if (n->ap_cap & UNASSOC_STA_REPORTING_ONCHAN) { + + r = find_radio_by_ssid(c, n, s->fh->ssid); + fh = find_interface_by_ssid(c, n, s->fh->ssid); + + if (!r || !fh) + return -1; + + for (j = 0; j < r->num_opclass; j++) { + metrics[0].channel = fh->channel; + metrics[0].num_sta = 1; + memcpy(metrics[0].sta[0].macaddr, s->macaddr, 6); + + usta_cmdu = cntlr_gen_unassoc_sta_metric_query(c, + n->alid, r->opclass[j], 1, metrics); + + if (usta_cmdu) { + send_cmdu(c, usta_cmdu); + cmdu_free(usta_cmdu); + } + } + + } else { + dbg("%s: Unassoc STA metric not supported by " MACFMT "\n", + __func__, MAC2STR(n->alid)); + } + + return 0; +} + +static int cntlr_request_bcn_metrics(struct controller *c, struct sta *s) +{ + struct cmdu_buff *bcn_cmdu; + uint8_t wildcard[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + uint8_t *opclasses, num_opclasses; + int i; + + dbg("%s %d band = %d\n", __func__, __LINE__, s->fh->band); + if (s->fh->band == BAND_5) { + num_opclasses = 4; + opclasses = calloc(1, num_opclasses); + if (!opclasses) + return -1; + + // 115, 118, 121, 124 + opclasses[0] = 115; + opclasses[1] = 118; + opclasses[2] = 121; + opclasses[3] = 124; + } else if (s->fh->band == BAND_2) { + num_opclasses = 1; + opclasses = calloc(1, num_opclasses); + if (!opclasses) + return -1; + + // 81 + opclasses[0] = 81; + } else + return -1; + + + /* TODO: use one query with num_report & report instead + * of sending separate bmq for each opclass + * as in ubus call: "channel_report":[{"opclass": + * 81, "channels": [1, 6, 13]}, {"opclass":82, ... + */ + for (i = 0; i < num_opclasses; i++) { + bcn_cmdu = cntlr_gen_beacon_metrics_query(c, + s->fh->agent->alid, + s->macaddr, opclasses[i], 0, + wildcard, 0, s->fh->ssid, 0, + NULL, 0, 0); + if (bcn_cmdu) { + send_cmdu(c, bcn_cmdu); + cmdu_free(bcn_cmdu); + } + } + + free(opclasses); + + return 0; +} + int handle_sta_link_metrics_response(void *cntlr, struct cmdu_buff *cmdu) { int i; @@ -805,82 +915,64 @@ int handle_sta_link_metrics_response(void *cntlr, struct cmdu_buff *cmdu) } if (s->type == IEEE1905 && s->ul_rcpi < c->cfg.rcpi_threshold) { - struct cmdu_buff *bcn_cmdu; - uint8_t wildcard[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; if (!c->cfg.enable_bsta_steer) { - trace("|%s:%d| rcpi below %d, but will not query for beacon \ - metrics to steer as 'enable_bsta_steer' not set!\n", + trace("|%s:%d| rcpi below %d, but will not query for any \ + metrics as 'enable_bsta_steer' is not set!\n", __func__, __LINE__, c->cfg.rcpi_threshold); return 0; } - free_bcn_metrics(c, s); - dbg("%s %d\n", __func__, __LINE__); - bcn_cmdu = cntlr_gen_beacon_metrics_query(c, + if (!c->cfg.use_bcn_metrics) { + struct cmdu_buff *bcn_cmdu; + uint8_t wildcard[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + + free_bcn_metrics(c, s); + dbg("%s %d\n", __func__, __LINE__); + bcn_cmdu = cntlr_gen_beacon_metrics_query(c, s->fh->agent->alid, s->macaddr, 0, 0, wildcard, 0, s->fh->ssid, 0, NULL, 0, 0); - if (bcn_cmdu) { - send_cmdu(c, bcn_cmdu); - cmdu_free(bcn_cmdu); + if (bcn_cmdu) { + send_cmdu(c, bcn_cmdu); + cmdu_free(bcn_cmdu); + } } + + if (!c->cfg.use_usta_metrics) { + /* TODO: Unassociated bsta metrics (?) + * free_usta_metrics(c, s); + * cntlr_gen_unassoc_sta_metric_query + */ + } + } else if (s->type == NON_IEEE1905 && s->ul_rcpi < c->cfg.rcpi_threshold) { - struct cmdu_buff *bcn_cmdu; - int i; - uint8_t wildcard[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; - uint8_t *opclasses, num_opclasses; if (!c->cfg.enable_sta_steer) { - trace("|%s:%d| rcpi below %d, but will not query for beacon \ - metrics to steer as 'enable_sta_steer' not set!\n", - __func__, __LINE__, c->cfg.rcpi_threshold); + trace("|%s:%d| rcpi below %d, but will not query for any \ + metrics as 'enable_sta_steer' is not set!\n", + __func__, __LINE__, c->cfg.rcpi_threshold); return 0; } - dbg("%s %d band = %d\n", __func__, __LINE__, s->fh->band); - if (s->fh->band == BAND_5) { - num_opclasses = 4; - opclasses = calloc(1, num_opclasses); - if (!opclasses) - return -1; - - // 115, 118, 121, 124 - opclasses[0] = 115; - opclasses[1] = 118; - opclasses[2] = 121; - opclasses[3] = 124; - } else if (s->fh->band == BAND_2) { - num_opclasses = 1; - opclasses = calloc(1, num_opclasses); - if (!opclasses) - return -1; - - // 81 - opclasses[0] = 81; - } else - return -1; + /* Get bcn metrics */ + if (!c->cfg.use_bcn_metrics) { + trace("%s %d\n", __func__, __LINE__); - free_bcn_metrics(c, s); - /* TODO: use one query with num_report & report instead - * of sending separate bmq for each opclass - * as in ubus call: "channel_report":[{"opclass": - * 81, "channels": [1, 6, 13]}, {"opclass":82, ... - */ - for (i = 0; i < num_opclasses; i++) { - bcn_cmdu = cntlr_gen_beacon_metrics_query(c, - s->fh->agent->alid, - s->macaddr, opclasses[i], 0, - wildcard, 0, s->fh->ssid, 0, - NULL, 0, 0); - if (bcn_cmdu) { - send_cmdu(c, bcn_cmdu); - cmdu_free(bcn_cmdu); - } + free_bcn_metrics(c, s); + cntlr_request_bcn_metrics(c, s); + uloop_timeout_set(&s->bcn_metrics_timer, 2 * 1000); } - free(opclasses); - trace("%s %d\n", __func__, __LINE__); - uloop_timeout_set(&s->bcn_metrics_timer, 2 * 1000); + /* Get usta metrics for each agent in mesh */ + if (!c->cfg.use_usta_metrics) { + struct node *no; + + trace("%s %d\n", __func__, __LINE__); + free_usta_metrics(c, s); + list_for_each_entry(no, &c->nodelist, list) { + cntlr_request_usta_metrics(c, no, s); + } + } } trace("%s %d\n", __func__, __LINE__); } @@ -889,13 +981,6 @@ trace("%s %d\n", __func__, __LINE__); return 0; } -int handle_unassoc_sta_link_metrics_response(void *cntlr, - struct cmdu_buff *cmdu) -{ - trace("%s: --->\n", __func__); - return 0; -} - int cntlr_send_1905_acknowledge(void *cntlr, struct cmdu_buff *rx_cmdu, struct sta_error_response *sta_resp, uint32_t sta_count) @@ -916,126 +1001,183 @@ int cntlr_send_1905_acknowledge(void *cntlr, return 0; } -int cntlr_check_sta_steer(struct controller *c, struct sta *s) +struct una_sta_metrics *cntlr_find_usta_metric(struct controller *c, + struct sta *s, uint8_t *mac) { - struct bcn_metrics *best = NULL, *b; + struct una_sta_metrics *u; + + dbg("%s %d\n", __func__, __LINE__); + list_for_each_entry(u, &s->unassoclist, list) { + dbg("%s %d mac "MACFMT" alid " MACFMT"\n", __func__, __LINE__, + MAC2STR(u->agent->alid), MAC2STR(mac)); + if (!memcmp(u->agent->alid, mac, 6)) + return u; + } + + return NULL; +} + +#define USTA_STEER_UL_RCPI_DELTA 10 + +/* use unassociated STA measurements to steer */ +void cntlr_check_usta_steer(struct controller *c, struct sta *s) +{ + struct una_sta_metrics *best = NULL, *u; struct node *n = s->fh->agent; + struct netif_iface *best_fh; - dbg("%s %d for "MACFMT" attached to bssid " MACFMT "\n", __func__, __LINE__, MAC2STR(s->macaddr), MAC2STR(s->bssid)); - dbg("%s %d node = "MACFMT"\n", __func__, __LINE__, MAC2STR(n->alid)); + dbg("%s %d\n", __func__, __LINE__); + + if (!c->cfg.use_usta_metrics) { + dbg("%s %d Will not use unassociated STA metrics \ + data to steer\n", __func__, __LINE__); + return; + } + + dbg("%s %d for "MACFMT" attached to bssid " MACFMT " node = " \ + MACFMT "\n", __func__, __LINE__, MAC2STR(s->macaddr), + MAC2STR(s->bssid), MAC2STR(n->alid)); + + list_for_each_entry(u, &s->unassoclist, list) { + dbg("%s %d check usta node "MACFMT"\n", + __func__, __LINE__, MAC2STR(u->agent->alid)); - //for (i = 0; i < s->num_bcn_metrics; i++) { - list_for_each_entry(b, &s->bcnlist, list) { - dbg("%s %d bcn "MACFMT" \n", __func__, __LINE__, - MAC2STR(b->bssid)); if (!best) { - best = b; + best = u; continue; } - dbg("%s %d best rcpi %u this rcpi %u\n", __func__, __LINE__, - best->rcpi, b->rcpi); + dbg("%s %d best ul_rcpi %u this ul_rcpi %u\n", __func__, __LINE__, + best->ul_rcpi, u->ul_rcpi); - if ((best->rcpi - b->rcpi) > 10) { - dbg("%s %d new best bcn "MACFMT" with rcpi %d\n", + if ((best->ul_rcpi - u->ul_rcpi) > USTA_STEER_UL_RCPI_DELTA) { + dbg("%s %d new best usta node "MACFMT" with ul_rcpi %d\n", __func__, __LINE__, - MAC2STR(b->bssid), - b->rcpi); - best = b; + MAC2STR(u->agent->alid), + u->ul_rcpi); + best = u; } } if (!best) { dbg("%s %d\n", __func__, __LINE__); - return 0; + return; } - //if (!hwaddr_is_zero(best->bssid) && memcmp(best->bssid, s->bssid, 6)) { + /* Get appropriate netif on best node based on current ssid */ + best_fh = find_interface_by_ssid(c, best->agent, s->fh->ssid); + + if (best_fh && !hwaddr_is_zero(best_fh->bssid) + && memcmp(best_fh->bssid, s->bssid, 6)) { + struct cmdu_buff *cmdu; - if (!c->cfg.enable_sta_steer) { + if ((s->type == IEEE1905 && !c->cfg.enable_bsta_steer) || + (s->type == NON_IEEE1905 && !c->cfg.enable_sta_steer)) { trace("|%s:%d| better bssid found, but will not steer "MACFMT",\ - because the 'enable_sta_steer' is not set!\n", + because the 'enable_(b)sta_steer' is not set!\n", __func__, __LINE__, MAC2STR(s->macaddr)); - return 0; + return; } - dbg("%s %d new bssid, wow! try to steer "MACFMT " " MACFMT " " MACFMT "\n", __func__, - __LINE__, MAC2STR(s->macaddr), MAC2STR(best->bssid), MAC2STR(s->bssid)); + dbg("%s %d better bssid found! try to steer " MACFMT " \ + from " MACFMT " to " MACFMT "\n", + __func__, __LINE__, + MAC2STR(s->macaddr), MAC2STR(s->bssid), MAC2STR(best_fh->bssid)); + + if (s->type == IEEE1905) + cmdu = cntlr_gen_backhaul_steer_request(c, s->agent->alid, + s->macaddr, best_fh->bssid, 0, 0); + else + cmdu = cntlr_gen_client_steer_request(c, s->fh->agent->alid, + s->bssid, 0, + 1, (uint8_t (*)[6])s->macaddr, + 1, (uint8_t (*)[6])best_fh->bssid, + 1); - - cmdu = cntlr_gen_client_steer_request(c, s->fh->agent->alid, - s->bssid, 0, - 1, (uint8_t (*)[6])s->macaddr, - 1, (uint8_t (*)[6])best->bssid, - 1); if (cmdu) { send_cmdu(c, cmdu); cmdu_free(cmdu); } - //} + } dbg("%s %d\n", __func__, __LINE__); - - return 0; } -int cntlr_check_bsta_steer(struct controller *c, struct sta *s) +int handle_unassoc_sta_link_metrics_response(void *cntlr, + struct cmdu_buff *cmdu) { - struct bcn_metrics *best = NULL, *b; - struct node *n; + struct controller *c = (struct controller *) cntlr; + struct tlv_policy a_policy[] = { + [0] = { .type = MAP_TLV_UNASSOCIATED_STA_LINK_METRICS_RESPONSE, .present = TLV_PRESENT_ONE }, + }; + struct tlv *tv[1][16] = {0}; + int i = 0; + int ret = 0; - dbg("%s %d for "MACFMT"\n", __func__, __LINE__, MAC2STR(s->macaddr)); + trace("%s: --->\n", __func__); - n = find_node_by_mac(c, s->agent->alid); - if (!n) + if (!cntlr || !cmdu) return -1; - dbg("%s %d node = "MACFMT"\n", __func__, __LINE__, MAC2STR(n->alid)); + /* If a Multi-AP Controller receives an Unassociated STA + * Link Metrics Response message, then it shall respond + * within one second with a 1905 Ack message. + */ + cntlr_send_1905_acknowledge(cntlr, cmdu, NULL, 0); - list_for_each_entry(b, &s->bcnlist, list) { - dbg("%s %d bcn "MACFMT" \n", __func__, __LINE__, MAC2STR(b->bssid)); - if (!best) { - best = b; - continue; - } - dbg("%s %d best rcpi %u this rcpi %u\n", __func__, __LINE__, - best->rcpi, b->rcpi); - if ((best->rcpi - b->rcpi) > 10) { - dbg("%s %d new best bcn "MACFMT" with rcpi %d\n", - __func__, __LINE__, - MAC2STR(b->bssid), - b->rcpi); - best = b; - } - } + ret = cmdu_parse_tlvs(cmdu, tv, a_policy, 1); - if (!best) { - dbg("%s %d\n", __func__, __LINE__); - return 0; - } + if (ret || !tv[0][0]) + return -1; - if (memcmp(best->bssid, s->bssid, 6)) { - if (!c->cfg.enable_bsta_steer) { - trace("|%s:%d| new bssid, but will not try to steer "MACFMT",\ - because the 'enable_bsta_steer' is not set!\n", - __func__, __LINE__, MAC2STR(s->macaddr)); - return 0; - } - struct cmdu_buff *cmdu; - dbg("%s %d new bssid, wow! try to steer "MACFMT"\n", __func__, __LINE__, MAC2STR(s->macaddr)); - cmdu = cntlr_gen_backhaul_steer_request(c, s->agent->alid, - s->macaddr, best->bssid, 0, 0); - if (!cmdu) - return -1; + if (tv[0][0]) { + int offset = 0; + uint8_t *tv_data = (uint8_t *)tv[0][0]->data; + struct tlv_unassoc_sta_link_metrics_resp *resp = + (struct tlv_unassoc_sta_link_metrics_resp *)tv_data; + + offset = sizeof(*resp); + for (i = 0; i < resp->num_sta; i++) { + struct una_sta_metrics *u; + struct unassoc_sta_link_metrics_sta *b = + (struct unassoc_sta_link_metrics_sta *)&tv_data[offset]; + struct sta *s = cntlr_find_sta(c, b->macaddr); + + if (!s) { + dbg("|%s:%d| unassociated STA "MACFMT" not found!\n", + __func__, __LINE__, MAC2STR(b->macaddr)); + continue; + } - dbg("%s %d\n", __func__, __LINE__); - send_cmdu(c, cmdu); - cmdu_free(cmdu); + u = cntlr_find_usta_metric(c, s, cmdu->origin); /* alid */ + if (!u) { + u = calloc(1, sizeof(*u)); + if (!u) + continue; + + list_add(&u->list, &s->unassoclist); + u->agent = find_node_by_mac(c, cmdu->origin); + dbg("%s %d\n", __func__, __LINE__); + } + + u->channel = b->channel; + u->time_delta = BUF_GET_BE32(b->time_delta); + u->ul_rcpi = b->ul_rcpi; + + trace("\t\tu->agent.alid: " MACFMT "\n", MAC2STR(u->agent->alid)); + trace("\t\tu->channel: %d\n", u->channel); + trace("\t\tu->time_delta: %lu\n",BUF_GET_BE32(u->time_delta)); + trace("\t\tu->ul_rcpi: %d\n", u->ul_rcpi); + + offset += sizeof(*b); + + /* Steer if (b)STA would benefit from it */ + cntlr_check_usta_steer(c, s); + } } - dbg("%s %d\n", __func__, __LINE__); - return 0; + return ret; } struct bcn_metrics *cntlr_find_sta_bcn_metric(struct controller *c, @@ -1175,10 +1317,6 @@ dbg("%s %d\n", __func__, __LINE__); dbg("%s %d\n", __func__, __LINE__); } dbg("%s %d\n", __func__, __LINE__); -// if (s->type == IEEE1905) -// cntlr_check_bsta_steer(c, s); -// else -// cntlr_check_sta_steer(c, s); return ret; } diff --git a/src/cntlr_map_debug.c b/src/cntlr_map_debug.c index 06d9d3f82e3f0902307eb9015c552ce04496ac99..d979f6569871c10222615db9e33176cb764d63fe 100644 --- a/src/cntlr_map_debug.c +++ b/src/cntlr_map_debug.c @@ -919,6 +919,41 @@ int debug_unassoc_sta_link_metrics_response(void *cntlr, struct cmdu_buff *cmdu) { trace("%s: --->\n", __func__); + + int i; + int offset = 0; + struct tlv *tv[1][16]; + struct tlv_policy sta_policy[] = { + [0] = { .type = MAP_TLV_UNASSOCIATED_STA_LINK_METRICS_RESPONSE, .present = TLV_PRESENT_ONE }, + }; + + trace("parsing unassociated sta link metric response |" \ + MACFMT "\n", MAC2STR(cmdu->origin)); + cmdu_parse_tlvs(cmdu, tv, sta_policy, 1); + + if (tv[0][0]) { + uint8_t *tv_data = (uint8_t *)tv[0][0]->data; + struct tlv_unassoc_sta_link_metrics_resp *p = + (struct tlv_unassoc_sta_link_metrics_resp *)tv_data; + + trace("MAP_TLV_UNASSOCIATED_STA_LINK_METRICS:\n"); + trace("\topclass: %d\n", p->opclass); + trace("\tnum_sta: %d\n", p->num_sta); + + offset = sizeof(*p); + for (i = 0; i < p->num_sta; i++) { + struct unassoc_sta_link_metrics_sta *b = + (struct unassoc_sta_link_metrics_sta *)&tv_data[offset]; + + trace("\t\tmacaddr: " MACFMT "\n", MAC2STR(b->macaddr)); + trace("\t\tchannel: %d\n", b->channel); + trace("\t\ttime_delta: %lu\n", + BUF_GET_BE32(b->time_delta)); + trace("\t\tul_rcpi: %d\n", b->ul_rcpi); + offset += sizeof(*b); + } + } + return 0; } diff --git a/src/cntlr_tlv.c b/src/cntlr_tlv.c index b715cd9d41eecce45d2e6370824d7e15f69202ba..e9eb1e9af2905bd0cde51737ea1df146769d9854 100644 --- a/src/cntlr_tlv.c +++ b/src/cntlr_tlv.c @@ -937,6 +937,54 @@ int cntlr_gen_sta_mac(struct controller *c, return 0; } +int cntlr_gen_unassociated_sta_link_metrics(struct controller *c, + struct cmdu_buff *frm, uint8_t opclass, + uint8_t num_metrics, struct unassoc_sta_metric *metrics) +{ + int ret, i, j, num_sta; + struct tlv *t; + struct tlv_unassoc_sta_link_metrics_query *data; + + t = cmdu_reserve_tlv(frm, 512); + if (!t) + return -1; + + t->type = MAP_TLV_UNASSOCIATED_STA_LINK_METRICS_QUERY; + t->len = sizeof(struct tlv_unassoc_sta_link_metrics_query); + + data = (struct tlv_unassoc_sta_link_metrics_query *) t->data; + data->opclass = opclass; + data->num_channel = num_metrics; + + for (i = 0; i < num_metrics; i++) { + t->len += 2; /* two bytes: channel & num_sta */ + + data->ch[i].channel = metrics[i].channel; + num_sta = metrics[i].num_sta; + + if (num_sta > MAX_UNASSOC_STAMACS) { + dbg("%s: error: num_sta (%d) greater than %d\n", + __func__, num_sta, MAX_UNASSOC_STAMACS); + num_sta = MAX_UNASSOC_STAMACS; + } + + t->len += (num_sta * 6); /* six bytes: macaddr */ + + data->ch[i].num_sta = num_sta; + for (j = 0; j < num_sta; j++) + memcpy(data->ch[i].sta[j].macaddr, + metrics[i].sta[j].macaddr, 6); + } + + ret = cmdu_put_tlv(frm, t); + if (ret) { + dbg("%s: error: cmdu_put_tlv()\n", __func__); + return -1; + } + + return 0; +} + int cntlr_gen_searched_role(struct controller *c, struct cmdu_buff *frm, uint8_t role) { diff --git a/src/cntlr_tlv.h b/src/cntlr_tlv.h index 0bb12de1a1c9d639a5fac1ada698bfc06268a375..ed517a6ee6899170505eea9dc2f934a12f10e5a4 100644 --- a/src/cntlr_tlv.h +++ b/src/cntlr_tlv.h @@ -83,6 +83,9 @@ int cntlr_gen_ap_metric_query(struct controller *c, struct cmdu_buff *frm, uint8_t num_bss, uint8_t *bsslist); int cntlr_gen_sta_mac(struct controller *c, struct cmdu_buff *frm, uint8_t *sta); +int cntlr_gen_unassociated_sta_link_metrics(struct controller *c, + struct cmdu_buff *frm, uint8_t opclass, + uint8_t num_metrics, struct unassoc_sta_metric *metrics); int cntlr_gen_searched_role(struct controller *c, struct cmdu_buff *frm, uint8_t role); int cntlr_gen_autoconf_freq_band(struct controller *c, struct cmdu_buff *frm, diff --git a/src/cntlr_ubus.c b/src/cntlr_ubus.c index a86b684cc91fae9a22893a50dd5b0492673f705a..c9e0cd9d3c67883a1e0fb4b784696bf1494fa9fa 100644 --- a/src/cntlr_ubus.c +++ b/src/cntlr_ubus.c @@ -232,6 +232,22 @@ static const struct blobmsg_policy sta_metric_query_params[__STA_METRIC_QUERY_MA [STA_METRIC_QUERY_STA] = { .name = "sta", .type = BLOBMSG_TYPE_STRING }, }; +enum { + UNASSOC_STA_LM_QUERY_AGENT, + UNASSOC_STA_LM_QUERY_OPCLASS, + UNASSOC_STA_LM_QUERY_METRICS, + __UNASSOC_STA_LM_QUERY_MAX, +}; + +static const struct blobmsg_policy unassoc_sta_lm_query_params[__UNASSOC_STA_LM_QUERY_MAX] = { + [UNASSOC_STA_LM_QUERY_AGENT] = { .name = "agent", + .type = BLOBMSG_TYPE_STRING }, + [UNASSOC_STA_LM_QUERY_OPCLASS] = { .name = "opclass", + .type = BLOBMSG_TYPE_INT32 }, + [UNASSOC_STA_LM_QUERY_METRICS] = { .name = "metrics", + .type = BLOBMSG_TYPE_ARRAY }, +}; + enum { BK_CAPS_POLICY_AGENT, __BK_CAPS_POLICY_MAX, @@ -1648,6 +1664,157 @@ static int cntlr_sta_metric_query(struct ubus_context *ctx, return UBUS_STATUS_OK; } +static int cntlr_unassoc_sta_lm_query(struct ubus_context *ctx, + struct ubus_object *obj, struct ubus_request_data *req, + const char *method, struct blob_attr *msg) +{ + struct cmdu_buff *cmdu; + char mac_str[18]; + uint8_t agent_mac[6] = { 0 }; + struct blob_attr *tb[__UNASSOC_STA_LM_QUERY_MAX]; + struct controller *c = container_of(obj, struct controller, obj); + uint8_t opclass = 0; + int num_metrics = 0; + struct unassoc_sta_metric *metrics = NULL; + int ret = UBUS_STATUS_OK; + + blobmsg_parse(unassoc_sta_lm_query_params, __UNASSOC_STA_LM_QUERY_MAX, tb, + blob_data(msg), blob_len(msg)); + + if (!tb[UNASSOC_STA_LM_QUERY_AGENT]) { + fprintf(stderr, "Unassociated STA link metric query: provide Agent" \ + "address in format aa:bb:cc:dd:ee:ff\n"); + return UBUS_STATUS_INVALID_ARGUMENT; + } + + memset(mac_str, 0, sizeof(mac_str)); + strncpy(mac_str, blobmsg_data(tb[UNASSOC_STA_LM_QUERY_AGENT]), + sizeof(mac_str) - 1); + if (!hwaddr_aton(mac_str, agent_mac)) + return UBUS_STATUS_UNKNOWN_ERROR; + + if (tb[UNASSOC_STA_LM_QUERY_OPCLASS]) + opclass = (int) blobmsg_get_u32( + tb[UNASSOC_STA_LM_QUERY_OPCLASS]); + + if (!opclass) { + fprintf(stderr, "unassoc_sta_lm_query: missing opclass\n"); + return UBUS_STATUS_INVALID_ARGUMENT; + } + + /* Example ubus call: + * ubus call map.controller unassoc_sta_lm_query '{"agent": + * "44:d4:37:42:47:b9", "opclass":81, "metrics": + * [{"channel":11, "stamacs": ["44:d4:37:42:3a:c6", "44:d4:37:42:47:be"]}]}' + * + * ubus call map.controller unassoc_sta_lm_query '{"agent": + * "44:d4:37:42:47:b9", "opclass":128, + * "metrics":[{"channel":36,"stamacs: ["e0:d4:e8:79:c4:ef"]}]}' + */ + + if (tb[UNASSOC_STA_LM_QUERY_METRICS]) { + struct blob_attr *cur; + static const struct blobmsg_policy supp_attrs[2] = { + [0] = { .name = "channel", + .type = BLOBMSG_TYPE_INT32 }, + [1] = { .name = "stamacs", + .type = BLOBMSG_TYPE_ARRAY }, + }; + int rem, i = 0; + + num_metrics = blobmsg_check_array(tb[UNASSOC_STA_LM_QUERY_METRICS], + BLOBMSG_TYPE_TABLE); + if (!num_metrics) { + fprintf(stderr, "unassoc_sta_lm_query: missing metrics\n"); + return UBUS_STATUS_INVALID_ARGUMENT; + } + + /* TODO: consider dynamic allocation for number of STAs on the list */ + metrics = calloc(num_metrics, sizeof(struct unassoc_sta_metric)); + if (!metrics) { + ret = -ENOMEM; + goto out; + } + + blobmsg_for_each_attr(cur, tb[UNASSOC_STA_LM_QUERY_METRICS], rem) { + int remm, j = 0; + struct blob_attr *data[2], *attr; + char mac[18]; + + blobmsg_parse(supp_attrs, 2, data, blobmsg_data(cur), + blobmsg_data_len(cur)); + + if (!data[0] || !data[1]) + continue; + + metrics[i].channel = (uint8_t) blobmsg_get_u32(data[0]); + metrics[i].num_sta = blobmsg_check_array( + data[1], BLOBMSG_TYPE_STRING); + + if (!metrics[i].channel) { + fprintf(stderr, "unassoc_sta_lm_query: missing channel \ + for metrics [%d]\n", i); + ret = UBUS_STATUS_INVALID_ARGUMENT; + goto out; + } + + if (!metrics[i].num_sta) { + fprintf(stderr, "unassoc_sta_lm_query: no stations for \ + channel %d\n", metrics[i].channel); + ret = UBUS_STATUS_INVALID_ARGUMENT; + goto out; + } + + if (metrics[i].num_sta > MAX_UNASSOC_STAMACS) { + fprintf(stderr, "unassoc_sta_lm_query: max 10 stations \ + allowed per channel!\n"); + ret = UBUS_STATUS_INVALID_ARGUMENT; + goto out; + } + + /* Iterate through all metrics of given channel */ + blobmsg_for_each_attr(attr, data[1], remm) { + if (blobmsg_type(attr) != BLOBMSG_TYPE_STRING) + continue; + + /* STA list */ + strncpy(mac, blobmsg_get_string(attr), sizeof(mac) - 1); + hwaddr_aton(mac, metrics[i].sta[j].macaddr); + + j++; + } + + if (metrics[i].num_sta != j) { + dbg("%s(): invalid metric [%d]!\n", __func__, i); + ret = UBUS_STATUS_INVALID_ARGUMENT; + goto out; + } + + i++; + } + + if (num_metrics != i) { + dbg("%s(): invalid metrics!\n", __func__); + ret = UBUS_STATUS_INVALID_ARGUMENT; + goto out; + } + } + + cmdu = cntlr_gen_unassoc_sta_metric_query(c, agent_mac, + opclass, num_metrics, metrics); + if (!cmdu) + return UBUS_STATUS_UNKNOWN_ERROR; + + send_cmdu(c, cmdu); + cmdu_free(cmdu); + +out: + if (metrics) + free(metrics); + + return ret; +} + int cntlr_bcn_metrics_query(struct ubus_context *ctx, struct ubus_object *obj, struct ubus_request_data *req, const char *method, struct blob_attr *msg) @@ -2184,6 +2351,8 @@ int cntlr_publish_object(struct controller *c, const char *objname) UBUS_METHOD("scan", cntlr_scan, scan_policy_params), UBUS_METHOD("sta_metric_query", cntlr_sta_metric_query, sta_metric_query_params), + UBUS_METHOD("unassoc_sta_lm_query", cntlr_unassoc_sta_lm_query, + unassoc_sta_lm_query_params), UBUS_METHOD("bcn_metrics_query", cntlr_bcn_metrics_query, bcn_metrics_query_params), UBUS_METHOD("bk_caps", cntlr_bk_caps, diff --git a/src/config.c b/src/config.c index 4ea5a3fbd7547ed4c8a80490218a366d86b7ce9b..51f056702a270c9c640a817db2086104baef37a3 100644 --- a/src/config.c +++ b/src/config.c @@ -364,6 +364,8 @@ void cntlr_config_dump(struct controller_config *c) dbg("Registrar @2Ghz: %d\n", c->has_registrar_2g); dbg("Enable STA steer: %d\n", c->enable_sta_steer); dbg("Enable BSTA steer: %d\n", c->enable_bsta_steer); + dbg("Use bcn metrics to steer: %d\n", c->use_bcn_metrics); + dbg("Use uSTA metrics to steer: %d\n", c->use_usta_metrics); dbg("Credentials\n"); list_for_each_entry(cred, &c->aplist, list) { @@ -424,6 +426,8 @@ static int cntlr_config_get_base(struct controller_config *c, CNTLR_DEBUG, CNTLR_ENABLE_STA_STEER, CNTLR_ENABLE_BSTA_STEER, + CNTLR_USE_BCN_METRICS, + CNTLR_USE_USTA_METRICS, CNTLR_RCPI_STEER_THRESHOLD, CNTLR_PVID, CNTLR_PCP_DEFAULT, @@ -435,6 +439,8 @@ static int cntlr_config_get_base(struct controller_config *c, { .name = "debug", .type = UCI_TYPE_STRING }, { .name = "enable_sta_steer", .type = UCI_TYPE_STRING }, { .name = "enable_bsta_steer", .type = UCI_TYPE_STRING }, + { .name = "use_bcn_metrics", .type = UCI_TYPE_STRING }, + { .name = "use_usta_metrics", .type = UCI_TYPE_STRING }, { .name = "rcpi_threshold", .type = UCI_TYPE_STRING }, { .name = "primary_vid", .type = UCI_TYPE_STRING }, { .name = "primary_pcp", .type = UCI_TYPE_STRING }, @@ -476,6 +482,18 @@ static int cntlr_config_get_base(struct controller_config *c, c->enable_bsta_steer = atoi(val) == 1 ? true : false; } + if (tb[CNTLR_USE_BCN_METRICS]) { + const char *val = tb[CNTLR_USE_BCN_METRICS]->v.string; + + c->use_bcn_metrics = atoi(val) == 1 ? true : false; + } + + if (tb[CNTLR_USE_USTA_METRICS]) { + const char *val = tb[CNTLR_USE_USTA_METRICS]->v.string; + + c->use_usta_metrics = atoi(val) == 1 ? true : false; + } + if (tb[CNTLR_RCPI_STEER_THRESHOLD]) { const char *val = tb[CNTLR_RCPI_STEER_THRESHOLD]->v.string; diff --git a/src/config.h b/src/config.h index fab9d1c020168d55aa621563005994cf3339ab09..42028f1ac53b712ae42b00ff83b02b66991e4428 100644 --- a/src/config.h +++ b/src/config.h @@ -126,6 +126,8 @@ struct controller_config { int debug_level; bool enable_sta_steer; bool enable_bsta_steer; + bool use_bcn_metrics; + bool use_usta_metrics; int rcpi_threshold; int num_bss; int num_vlans;