From 6609836dfdb95677da4a40b61c4cf4cb57df2bd1 Mon Sep 17 00:00:00 2001
From: Filip Matusiak <filip.matusiak@iopsys.eu>
Date: Thu, 19 May 2022 14:51:46 +0200
Subject: [PATCH] Validate bss metrics

---
 src/cmdu_validate.c | 224 +++++++++++++++++++++++++++++++++++++++++++-
 src/cmdu_validate.h |   1 +
 src/cntlr_map.c     |  16 +---
 3 files changed, 228 insertions(+), 13 deletions(-)

diff --git a/src/cmdu_validate.c b/src/cmdu_validate.c
index 2fb70d2b..4aff49a2 100644
--- a/src/cmdu_validate.c
+++ b/src/cmdu_validate.c
@@ -143,6 +143,228 @@ static int check_searched_service_tlv(struct tlv *t)
 	return check_service_tlv(t);
 }
 
+/* Check AP Metrics TLV */
+static int check_ap_metrics_tlv(struct tlv *t)
+{
+	uint16_t tlv_len = 0;
+	struct tlv_ap_metrics *tlv;
+	int offset = 0;
+
+	tlv_len = tlv_length(t);
+	if (!tlv_len)
+		return -1;
+
+	tlv = (struct tlv_ap_metrics *) t->data;
+	if (!tlv)
+		return -1;
+
+	/* bssid(6), channel_utilization(1), num_station(1), esp_ac(1), esp_be(3) */
+	if (offset + sizeof(*tlv) > tlv_len)
+		return -1;
+
+	offset += sizeof(*tlv);
+
+	/* esp_be */
+	if (!(tlv->esp_ac & ESP_AC_BE))
+		return -1;
+
+	/* esp[] */
+	if (tlv->esp_ac & ESP_AC_BK)
+			offset += 3;
+	if (tlv->esp_ac & ESP_AC_VO)
+			offset += 3;
+	if (tlv->esp_ac & ESP_AC_VI)
+			offset += 3;
+
+	if (offset > tlv_len)
+		return -1;
+
+	return 0;
+}
+
+/* Check Associated STA Traffic Stats TLV */
+static int check_assoc_sta_traffic_stats_tlv(struct tlv *t)
+{
+	return check_serialized_tlv(t,
+		sizeof(struct tlv_assoc_sta_traffic_stats));
+}
+
+/* Check Associated STA Link Metrics TLV */
+static int check_assoc_sta_link_metrics_tlv(struct tlv *tlv)
+{
+	int offset = 0;
+	uint8_t num_bss;
+	uint8_t *tv_data;
+	uint16_t tlv_len;
+
+	if (!tlv)
+		return -1;
+
+	tlv_len = tlv_length(tlv);
+	if (tlv_len < sizeof(struct tlv_assoc_sta_link_metrics))
+		return -1;
+
+	tv_data = (uint8_t *)tlv->data;
+	if (!tv_data)
+		return -1;
+
+	offset += 6;	/* bssid */
+	num_bss = tv_data[offset++];
+
+	if (offset + num_bss * sizeof(struct assoc_sta_link_metrics_bss) > tlv_len)
+		return -1;
+
+	return 0;
+}
+
+/* Check AP Extended Metrics TLV */
+static int check_ap_ext_metrics_tlv(struct tlv *t)
+{
+	return check_serialized_tlv(t,
+		sizeof(struct tlv_ap_ext_metrics));
+}
+
+/* Check Radio Metrics TLV */
+static int check_radio_metrics_tlv(struct tlv *t)
+{
+	return check_serialized_tlv(t,
+		sizeof(struct tlv_radio_metrics));
+}
+
+/* Check Associated STA Extended Link Metrics TLV */
+static int check_assoc_sta_ext_link_metrics_tlv(struct tlv *tlv)
+{
+	int offset = 0;
+	uint8_t num_bss;
+	uint8_t *tv_data;
+	uint16_t tlv_len;
+
+	if (!tlv)
+		return -1;
+
+	tlv_len = tlv_length(tlv);
+	if (tlv_len < sizeof(struct tlv_sta_ext_link_metric))
+		return -1;
+
+	tv_data = (uint8_t *)tlv->data;
+	if (!tv_data)
+		return -1;
+
+	offset += 6;	/* bssid */
+	num_bss = tv_data[offset++];
+
+	if (offset + num_bss * sizeof(struct sta_ext_link_metric_bss) > tlv_len)
+		return -1;
+
+	return 0;
+}
+
+bool validate_ap_metrics_response(struct cmdu_buff *cmdu, struct tlv *tv[][16])
+{
+	int ret = 0;
+	int idx;
+	struct tlv_policy metric_policy[] = {
+		[0] = {
+			.type = MAP_TLV_AP_METRICS,
+			.present = TLV_PRESENT_MORE,
+			.minlen = 13
+		},
+		[1] = {
+			.type = MAP_TLV_ASSOCIATED_STA_TRAFFIC_STATS,
+			.present = TLV_PRESENT_OPTIONAL_MORE,
+			.minlen = 34,
+			.maxlen = 34
+		},
+		[2] = {
+			.type = MAP_TLV_ASSOCIATED_STA_LINK_METRICS,
+			.present = TLV_PRESENT_OPTIONAL_MORE,
+			.minlen = 7
+		},
+		[3] = {
+			.type = MAP_TLV_AP_EXTENDED_METRICS,
+			.present = TLV_PRESENT_MORE,
+			.minlen = 30,
+			.maxlen = 30
+		},
+		[4] = {
+			.type = MAP_TLV_RADIO_METRICS,
+			.present = TLV_PRESENT_OPTIONAL_MORE,
+			.minlen = 10,
+			.maxlen = 10
+		},
+		[5] = {
+			.type = MAP_TLV_ASSOCIATED_STA_EXT_LINK_METRICS,
+			.present = TLV_PRESENT_OPTIONAL_MORE,
+			.minlen = 7
+		}
+	};
+
+	trace("%s |" MACFMT "|CMDU: ap metrics response\n",
+		  __func__, MAC2STR(cmdu->origin));
+
+	/* Parsing AP Metrics TLV */
+	ret = cmdu_parse_tlvs(cmdu, tv, metric_policy, 6);
+	if (ret) {
+		dbg("%s: parse_tlv failed\n", __func__);
+		return false;
+	}
+
+	if (!tv[0][0] || !tv[3][0]) {
+		dbg("%s: Missing one or more mandatory TLV!\n", __func__);
+		return false;
+	}
+
+	idx = 0;
+	while (tv[0][idx]) {
+		/* Parse AP Metrics TLV */
+		if (check_ap_metrics_tlv(tv[0][idx]))
+			return false;
+		idx++;
+	}
+
+	idx = 0;
+	while (tv[1][idx]) {
+		/* Parse Associated STA Traffic Stats TLV */
+		if (check_assoc_sta_traffic_stats_tlv(tv[1][idx]))
+			return false;
+		idx++;
+	}
+
+	idx = 0;
+	while (tv[2][idx]) {
+		/* Parse Associated STA Link Metrics TLV */
+		if (check_assoc_sta_link_metrics_tlv(tv[2][idx]))
+			return false;
+		idx++;
+	}
+
+	idx = 0;
+	while (tv[3][idx]) {
+		/* Parse AP Extended Metrics TLV */
+		if (check_ap_ext_metrics_tlv(tv[3][idx]))
+			return false;
+		idx++;
+	}
+
+	idx = 0;
+	while (tv[4][idx]) {
+		/* Parse Radio Metrics TLV */
+		if (check_radio_metrics_tlv(tv[4][idx]))
+			return false;
+		idx++;
+	}
+
+	idx = 0;
+	while (tv[5][idx]) {
+		/* Parse Associated STA Extended Link Metrics TLV */
+		if (check_assoc_sta_ext_link_metrics_tlv(tv[5][idx]))
+			return false;
+		idx++;
+	}
+
+	return true;
+}
+
 bool validate_channel_scan_report(struct cmdu_buff *cmdu, struct tlv *tv_tsp[][16],
 		struct tlv *tv_scan[], int *num)
 {
@@ -811,7 +1033,7 @@ bool validate_ap_autoconfig_response(struct cmdu_buff *cmdu, struct tlv *tv[][16
 	if (check_map_profile_tlv(tv[2][0]))
 		return false;
 
-    /* Parse SupportedFreqBand TLV */
+	/* Parse SupportedFreqBand TLV */
 	if (check_supported_band_tlv(tv[3][0]))
 		return false;
 
diff --git a/src/cmdu_validate.h b/src/cmdu_validate.h
index d173b6da..509407a7 100644
--- a/src/cmdu_validate.h
+++ b/src/cmdu_validate.h
@@ -3,6 +3,7 @@
 #ifndef CMDU_VALIDATE
 #define CMDU_VALIDATE
 
+bool validate_ap_metrics_response(struct cmdu_buff *cmdu, struct tlv *tv[][16]);
 bool validate_channel_scan_report(struct cmdu_buff *cmdu, struct tlv *tv_tsp[][16],
 		struct tlv *tv_scan[], int *num);
 bool validate_topology_response(struct cmdu_buff *cmdu, struct tlv *tv_tsp[][16]);
diff --git a/src/cntlr_map.c b/src/cntlr_map.c
index dfc8a39d..cfccce0d 100644
--- a/src/cntlr_map.c
+++ b/src/cntlr_map.c
@@ -1128,14 +1128,6 @@ int handle_ap_metrics_response(void *cntlr, struct cmdu_buff *cmdu)
 	struct wifi_bss_element *b;
 	//struct link_metrics *link;
 	struct tlv *tv[6][16] = { 0 };
-	struct tlv_policy metric_policy[] = {
-		[0] = { .type = MAP_TLV_AP_METRICS, .present = TLV_PRESENT_MORE },
-		[1] = { .type = MAP_TLV_ASSOCIATED_STA_TRAFFIC_STATS, .present = TLV_PRESENT_OPTIONAL_MORE },
-		[2] = { .type = MAP_TLV_ASSOCIATED_STA_LINK_METRICS, .present = TLV_PRESENT_OPTIONAL_MORE },
-		[3] = { .type = MAP_TLV_AP_EXTENDED_METRICS, .present = TLV_PRESENT_MORE },
-		[4] = { .type = MAP_TLV_RADIO_METRICS, .present = TLV_PRESENT_OPTIONAL_MORE },
-		[5] = { .type = MAP_TLV_ASSOCIATED_STA_EXT_LINK_METRICS, .present = TLV_PRESENT_OPTIONAL_MORE }
-	};
 	struct tlv_ap_metrics *p;
 
 	if (!cmdu) {
@@ -1145,10 +1137,10 @@ int handle_ap_metrics_response(void *cntlr, struct cmdu_buff *cmdu)
 
 	trace("Storing AP metrics of |" MACFMT "|\n", MAC2STR(cmdu->origin));
 
-	/* TODO: validate frame */
-
-	/* Parsing AP Metrics TLV */
-	cmdu_parse_tlvs(cmdu, tv, metric_policy, 6);
+	if (!validate_ap_metrics_response(cmdu, tv)) {
+		dbg("cmdu validation: [AP_METRICS_RESPONSE] failed\n");
+		return -1;
+	}
 
 	/* Storing AP Metrics TLV */
 	idx = 0;
-- 
GitLab