From 55a49b0614fbd0c454c655d72d9bf9825706d77c Mon Sep 17 00:00:00 2001
From: Kamil Zulewski <kamil.zulewski@iopsys.eu>
Date: Thu, 9 Nov 2023 17:15:54 +0100
Subject: [PATCH] skip redundant ap autoconifg wsc based on cmdu md5 sum

---
 src/agent.c       |   8 +++
 src/agent.h       |   2 +
 src/agent_map.c   | 131 +++++++++++++++++++++++++++++++++++++++++++---
 src/utils/utils.c |   1 +
 4 files changed, 135 insertions(+), 7 deletions(-)

diff --git a/src/agent.c b/src/agent.c
index 2c97ce3ec..0a42d9369 100644
--- a/src/agent.c
+++ b/src/agent.c
@@ -5047,6 +5047,7 @@ static void _enumerate_wifi_objects(struct ubus_request *req, int type,
 		enum autocfg_state state;
 		uint16_t mid;
 		uint16_t wsc_mid;
+		uint8_t autconfig_wsc_md5sum[MD5_SUM_LENGTH];
 	} apcfg_state[WIFI_DEVICE_MAX_NUM] = {0};
 	struct agent *a = (struct agent *)req->priv;
 	struct json_object *json_msg;
@@ -5079,6 +5080,10 @@ static void _enumerate_wifi_objects(struct ubus_request *req, int type,
 			apcfg_state[i].state = a->radios[i].state;
 			apcfg_state[i].mid = a->radios[i].mid;
 			apcfg_state[i].wsc_mid = a->radios[i].wsc_mid;
+			apcfg_state[i].wsc_mid = a->radios[i].wsc_mid;
+			memcpy(apcfg_state[i].autconfig_wsc_md5sum,
+			       a->radios[i].autconfig_wsc_md5sum,
+			       MD5_SUM_LENGTH);
 		}
 		prev_len = a->num_radios;
 		/* clears radio apcfg states */
@@ -5119,6 +5124,9 @@ static void _enumerate_wifi_objects(struct ubus_request *req, int type,
 				a->radios[i].state = apcfg_state[j].state;
 				a->radios[i].mid = apcfg_state[j].mid;
 				a->radios[i].wsc_mid = apcfg_state[j].wsc_mid;
+				memcpy(a->radios[i].autconfig_wsc_md5sum,
+				       apcfg_state[i].autconfig_wsc_md5sum,
+				       MD5_SUM_LENGTH);
 			}
 		}
 
diff --git a/src/agent.h b/src/agent.h
index 31eb2092d..89b707e50 100644
--- a/src/agent.h
+++ b/src/agent.h
@@ -530,6 +530,7 @@ struct wifi_cac_request {
 	uint8_t report_failed_status;
 };
 
