diff --git a/src/core/agent_cmdu_generator.c b/src/core/agent_cmdu_generator.c index faa258700acf8be93ad11d17fd8dbba8b7eed91a..3b2d19d77aacfbb9fc4ffe2d291cd6451ce7ca01 100644 --- a/src/core/agent_cmdu_generator.c +++ b/src/core/agent_cmdu_generator.c @@ -160,3 +160,230 @@ fail_cmdu: free(cmdu); return NULL; } + +struct cmdu_cstruct *agent_gen_ap_metrics_response(struct agent *a, + struct cmdu_cstruct *rec_cmdu) +{ + int i, j; + int tlv_index = 0; + uint8_t *tmp; + struct cmdu_cstruct *cmdu; + + cmdu = calloc(1, sizeof(struct cmdu_cstruct)); + if (!cmdu) { + dbg("Out of memory.!\n"); + return NULL; + } + + cmdu->message_type = CMDU_AP_METRICS_RESPONSE; + memcpy(cmdu->origin, rec_cmdu->origin, 6); + strncpy(cmdu->intf_name, rec_cmdu->intf_name, IFNAMESIZE - 1); + cmdu->message_id = rec_cmdu->message_id; + + /* Calculate number of TLVs required for the response + * based on the received CMDU + */ + for (i = 0; i < rec_cmdu->num_tlvs; i++) { + tmp = (uint8_t *)rec_cmdu->tlvs[i]; + + switch (*tmp) { + case MAP_TLV_AP_RADIO_IDENTIFIER: + { + int radio_index; + struct tlv_ap_radio_identifier *p = + (struct tlv_ap_radio_identifier *)tmp; + + radio_index = get_radio_index(a, p->radio_id); + if (radio_index != -1) { +// #ifdef PROFILE2 + /* Radio Metrics TLV */ + cmdu->num_tlvs++; +// #endif + } + break; + } + + case MAP_TLV_AP_METRIC_QUERY: + { + int radio_index; + int bss_index; + struct wifi_radio_element *radio; + struct netif_fh *fh; + struct tlv_ap_metric_query *p = + (struct tlv_ap_metric_query *)tmp; + + for (j = 0; j < p->bssid_nr; j++) { + int num_sta = 0; + struct sta *s; + + bss_index = get_radio_and_bss_index(a, + p->ap_metric_query_bssid[j].bssid, + &radio_index); + + if (bss_index == -1) + continue; + + /* AP Metrics TLV */ + cmdu->num_tlvs++; +// #ifdef PROFILE2 + /* AP Extended Metrics TLV */ + cmdu->num_tlvs++; +// #endif + + radio = a->radios + radio_index; + fh = wifi_radio_to_ap(a, radio->name); + if (fh == NULL) + continue; + + list_for_each_entry(s, &fh->stalist, list) + num_sta++; + + // if (fh->cfg->include_sta_stats) { + /* Associated STA Traffic Stats TLV */ + cmdu->num_tlvs += num_sta; + // } + + // if (fh->cfg->include_sta_metric) { + /* Associated STA Link Metrics TLV */ + cmdu->num_tlvs += num_sta; + +// #ifdef PROFILE2 + /* Associated STA Extended Link Metrics TLV */ + cmdu->num_tlvs += num_sta; +// #endif + // } + + } + break; + } + + default: + dbg("Unsupported tlv in cmdu:%s\n", + map_stringify_cmdu_type(rec_cmdu->message_type)); + break; + } + } + + cmdu->tlvs = (uint8_t **)calloc(cmdu->num_tlvs, sizeof(uint8_t *)); + if (!cmdu->tlvs) { + cmdu->num_tlvs = 0; + goto error; + } + + /* Process response based on received CMDU */ + for (i = 0; i < rec_cmdu->num_tlvs; i++) { + tmp = (uint8_t *)rec_cmdu->tlvs[i]; + + switch (*tmp) { + case MAP_TLV_AP_RADIO_IDENTIFIER: + { + int radio_index; + struct tlv_radio_metrics *p1; + struct tlv_ap_radio_identifier *p = + (struct tlv_ap_radio_identifier *)tmp; + + radio_index = get_radio_index(a, p->radio_id); + if (radio_index != -1) { +// #ifdef PROFILE2 + /* Radio Metrics TLV */ + p1 = agent_gen_radio_metrics(a, + radio_index); + if (!p1) + goto error; + + cmdu->tlvs[tlv_index++] = (uint8_t *)p1; +// #endif + } + break; + } + + case MAP_TLV_AP_METRIC_QUERY: + { + int radio_index; + int bss_index; + struct wifi_radio_element *radio; + struct wifi_bss_element *bss; + struct netif_fh *fh; + struct tlv_ap_metrics *p2; + struct tlv_ap_ext_metrics *p3; + struct tlv_ap_metric_query *p = + (struct tlv_ap_metric_query *)tmp; + + for (j = 0; j < p->bssid_nr; j++) { + struct sta *s; + + bss_index = get_radio_and_bss_index(a, + p->ap_metric_query_bssid[j].bssid, + &radio_index); + + if (bss_index == -1) + continue; + + radio = a->radios + radio_index; + bss = radio->bsslist + bss_index; + /* AP Metrics TLV */ + p2 = agent_gen_ap_metrics(a, + radio_index, bss_index); + if (!p2) + goto error; + + cmdu->tlvs[tlv_index++] = (uint8_t *)p2; +// #ifdef PROFILE2 + /* AP Extended Metrics TLV */ + p3 = agent_gen_ap_ext_metrics(a, + radio_index, bss_index); + if (!p3) + goto error; + + cmdu->tlvs[tlv_index++] = (uint8_t *)p3; +// #endif + + fh = wifi_radio_to_ap(a, radio->name); + if (fh == NULL) + continue; + + + list_for_each_entry(s, &fh->stalist, list) { + // if (fh->cfg->include_sta_stats) { + /* Associated STA Traffic Stats TLV */ + struct tlv_assoc_sta_traffic_stats *p4; + + p4 = agent_gen_assoc_sta_traffic_stats(a, s); + if (!p4) + goto error; + + cmdu->tlvs[tlv_index++] = (uint8_t *)p4; + // } + + // if (fh->cfg->include_sta_metric) { + struct tlv_assoc_sta_link_metrics *p5; + struct tlv_assoc_sta_ext_link_metric *p6; + + p5 = agent_gen_assoc_sta_link_metrics(a, s, bss->bssid); + if (!p5) + goto error; + + cmdu->tlvs[tlv_index++] = (uint8_t *)p5; + +// #ifdef PROFILE2 + p6 = agent_gen_assoc_sta_ext_link_metric(a, s, bss->bssid); + if (!p6) + goto error; + + cmdu->tlvs[tlv_index++] = (uint8_t *)p6; +// #endif + // } + + } + } + break; + } + } + } + + return cmdu; + +error: + map_free_cmdu(cmdu); + return NULL; +} diff --git a/src/core/agent_cmdu_generator.h b/src/core/agent_cmdu_generator.h index acf40545f190fba54a8390ce3c5c208c556b3a31..f126aacb0fc7801f001a95d73b113cb2bcb1bec9 100644 --- a/src/core/agent_cmdu_generator.h +++ b/src/core/agent_cmdu_generator.h @@ -13,4 +13,10 @@ struct cmdu_cstruct *agent_gen_ap_autoconfig_search(struct agent *a, struct wifi_radio_element *radio, char *intf_name, uint8_t profile); +struct cmdu_cstruct *agent_gen_ap_metrics_response(struct agent *a, + struct cmdu_cstruct *rec_cmdu); + +int get_radio_index(struct agent *a, uint8_t *mac); +int get_bss_index(struct wifi_radio_element *radio, uint8_t *bssid); +int get_radio_and_bss_index(struct agent *a, uint8_t *bssid, int *radio_index); #endif diff --git a/src/core/agent_map.c b/src/core/agent_map.c index 59b8ea7cebb2381060531b88103e6754b97e28cc..e5b984b2e1df9820dcfbbf7ffdf006387ecf702f 100644 --- a/src/core/agent_map.c +++ b/src/core/agent_map.c @@ -54,6 +54,7 @@ #include <wsc.h> #include "agent_tlv_generator.h" +#include "agent_cmdu_generator.h" #define UBUS_TIMEOUT 1000 @@ -860,6 +861,181 @@ int handle_ap_autoconfig_renew(void *agent, struct cmdu_cstruct *cmdu) return 0; } +int get_radio_index(struct agent *a, uint8_t *mac) +{ + int i; + + for (i = 0; i < a->num_radios; i++) { + if (hwaddr_equal(a->radios[i].macaddr, mac)) + return i; + } + + return -1; +} + +int get_bss_index(struct wifi_radio_element *radio, uint8_t *bssid) +{ + int i; + + for (i = 0; i < radio->num_bss; i++) { + if (hwaddr_equal(radio->bsslist[i].bssid, bssid)) + return i; + } + + return -1; +} + +int get_radio_and_bss_index(struct agent *a, uint8_t *bssid, + int *radio_index) +{ + int i; + int bss_index; + + for (i = 0; i < a->num_radios; i++) { + bss_index = get_bss_index(&a->radios[i], bssid); + if (bss_index != -1) { + *radio_index = i; + return bss_index; + } + } + + return -1; +} + +/* ifname: some value, prepare the query for that specific interface, + * ifname: NULL, include all the interface. + */ +static int prepare_ap_metrics_query(void *agent, + struct cmdu_cstruct *cmdu, char *ifname) +{ + int total_bss = 0; + int i, j, k; + int tlv_index = 0; + struct agent *a = (struct agent *)agent; + struct wifi_radio_element *radio; + struct wifi_bss_element *bss; + struct tlv_ap_metric_query *p1; + + if (ifname == NULL) { + /* AP Metrics Query TLV */ + cmdu->num_tlvs++; +// #ifdef PROFILE2 + /* AP Radio Identifier TLV */ + cmdu->num_tlvs += a->num_radios; +// #endif + + for (i = 0; i < a->num_radios; i++) + total_bss += a->radios[i].num_bss; + + } else if (ifname && (ifname[0] != '\0')) { + /* AP Metrics Query TLV */ + cmdu->num_tlvs++; +// #ifdef PROFILE2 + /* AP Radio Identifier TLV */ + cmdu->num_tlvs++; +// #endif + + radio = wifi_ifname_to_radio_element(a, ifname); + if (!radio) { + cmdu->num_tlvs = 0; + return -1; + } + + total_bss = radio->num_bss; + } + + cmdu->tlvs = (uint8_t **)calloc(cmdu->num_tlvs, sizeof(uint8_t *)); + if (!cmdu->tlvs) { + cmdu->num_tlvs = 0; + return -1; + } + + + p1 = calloc(1, sizeof(*p1)); + if (!p1) + return -1; + + p1->tlv_type = MAP_TLV_AP_METRIC_QUERY; + p1->bssid_nr = total_bss; + p1->ap_metric_query_bssid = calloc(p1->bssid_nr, + sizeof(*p1->ap_metric_query_bssid)); + if (!p1->ap_metric_query_bssid) { + free(p1); + return -1; + } + + if (ifname == NULL) { + struct tlv_ap_radio_identifier *p2; + + k = 0; + for (i = 0; i < a->num_radios; i++) { + radio = a->radios + i; +// #ifdef PROFILE2 + p2 = calloc(1, sizeof(*p2)); + if (p2) { + p2->tlv_type = MAP_TLV_AP_RADIO_IDENTIFIER; + memcpy(p2->radio_id, radio->macaddr, 6); + cmdu->tlvs[tlv_index++] = (uint8_t *)p2; + } +// #endif + + for (j = 0; j < radio->num_bss; j++) { + bss = radio->bsslist + j; + memcpy(p1->ap_metric_query_bssid[k].bssid, + bss->bssid, 6); + k++; + } + } + + } else if (ifname && (ifname[0] != '\0')) { + struct tlv_ap_radio_identifier *p2; + + k = 0; + radio = wifi_ifname_to_radio_element(a, ifname); + if (!radio) { + map_free_tlv_cstruct((uint8_t *)p1); + return -1; + } + +// #ifdef PROFILE2 + p2 = calloc(1, sizeof(*p2)); + if (p2) { + p2->tlv_type = MAP_TLV_AP_RADIO_IDENTIFIER; + memcpy(p2->radio_id, radio->macaddr, 6); + cmdu->tlvs[tlv_index++] = (uint8_t *)p2; + } +// #endif + + for (i = 0; i < radio->num_bss; i++) { + bss = radio->bsslist + i; + memcpy(p1->ap_metric_query_bssid[k].bssid, + bss->bssid, 6); + k++; + } + + } + + cmdu->tlvs[tlv_index] = (uint8_t *)p1; + + return 0; +} + +static int prepare_ap_metrics_response(void *agent, + struct cmdu_cstruct *rec_cmdu) +{ + trace("%s: --->\n", __func__); + struct cmdu_cstruct *cmdu; + struct agent *a = (struct agent *)agent; + + cmdu = agent_gen_ap_metrics_response(a, rec_cmdu); + if (!cmdu) + return -1; + + agent_send_cmdu(a, cmdu); + map_free_cmdu(cmdu); + + return 0; +} int handle_1905_ack(void *agent, struct cmdu_cstruct *cmdu) { @@ -1495,6 +1671,7 @@ fail_cmdu: int handle_ap_metrics_query(void *agent, struct cmdu_cstruct *cmdu) { trace("%s: --->\n", __func__); + prepare_ap_metrics_response(agent, cmdu); return 0; } diff --git a/src/core/agent_tlv_generator.c b/src/core/agent_tlv_generator.c index 67d271fe67c2d8a26290d936bf36e7c413893192..532a4a542d39188b58a1fa86f100cbe6fbb7bbff 100644 --- a/src/core/agent_tlv_generator.c +++ b/src/core/agent_tlv_generator.c @@ -725,3 +725,166 @@ struct tlv_searched_role *agent_gen_searched_role(struct agent *a, return p; } + +struct tlv_radio_metrics *agent_gen_radio_metrics(struct agent *a, + int radio_index) +{ + struct tlv_radio_metrics *p; + struct wifi_radio_element *radio = a->radios + radio_index; + + p = (struct tlv_radio_metrics *)calloc(1, sizeof(*p)); + if (!p) + return NULL; + + p->tlv_type = MAP_TLV_RADIO_METRICS; + memcpy(p->radio_id, radio->macaddr, 6); + p->noise = radio->anpi; + /* + * TODO/FIXME: + * transmit, receive_self & receive_other; + * these params need to be exposed in wifi.radio.<intf>. + */ + p->transmit = radio->tx_utilization; + p->receive_self = radio->rx_utilization; + p->receive_other = radio->other_utilization; + + return p; +} + +struct tlv_ap_metrics *agent_gen_ap_metrics(struct agent *a, + int radio_index, int bss_index) +{ + struct tlv_ap_metrics *p; + struct wifi_radio_element *radio = a->radios + radio_index; + struct wifi_bss_element *bss = radio->bsslist + bss_index; + + p = (struct tlv_ap_metrics *)calloc(1, sizeof(*p)); + if (!p) + return NULL; + + p->tlv_type = MAP_TLV_AP_METRICS; + memcpy(p->bssid, bss->bssid, 6); + if (bss->is_ac_be) { + p->is_ac_be = bss->is_ac_be; + memcpy(p->service_param_info_be, bss->est_wmm_be, 3); + } + if (bss->is_ac_bk) { + p->is_ac_bk = bss->is_ac_bk; + memcpy(p->service_param_info_bk, bss->est_wmm_bk, 3); + } + if (bss->is_ac_vo) { + p->is_ac_vo = bss->is_ac_vo; + memcpy(p->service_param_info_vo, bss->est_wmm_vo, 3); + } + if (bss->is_ac_vi) { + p->is_ac_vi = bss->is_ac_vi; + memcpy(p->service_param_info_vi, bss->est_wmm_vi, 3); + } + + return p; +} + +struct tlv_ap_ext_metrics *agent_gen_ap_ext_metrics(struct agent *a, + int radio_index, int bss_index) +{ + struct tlv_ap_ext_metrics *p; + struct wifi_radio_element *radio = a->radios + radio_index; + struct wifi_bss_element *bss = radio->bsslist + bss_index; + + p = (struct tlv_ap_ext_metrics *)calloc(1, sizeof(*p)); + if (!p) + return NULL; + + p->tlv_type = MAP_TLV_AP_EXTENDED_METRICS; + memcpy(p->bssid, bss->bssid, 6); + p->uni_bytes_sent = bss->tx_ucast_bytes; + p->uni_bytes_recv = bss->rx_ucast_bytes; + p->multi_bytes_sent = bss->tx_mcast_bytes; + p->multi_bytes_recv = bss->rx_mcast_bytes; + p->bro_bytes_sent = bss->tx_bcast_bytes; + p->bro_bytes_recv = bss->rx_bcast_bytes; + + return p; +} + +struct tlv_assoc_sta_traffic_stats *agent_gen_assoc_sta_traffic_stats( + struct agent *a, struct sta *s) +{ + struct tlv_assoc_sta_traffic_stats *p; + + p = (struct tlv_assoc_sta_traffic_stats *)calloc(1, sizeof(*p)); + if (!p) + return NULL; + + p->tlv_type = MAP_TLV_ASSOCIATED_STA_TRAFFIC_STATS; + memcpy(p->addr, s->macaddr, 6); + p->bytes_sent = s->tx_bytes; + p->bytes_received = s->rx_bytes; + p->packets_sent = s->tx_pkts; + p->packets_received = s->rx_pkts; + p->tx_packets_err = s->tx_fail_pkts; + p->rx_packets_err = s->rx_fail_pkts; + + return p; +} + +struct tlv_assoc_sta_link_metrics *agent_gen_assoc_sta_link_metrics( + struct agent *a, struct sta *s, uint8_t *bssid) +{ + int i; + struct tlv_assoc_sta_link_metrics *p; + + p = (struct tlv_assoc_sta_link_metrics *)calloc(1, sizeof(*p)); + if (!p) + return NULL; + + p->tlv_type = MAP_TLV_ASSOCIATED_STA_LINK_METRICS; + memcpy(p->addr, s->macaddr, 6); + /* Reported BSS for specific STA */ + p->bssid_nr = 1; + p->sta_link_metrics_bssid = calloc(p->bssid_nr, sizeof(*p->sta_link_metrics_bssid)); + if (p->sta_link_metrics_bssid) { + for (i = 0; i < p->bssid_nr; i++) { + memcpy(p->sta_link_metrics_bssid[i].bssid, bssid, 6); + p->sta_link_metrics_bssid[i].time_delta = s->connected_ms; + p->sta_link_metrics_bssid[i].dl_mac_data_rate = s->rx_thput; + p->sta_link_metrics_bssid[i].ul_mac_data_rate = s->tx_thput; + p->sta_link_metrics_bssid[i].ul_rcpi = s->rssi[0]; + } + } + + return p; +} + +struct tlv_assoc_sta_ext_link_metric *agent_gen_assoc_sta_ext_link_metric( + struct agent *a, struct sta *s, uint8_t *bssid) +{ + int i; + struct tlv_assoc_sta_ext_link_metric *p; + + p = (struct tlv_assoc_sta_ext_link_metric *)calloc(1, sizeof(*p)); + if (!p) + return NULL; + + p->tlv_type = MAP_TLV_ASSOCIATED_STA_EXT_LINK_METRICS; + memcpy(p->mac, s->macaddr, 6); + /* Reported BSS for specific STA */ + p->nbr_bssid = 1; + p->data = calloc(p->nbr_bssid, sizeof(*p->data)); + if (p->data) { + for (i = 0; i < p->nbr_bssid; i++) { + memcpy(p->data[i].bssid, bssid, 6); + p->data[i].last_data_dwn_rate = s->rx_rate; + p->data[i].last_data_up_rate = s->tx_rate; + /* + * TODO/FIXME: + * Need to add parameters for uti_recv, uti_transmit in + * wifi.ap.<intf> stations UBUS method. + */ + p->data[i].uti_recv = 101; + p->data[i].uti_transmit = 101; + } + } + + return p; +} diff --git a/src/core/agent_tlv_generator.h b/src/core/agent_tlv_generator.h index 68112982ff333f8c2d8598b37b7f5d38cd3d3dc4..d6e67cd3426d1194d09a68b397c1fda186c337d2 100644 --- a/src/core/agent_tlv_generator.h +++ b/src/core/agent_tlv_generator.h @@ -48,4 +48,16 @@ struct tlv_autoconf_freq_band *agent_gen_autoconf_freq_band(struct agent *a, uint8_t band); struct tlv_searched_role *agent_gen_searched_role(struct agent *a, uint8_t role); +struct tlv_radio_metrics *agent_gen_radio_metrics(struct agent *a, + int radio_index); +struct tlv_ap_metrics *agent_gen_ap_metrics(struct agent *a, + int radio_index, int bss_index); +struct tlv_ap_ext_metrics *agent_gen_ap_ext_metrics(struct agent *a, + int radio_index, int bss_index); +struct tlv_assoc_sta_traffic_stats *agent_gen_assoc_sta_traffic_stats( + struct agent *a, struct sta *s); +struct tlv_assoc_sta_link_metrics *agent_gen_assoc_sta_link_metrics( + struct agent *a, struct sta *s, uint8_t *bssid); +struct tlv_assoc_sta_ext_link_metric *agent_gen_assoc_sta_ext_link_metric( + struct agent *a, struct sta *s, uint8_t *bssid); #endif