+# define MD5_SUM_LENGTH 16
 struct wifi_radio_element {
 	char name[16];
 	uint8_t macaddr[6];
@@ -600,6 +601,7 @@ struct wifi_radio_element {
 	uint16_t mid;
 	uint16_t wsc_mid;
 	uint16_t renew_mid; /* debug purposes */
+	uint8_t autconfig_wsc_md5sum[MD5_SUM_LENGTH];
 
 #if (EASYMESH_VERSION > 2)
 	bool dpp_onboarded;
diff --git a/src/agent_map.c b/src/agent_map.c
index da683d5e7..e28373be9 100644
--- a/src/agent_map.c
+++ b/src/agent_map.c
@@ -37,6 +37,7 @@
 #include <json-c/json.h>
 #include <libubox/blobmsg.h>
 #include <libubox/blobmsg_json.h>
+#include <libubox/md5.h>
 #include <libubox/uloop.h>
 #include <libubox/ustream.h>
 #include <libubox/utils.h>
@@ -1954,7 +1955,105 @@ bool agent_is_ts_enabled(struct agent *a)
 	return true;
 }
 
+static bool has_ap_autoconfig_wsc_changed(struct agent *a,
+					 struct tlv *tlvs[][TLV_MAXNUM],
+					 size_t tlvs_size, uint8_t *md5sum_out)
+{
+	enum {
+		AP_RADIO_ID = 0,
+		WSC = 1,
+		DEFAULT_8021Q_SETTINGS = 2,
+		TRAFFIC_SEPARATION_POLICY = 3,
+
+		MAX_TLV_TYPES = 4
+	};
+
+	uint8_t new_md5sum[MD5_SUM_LENGTH];
+	const struct wifi_radio_element *radio;
+	uint8_t bssid[6];
+	md5_ctx_t ctx;
+	int i;
+
+	if (tlvs_size != MAX_TLV_TYPES) {
+		err("%s: unsupported version of CMDU.\n", __func__);
+		return true;
+	}
+
+	memcpy(bssid, tlvs[AP_RADIO_ID][0]->data, 6);
+	radio = wifi_get_radio_by_mac(a, bssid);
+	if (!radio) {
+		err("%s: Unknown radio.\n", __func__);
+		return true;
+	}
+
+	/* Calculate MD5 hash from all TLVs data */
+	md5_begin(&ctx);
+	md5_hash(tlvs[AP_RADIO_ID][0]->data, tlv_length(tlvs[AP_RADIO_ID][0]), &ctx);
+
+
+	/* Use decrypted data of WSC TLVs */
+	i = 0;
+	while (i < TLV_MAXNUM && tlvs[WSC][i]) {
+		struct wps_credential wps_out = { 0 };
+		uint8_t *ext_out = NULL;
+		uint16_t ext_len = 0;
+		int ret = 0;
+		uint16_t m2_len = tlv_length(tlvs[WSC][i]);
+		/* It's workaround as wsc_process_m2 modifies m2 buffer */
+		uint8_t *m2_tmp = malloc(m2_len);
+
+		if (!m2_tmp)
+			return true;
+
+		memcpy(m2_tmp, tlvs[WSC][i]->data, m2_len);
+
+		ret = wsc_process_m2(radio->autconfig.m1_frame,
+				     radio->autconfig.m1_size,
+				     radio->autconfig.key, m2_tmp, m2_len,
+				     &wps_out, &ext_out, &ext_len);
+
+		free(m2_tmp);
+
+		if (ret)
+			return true;
+
+		md5_hash(&wps_out, sizeof(wps_out), &ctx);
+		md5_hash(ext_out, ext_len, &ctx);
+
+		free(ext_out);
+
+		++i;
+	}
+
+
+	if (tlvs[DEFAULT_8021Q_SETTINGS][0]) {
+		md5_hash(tlvs[DEFAULT_8021Q_SETTINGS][0]->data,
+			 tlv_length(tlvs[DEFAULT_8021Q_SETTINGS][0]), &ctx);
+	}
+
+	if (tlvs[TRAFFIC_SEPARATION_POLICY][0]) {
+		md5_hash(tlvs[TRAFFIC_SEPARATION_POLICY][0]->data,
+			 tlv_length(tlvs[TRAFFIC_SEPARATION_POLICY][0]), &ctx);
+	}
+
+	md5_end(new_md5sum, &ctx);
+
+	dbg("%s: current md5sum: %02x%02x%02x%02x%02x%02x...\n", __func__,
+	    radio->autconfig_wsc_md5sum[0], radio->autconfig_wsc_md5sum[1],
+	    radio->autconfig_wsc_md5sum[2], radio->autconfig_wsc_md5sum[3],
+	    radio->autconfig_wsc_md5sum[4], radio->autconfig_wsc_md5sum[5]);
+
+	dbg("%s: new md5sum:     %02x%02x%02x%02x%02x%02x...\n", __func__,
+	    new_md5sum[0], new_md5sum[1], new_md5sum[2], new_md5sum[3],
+	    new_md5sum[4], new_md5sum[5]);
+
+	memcpy(md5sum_out, new_md5sum, MD5_SUM_LENGTH);
+
+	return memcmp(radio->autconfig_wsc_md5sum, new_md5sum, MD5_SUM_LENGTH);
+}
+
 #define RELOAD_TIMEOUT 5
+#define AP_AUTOCONFIG_TLVS_SIZE 4
 
 int handle_ap_autoconfig_wsc(void *agent, struct cmdu_buff *rx_cmdu,
 			     struct node *n)
@@ -1962,8 +2061,9 @@ int handle_ap_autoconfig_wsc(void *agent, struct cmdu_buff *rx_cmdu,
 	struct agent *a = (struct agent *) agent;
 	uint8_t bssid[6];
 	struct wifi_radio_element *radio;
-	struct tlv *tv[4][TLV_MAXNUM] = {0};
+	struct tlv *tv[AP_AUTOCONFIG_TLVS_SIZE][TLV_MAXNUM] = {0};
 	int ret = 0, num = 0;
+	uint8_t new_md5sum[MD5_SUM_LENGTH] = {0};
 
 	trace("%s: --->\n", __func__);
 
@@ -1978,12 +2078,6 @@ int handle_ap_autoconfig_wsc(void *agent, struct cmdu_buff *rx_cmdu,
 		return -1;
 	}
 
-	if (tv[2][0]) {
-		struct tlv_default_8021q_settings *tlv = (struct tlv_default_8021q_settings *) tv[2][0]->data;
-		//uci_apply_default_8021q_settings(tlv);
-		agent_fill_8021q_setting_from_tlv(a, tlv);
-	}
-
 	memcpy(bssid, tv[0][0]->data, 6);
 
 	radio = wifi_get_radio_by_mac(a, bssid);
@@ -1992,6 +2086,24 @@ int handle_ap_autoconfig_wsc(void *agent, struct cmdu_buff *rx_cmdu,
 
 	dbg("|%s:%d| found radio = %s\n", __func__, __LINE__, radio->name);
 
+	if (!has_ap_autoconfig_wsc_changed(a, tv, AP_AUTOCONFIG_TLVS_SIZE, new_md5sum)) {
+		info("%s: The same config received, skip radio configuration for: " MACFMT "\n",
+		     __func__, MAC2STR(radio->macaddr));
+
+		radio->state = AUTOCFG_HEARTBEAT;
+		goto teardown;
+	}
+
+	dbg("%s: New config received for radio: " MACFMT"\n",
+		     __func__, MAC2STR(radio->macaddr));
+
+	if (tv[2][0]) {
+		struct tlv_default_8021q_settings *tlv = (struct tlv_default_8021q_settings *) tv[2][0]->data;
+		//uci_apply_default_8021q_settings(tlv);
+		agent_fill_8021q_setting_from_tlv(a, tlv);
+	}
+
+
 	if (radio->dedicated_backhaul) {
 		dbg("|%s:%d| %s is dedicated backhaul"\
 				" radio found - discard WSC and set to"\
@@ -2131,6 +2243,11 @@ int handle_ap_autoconfig_wsc(void *agent, struct cmdu_buff *rx_cmdu,
 	radio->state = AUTOCFG_HEARTBEAT;
 	agent_autoconfig_event(a, radio->name, "success", "completed");
 
+	/* Save successfully processed autoconfig wsc md5sum */
+	memcpy(radio->autconfig_wsc_md5sum, new_md5sum, MD5_SUM_LENGTH);
+	dbg("%s: Applied autoconfig md5sum: %02x%02x%02x%02x%02x%02x...\n", __func__,
+	    new_md5sum[0], new_md5sum[1], new_md5sum[2], new_md5sum[3], new_md5sum[4], new_md5sum[5]);
+
 exit:
 	a->reconfig_reason |= AGENT_RECONFIG_REASON_AP_AUTOCONF; /* ap autoconfig bit */
 	timer_set(&a->reload_scheduler, RELOAD_TIMEOUT * 1000);
diff --git a/src/utils/utils.c b/src/utils/utils.c
index e92350740..eb80a74d0 100644
--- a/src/utils/utils.c
+++ b/src/utils/utils.c
@@ -624,3 +624,4 @@ bool is_vid_valid(unsigned int vid)
 {
         return (vid < TS_VID_INVALID) && (vid > 0);
 }
+
-- 
GitLab