From 43c64d73cb6001904664448c760c54a8a1f96721 Mon Sep 17 00:00:00 2001 From: Filip Matusiak <filip.matusiak@iopsys.eu> Date: Fri, 30 May 2025 11:52:41 +0200 Subject: [PATCH 1/5] wsc: Move crypto and autoconfig utilities to separate files --- src/Makefile | 2 + src/agent.c | 1 + src/agent.h | 16 +--- src/agent_map.c | 205 +++++------------------------------------- src/autoconfig.c | 162 +++++++++++++++++++++++++++++++++ src/autoconfig.h | 29 ++++++ src/utils/libcrypto.c | 42 +++++++++ src/utils/libcrypto.h | 16 ++++ 8 files changed, 275 insertions(+), 198 deletions(-) create mode 100644 src/autoconfig.c create mode 100644 src/autoconfig.h create mode 100644 src/utils/libcrypto.c create mode 100644 src/utils/libcrypto.h diff --git a/src/Makefile b/src/Makefile index 86b626e99..9ed290e43 100644 --- a/src/Makefile +++ b/src/Makefile @@ -18,6 +18,7 @@ endif OBJS = \ utils/debug.o \ + utils/libcrypto.o \ utils/liblist.o \ utils/utils.o \ utils/1905_ubus.o \ @@ -38,6 +39,7 @@ AGENT_OBJS = \ agent_tlv.o \ agent_cmdu.o \ assoc_ctrl.o \ + autoconfig.o \ backhaul.o \ backhaul_blacklist.o \ config.o \ diff --git a/src/agent.c b/src/agent.c index 7568bf8da..47c5aa7bb 100644 --- a/src/agent.c +++ b/src/agent.c @@ -48,6 +48,7 @@ #include "agent_ubus.h" #include "agent_ubus_dbg.h" #include "assoc_ctrl.h" +#include "autoconfig.h" #include "backhaul.h" #include "backhaul_blacklist.h" #include "config.h" diff --git a/src/agent.h b/src/agent.h index 3c70685d4..c66ac62b8 100644 --- a/src/agent.h +++ b/src/agent.h @@ -26,6 +26,8 @@ #include "utils/allmac.h" #include "utils/debug.h" +#include "utils/libcrypto.h" +#include "autoconfig.h" #include "config.h" #include "nl.h" #include "wifi.h" @@ -606,18 +608,6 @@ struct wsc_ext { struct wsc_vendor_ie ven_ies[VEN_IES_MAX]; }; -struct wsc_data { - uint8_t *m1_frame; - uint16_t m1_size; - struct wsc_key *key; -}; - -#define HEARTBEAT_AUTOCFG_INTERVAL 70 -enum autocfg_state { - AUTOCFG_HEARTBEAT, - AUTOCFG_ACTIVE -}; - #define SCAN_MAX_CHANNEL 16 struct wifi_scan_request_opclass { uint8_t classid; @@ -686,7 +676,6 @@ struct wifi7_radio_capabilities { }; #endif -#define SHA256_LENGTH 32 struct wifi_radio_element { char name[16]; uint8_t macaddr[6]; @@ -754,7 +743,6 @@ struct wifi_radio_element { uint16_t mid; uint16_t wsc_mid; uint16_t renew_mid; /* debug purposes */ - uint8_t autconfig_wsc_sha256[SHA256_LENGTH]; /* radio scan state */ enum wifi_scan_state scan_state; diff --git a/src/agent_map.c b/src/agent_map.c index 8d0ebc228..a908071a2 100644 --- a/src/agent_map.c +++ b/src/agent_map.c @@ -48,7 +48,7 @@ #include "agent_tlv.h" #include "agent_ubus.h" #include "assoc_ctrl.h" -#include "utils/allmac.h" +#include "autoconfig.h" #include "backhaul.h" #include "backhaul_blacklist.h" #include "config.h" @@ -62,9 +62,13 @@ #include "extension.h" #include "timer_impl.h" #include "unasta.h" -#include "utils/1905_ubus.h" + +#include "utils/allmac.h" #include "utils/debug.h" +#include "utils/1905_ubus.h" +#include "utils/libcrypto.h" #include "utils/utils.h" + #include "wifi.h" #include "wifi_opclass.h" #include "wifi_scanresults.h" @@ -2041,146 +2045,6 @@ 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 *sha256_out) -{ - enum { - AP_RADIO_ID = 0, - WSC = 1, - DEFAULT_8021Q_SETTINGS = 2, - TRAFFIC_SEPARATION_POLICY = 3, -#if (EASYMESH_VERSION >= 6) - AP_MLD_CONFIG = 4, - BACKHAUL_STA_MLD_CONFIG = 5, - MAX_TLV_TYPES = 6 -#else - MAX_TLV_TYPES = 4 -#endif - }; - - uint8_t new_sha256[SHA256_LENGTH]; - const struct wifi_radio_element *radio; - uint8_t bssid[6]; - EVP_MD_CTX *ctx; - int i; - bool ret = true; - - 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 = agent_get_radio(a, bssid); - if (!radio) { - err("%s: Unknown radio.\n", __func__); - return true; - } - - /* Calculate SHA-256 hash from all TLVs data */ - ctx = EVP_MD_CTX_new(); - if (!ctx) - return true; - - if (!EVP_DigestInit_ex(ctx, EVP_sha256(), NULL)) - goto error_cleanup; - - if (!EVP_DigestUpdate(ctx, tlvs[AP_RADIO_ID][0]->data, tlv_length(tlvs[AP_RADIO_ID][0]))) - goto error_cleanup; - - /* 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) - goto error_cleanup; - - 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) { - ret = false; - goto error_cleanup; - } - - if (!EVP_DigestUpdate(ctx, &wps_out, sizeof(wps_out))) { - free(ext_out); - goto error_cleanup; - } - - if (ext_out && !EVP_DigestUpdate(ctx, ext_out, ext_len)) { - free(ext_out); - goto error_cleanup; - } - - free(ext_out); - - ++i; - } - - if (tlvs[DEFAULT_8021Q_SETTINGS][0] && - !EVP_DigestUpdate(ctx, tlvs[DEFAULT_8021Q_SETTINGS][0]->data, - tlv_length(tlvs[DEFAULT_8021Q_SETTINGS][0]))) { - goto error_cleanup; - } - - if (tlvs[TRAFFIC_SEPARATION_POLICY][0] && - !EVP_DigestUpdate(ctx, tlvs[TRAFFIC_SEPARATION_POLICY][0]->data, - tlv_length(tlvs[TRAFFIC_SEPARATION_POLICY][0]))) { - goto error_cleanup; - } - -#if (EASYMESH_VERSION >= 6) - if (tlvs[AP_MLD_CONFIG][0] && - !EVP_DigestUpdate(ctx, tlvs[AP_MLD_CONFIG][0]->data, - tlv_length(tlvs[AP_MLD_CONFIG][0]))) { - goto error_cleanup; - } - - if (tlvs[BACKHAUL_STA_MLD_CONFIG][0] && - !EVP_DigestUpdate(ctx, tlvs[BACKHAUL_STA_MLD_CONFIG][0]->data, - tlv_length(tlvs[BACKHAUL_STA_MLD_CONFIG][0]))) { - goto error_cleanup; - } -#endif - - if (!EVP_DigestFinal_ex(ctx, new_sha256, NULL)) - goto error_cleanup; - - EVP_MD_CTX_free(ctx); - - dbg("%s: current SHA256: %02x%02x%02x%02x%02x%02x...\n", __func__, - radio->autconfig_wsc_sha256[0], radio->autconfig_wsc_sha256[1], - radio->autconfig_wsc_sha256[2], radio->autconfig_wsc_sha256[3], - radio->autconfig_wsc_sha256[4], radio->autconfig_wsc_sha256[5]); - - dbg("%s: new SHA256: %02x%02x%02x%02x%02x%02x...\n", __func__, - new_sha256[0], new_sha256[1], new_sha256[2], new_sha256[3], - new_sha256[4], new_sha256[5]); - - memcpy(sha256_out, new_sha256, SHA256_LENGTH); - - return memcmp(radio->autconfig_wsc_sha256, new_sha256, SHA256_LENGTH); - -error_cleanup: - EVP_MD_CTX_free(ctx); - return ret; -} - #define RELOAD_TIMEOUT 5 #if (EASYMESH_VERSION >= 6) static void mlo_update_id_in_configs(char *ifname, uint8_t mld_id) @@ -2519,37 +2383,6 @@ static int mlo_process_bsta_mld_config(struct agent *a, uint8_t *tlv_data) } #endif -#if (EASYMESH_VERSION >= 6) -static bool update_tlv_hash(struct agent *a, struct tlv *tv, - uint8_t *sha256_out) -{ - bool ret = false; - uint8_t new_sha256[SHA256_LENGTH] = {0}; - EVP_MD_CTX *ctx; - - ctx = EVP_MD_CTX_new(); - if (!ctx) - return true; - - if (!EVP_DigestInit_ex(ctx, EVP_sha256(), NULL)) - goto error_cleanup; - - if (!EVP_DigestUpdate(ctx, tv->data, tlv_length(tv))) - goto error_cleanup; - - if (!EVP_DigestFinal_ex(ctx, new_sha256, NULL)) - goto error_cleanup; - - if (memcmp(sha256_out, new_sha256, SHA256_LENGTH)) { - memcpy(sha256_out, new_sha256, SHA256_LENGTH); - ret = true; - } - -error_cleanup: - EVP_MD_CTX_free(ctx); - return ret; -} -#endif int handle_ap_autoconfig_wsc(void *agent, struct cmdu_buff *rx_cmdu, struct node *n) { @@ -2592,9 +2425,9 @@ int handle_ap_autoconfig_wsc(void *agent, struct cmdu_buff *rx_cmdu, return -1; dbg("|%s:%d| found radio = %s\n", __func__, __LINE__, radio->name); - - if (!has_ap_autoconfig_wsc_changed(a, tv, - AP_AUTOCONFIGURATION_WSC_M2_NUM_OF_TLV_TYPES, new_sha256)) { + if (!autoconfig_has_wsc_changed(&radio->autconfig, tv, + AP_AUTOCONFIGURATION_WSC_M2_NUM_OF_TLV_TYPES, + new_sha256)) { info("%s: The same config received, skip radio configuration for: " MACFMT "\n", __func__, MAC2STR(radio->macaddr)); @@ -2650,7 +2483,7 @@ int handle_ap_autoconfig_wsc(void *agent, struct cmdu_buff *rx_cmdu, */ agent_autoconfig_event(a, radio->name, "teardown", "M2 process failure"); - memset(radio->autconfig_wsc_sha256, 0, SHA256_LENGTH); + autoconfig_clean_wsc_hash(&radio->autconfig); return -1; } @@ -2668,7 +2501,7 @@ int handle_ap_autoconfig_wsc(void *agent, struct cmdu_buff *rx_cmdu, "IOPSYS extension process error"); if (ext) free(ext); - memset(radio->autconfig_wsc_sha256, 0, SHA256_LENGTH); + autoconfig_clean_wsc_hash(&radio->autconfig); return -1; } @@ -2797,7 +2630,8 @@ int handle_ap_autoconfig_wsc(void *agent, struct cmdu_buff *rx_cmdu, if (tv[AP_AUTOCONFIGURATION_WSC_M2_AP_MLD_CONFIG_IDX][0]) { mlo_process_ap_mld_config(a, tv[AP_AUTOCONFIGURATION_WSC_M2_AP_MLD_CONFIG_IDX][0]->data); - update_tlv_hash(a, tv[AP_AUTOCONFIGURATION_WSC_M2_AP_MLD_CONFIG_IDX][0], + update_sha256_hash(tv[AP_AUTOCONFIGURATION_WSC_M2_AP_MLD_CONFIG_IDX][0]->data, + tlv_length(tv[AP_AUTOCONFIGURATION_WSC_M2_AP_MLD_CONFIG_IDX][0]), a->ap_mld_cfg_sha256); a->reconfig_reason |= AGENT_RECONFIG_REASON_MLD_ID; timer_set(&a->reload_scheduler, RELOAD_TIMEOUT * 1000); @@ -2810,8 +2644,9 @@ int handle_ap_autoconfig_wsc(void *agent, struct cmdu_buff *rx_cmdu, tv[AP_AUTOCONFIGURATION_WSC_M2_BACKHAUL_STA_MLD_CONFIG_IDX][0]->data); if (ret) wifi_teardown_map_bsta_mld(a, radio->band); - update_tlv_hash(a, tv[AP_AUTOCONFIGURATION_WSC_M2_BACKHAUL_STA_MLD_CONFIG_IDX][0], - a->bsta_mld_cfg_sha256); + update_sha256_hash(tv[AP_AUTOCONFIGURATION_WSC_M2_BACKHAUL_STA_MLD_CONFIG_IDX][0]->data, + tlv_length(tv[AP_AUTOCONFIGURATION_WSC_M2_BACKHAUL_STA_MLD_CONFIG_IDX][0]), + a->ap_mld_cfg_sha256); a->reconfig_reason |= AGENT_RECONFIG_REASON_MLD_ID; timer_set(&a->reload_scheduler, RELOAD_TIMEOUT * 1000); } else { @@ -2827,7 +2662,7 @@ int handle_ap_autoconfig_wsc(void *agent, struct cmdu_buff *rx_cmdu, agent_autoconfig_event(a, radio->name, "success", "completed"); /* Save successfully processed autoconfig wsc SHA256 */ - memcpy(radio->autconfig_wsc_sha256, new_sha256, SHA256_LENGTH); + autoconfig_update_wsc_hash(&radio->autconfig, new_sha256); dbg("%s: Applied autoconfig SHA256: %02x%02x%02x%02x%02x%02x...\n", __func__, new_sha256[0], new_sha256[1], new_sha256[2], new_sha256[3], new_sha256[4], new_sha256[5]); @@ -6706,7 +6541,8 @@ int handle_ap_mld_config_request(void *agent, struct cmdu_buff *cmdu, struct nod /* Send the 1905 ack message to Controller */ send_1905_acknowledge(a, cmdu->origin, cmdu_get_mid(cmdu), NULL, 0); - if (update_tlv_hash(a, tv[AP_MLD_CONFIG_REQUEST_AP_MLD_CONFIG_IDX][0], + if (update_sha256_hash(tv[AP_MLD_CONFIG_REQUEST_AP_MLD_CONFIG_IDX][0]->data, + tlv_length(tv[AP_MLD_CONFIG_REQUEST_AP_MLD_CONFIG_IDX][0]), a->ap_mld_cfg_sha256)) { /* Proces ReConfiguration */ mlo_process_ap_mld_config(a, tv[AP_MLD_CONFIG_REQUEST_AP_MLD_CONFIG_IDX][0]->data); @@ -6772,7 +6608,8 @@ int handle_bsta_mld_config_request(void *agent, struct cmdu_buff *cmdu, struct n send_1905_acknowledge(a, cmdu->origin, cmdu_get_mid(cmdu), NULL, 0); /* Process TLV only if changed */ - if (update_tlv_hash(a, tv[BSTA_MLD_CONFIG_REQUEST_BSTA_MLD_CONFIG_IDX][0], + if (update_sha256_hash(tv[BSTA_MLD_CONFIG_REQUEST_BSTA_MLD_CONFIG_IDX][0]->data, + tlv_length(tv[BSTA_MLD_CONFIG_REQUEST_BSTA_MLD_CONFIG_IDX][0]), a->bsta_mld_cfg_sha256)) { int ret = 0; diff --git a/src/autoconfig.c b/src/autoconfig.c new file mode 100644 index 000000000..b6babb9b1 --- /dev/null +++ b/src/autoconfig.c @@ -0,0 +1,162 @@ +/* + * autoconfig.c - implements autoconfig logic and util functions + * + * Copyright (C) 2025 IOPSYS Software Solutions AB. All rights reserved. + * + */ + +#include <stdlib.h> +#include <string.h> + +#include <1905_tlvs.h> +#include <i1905_wsc.h> +#include <cmdu.h> +#include <openssl/evp.h> + +#include "autoconfig.h" + +#include "utils/debug.h" + +void autoconfig_clean_wsc_hash(struct wsc_data *data) +{ + memset(data->sha256, 0, SHA256_LENGTH); +} + +void autoconfig_update_wsc_hash(struct wsc_data *data, uint8_t *new_sha256) +{ + memcpy(&data->sha256, new_sha256, SHA256_LENGTH); +} + +bool autoconfig_has_wsc_changed(struct wsc_data *data, + struct tlv *tlvs[][TLV_MAXNUM], + size_t tlvs_size, + uint8_t *sha256_out) +{ + enum { + AP_RADIO_ID = 0, + WSC = 1, + DEFAULT_8021Q_SETTINGS = 2, + TRAFFIC_SEPARATION_POLICY = 3, +#if (EASYMESH_VERSION >= 6) + AP_MLD_CONFIG = 4, + BACKHAUL_STA_MLD_CONFIG = 5, + MAX_TLV_TYPES = 6 +#else + MAX_TLV_TYPES = 4 +#endif + }; + + uint8_t new_sha256[SHA256_LENGTH]; + uint8_t bssid[6]; + EVP_MD_CTX *ctx; + int i; + bool ret = true; + + 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); + + /* Calculate SHA-256 hash from all TLVs data */ + ctx = EVP_MD_CTX_new(); + if (!ctx) + return true; + + if (!EVP_DigestInit_ex(ctx, EVP_sha256(), NULL)) + goto error_cleanup; + + if (!EVP_DigestUpdate(ctx, tlvs[AP_RADIO_ID][0]->data, tlv_length(tlvs[AP_RADIO_ID][0]))) + goto error_cleanup; + + /* 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; + 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) + goto error_cleanup; + + memcpy(m2_tmp, tlvs[WSC][i]->data, m2_len); + + ret = wsc_process_m2(data->m1_frame, + data->m1_size, + data->key, m2_tmp, m2_len, + &wps_out, &ext_out, &ext_len); + + free(m2_tmp); + + if (ret) { + ret = false; + goto error_cleanup; + } + + if (!EVP_DigestUpdate(ctx, &wps_out, sizeof(wps_out))) { + free(ext_out); + goto error_cleanup; + } + + if (ext_out && !EVP_DigestUpdate(ctx, ext_out, ext_len)) { + free(ext_out); + goto error_cleanup; + } + + free(ext_out); + + ++i; + } + + if (tlvs[DEFAULT_8021Q_SETTINGS][0] && + !EVP_DigestUpdate(ctx, tlvs[DEFAULT_8021Q_SETTINGS][0]->data, + tlv_length(tlvs[DEFAULT_8021Q_SETTINGS][0]))) { + goto error_cleanup; + } + + if (tlvs[TRAFFIC_SEPARATION_POLICY][0] && + !EVP_DigestUpdate(ctx, tlvs[TRAFFIC_SEPARATION_POLICY][0]->data, + tlv_length(tlvs[TRAFFIC_SEPARATION_POLICY][0]))) { + goto error_cleanup; + } + +#if (EASYMESH_VERSION >= 6) + if (tlvs[AP_MLD_CONFIG][0] && + !EVP_DigestUpdate(ctx, tlvs[AP_MLD_CONFIG][0]->data, + tlv_length(tlvs[AP_MLD_CONFIG][0]))) { + goto error_cleanup; + } + + if (tlvs[BACKHAUL_STA_MLD_CONFIG][0] && + !EVP_DigestUpdate(ctx, tlvs[BACKHAUL_STA_MLD_CONFIG][0]->data, + tlv_length(tlvs[BACKHAUL_STA_MLD_CONFIG][0]))) { + goto error_cleanup; + } +#endif + + if (!EVP_DigestFinal_ex(ctx, new_sha256, NULL)) + goto error_cleanup; + + EVP_MD_CTX_free(ctx); + + dbg("%s: current SHA256: %02x%02x%02x%02x%02x%02x...\n", __func__, + data->sha256[0], data->sha256[1], + data->sha256[2], data->sha256[3], + data->sha256[4], data->sha256[5]); + + dbg("%s: new SHA256: %02x%02x%02x%02x%02x%02x...\n", __func__, + new_sha256[0], new_sha256[1], new_sha256[2], new_sha256[3], + new_sha256[4], new_sha256[5]); + + memcpy(sha256_out, new_sha256, SHA256_LENGTH); + + return memcmp(data->sha256, new_sha256, SHA256_LENGTH); + +error_cleanup: + EVP_MD_CTX_free(ctx); + return ret; +} diff --git a/src/autoconfig.h b/src/autoconfig.h new file mode 100644 index 000000000..189fe5c37 --- /dev/null +++ b/src/autoconfig.h @@ -0,0 +1,29 @@ +#ifndef AUTOCONFIG_H +#define AUTOCONFIG_H + +#include <stdbool.h> +#include <stdint.h> + +#include "utils/libcrypto.h" + +#define HEARTBEAT_AUTOCFG_INTERVAL 70 + +enum autocfg_state { + AUTOCFG_HEARTBEAT, + AUTOCFG_ACTIVE +}; + +struct wsc_data { + uint8_t *m1_frame; + uint16_t m1_size; + struct wsc_key *key; + uint8_t sha256[SHA256_LENGTH]; +}; + +void autoconfig_clean_wsc_hash(struct wsc_data *wsc); +void autoconfig_update_wsc_hash(struct wsc_data *data, uint8_t *new_sha256); +bool autoconfig_has_wsc_changed(struct wsc_data *wsc, + struct tlv *tlvs[][TLV_MAXNUM], + size_t tlvs_size, + uint8_t *sha256_out); +#endif diff --git a/src/utils/libcrypto.c b/src/utils/libcrypto.c new file mode 100644 index 000000000..3d5c1605e --- /dev/null +++ b/src/utils/libcrypto.c @@ -0,0 +1,42 @@ +/* + * liblcrytpo.c - implements crytpographic function wrappers + * + * Copyright (C) 2025 iopsys Software Solutions AB. All rights reserved. + * + */ + +#include <string.h> +#include <openssl/evp.h> + +#include "libcrypto.h" + +bool update_sha256_hash(void *data, int length, uint8_t *sha256_out) +{ + bool ret = false; + uint8_t new_sha256[SHA256_LENGTH] = {0}; + EVP_MD_CTX *ctx; + + ctx = EVP_MD_CTX_new(); + if (!ctx) + return true; + + if (!EVP_DigestInit_ex(ctx, EVP_sha256(), NULL)) + goto error_cleanup; + + if (!EVP_DigestUpdate(ctx, data, length)) + goto error_cleanup; + + if (!EVP_DigestFinal_ex(ctx, new_sha256, NULL)) + goto error_cleanup; + + if (memcmp(sha256_out, new_sha256, SHA256_LENGTH)) { + memcpy(sha256_out, new_sha256, SHA256_LENGTH); + ret = true; + } + +error_cleanup: + EVP_MD_CTX_free(ctx); + return ret; +} + + diff --git a/src/utils/libcrypto.h b/src/utils/libcrypto.h new file mode 100644 index 000000000..cfabe32d4 --- /dev/null +++ b/src/utils/libcrypto.h @@ -0,0 +1,16 @@ +/* + * libcrypto.h - crypto utility functions header file + * + * Copyright (C) 2025 iopsys Software Solutions AB. All rights reserved. + * + */ + +#ifndef LIBCRYPTO_H +#define LIBCRYPTO_H + +#include <stdbool.h> + +#define SHA256_LENGTH 32 + +bool update_sha256_hash(void *data, int length, uint8_t *sha256_out); +#endif /* LIBCRYPTO_H */ -- GitLab From 92af229c859f8e12ea3f5f574052fc446f5b23bc Mon Sep 17 00:00:00 2001 From: Filip Matusiak <filip.matusiak@iopsys.eu> Date: Wed, 4 Jun 2025 12:52:16 +0200 Subject: [PATCH 2/5] wsc: Add function to write WSC M2 md5sum to file --- src/agent_map.c | 2 + src/autoconfig.c | 145 +++++++++++++++++++++++++++++++++++++++++++++++ src/autoconfig.h | 3 + 3 files changed, 150 insertions(+) diff --git a/src/agent_map.c b/src/agent_map.c index a908071a2..9bdb3d6ee 100644 --- a/src/agent_map.c +++ b/src/agent_map.c @@ -2663,6 +2663,8 @@ int handle_ap_autoconfig_wsc(void *agent, struct cmdu_buff *rx_cmdu, /* Save successfully processed autoconfig wsc SHA256 */ autoconfig_update_wsc_hash(&radio->autconfig, new_sha256); + autoconfig_radio_hash_to_file(radio->macaddr, new_sha256); + dbg("%s: Applied autoconfig SHA256: %02x%02x%02x%02x%02x%02x...\n", __func__, new_sha256[0], new_sha256[1], new_sha256[2], new_sha256[3], new_sha256[4], new_sha256[5]); diff --git a/src/autoconfig.c b/src/autoconfig.c index b6babb9b1..5e8a8e96c 100644 --- a/src/autoconfig.c +++ b/src/autoconfig.c @@ -4,6 +4,8 @@ * Copyright (C) 2025 IOPSYS Software Solutions AB. All rights reserved. * */ +#include <json-c/json.h> +#include <libubox/blobmsg_json.h> #include <stdlib.h> #include <string.h> @@ -160,3 +162,146 @@ error_cleanup: EVP_MD_CTX_free(ctx); return ret; } + +static const char *json_get_string(struct json_object *object, const char *key) +{ + json_bool ret; + struct json_object *value; + + if (!object || !json_object_is_type(object, json_type_object)) + return NULL; + + ret = json_object_object_get_ex(object, key, &value); + if (!ret || !value || !json_object_is_type(value, json_type_string)) + return NULL; + + return json_object_get_string(value); +} + +/* keep the WSC M2 md5sum in non-persistent storage */ +int autoconfig_radio_hash_to_file(uint8_t *macaddr, uint8_t *sha256) +{ + struct json_object *wsc_json; + struct json_object *radios, *radio_obj = NULL; + struct json_object *sha256_obj; + char *sha256_str; + json_bool ret; + int len, i; + + dbg("%s: file:%s\n", __func__, AUTOCFG_FILE); + + /* Get the json object from the file */ + wsc_json = json_object_from_file(AUTOCFG_FILE); + if (!wsc_json) { + dbg("%s: failed to read json:%s, error:%s. "\ + "Try to generate new\n", __func__, + AUTOCFG_FILE, json_util_get_last_err()); + + wsc_json = json_object_new_object(); + if (!wsc_json) { + warn("%s: failed to create json obj, error:%s. ", + __func__, json_util_get_last_err()); + return -1; + } + } + + /* Get the radios array */ + ret = json_object_object_get_ex(wsc_json, "radios", &radios); + if (!ret) { + /* Create radios array if not found */ + radios = json_object_new_array(); + if (!radios) { + warn("%s: failed to add radio array, error:%s. ", + __func__, json_util_get_last_err()); + goto out_radio; + } + json_object_object_add(wsc_json, "radios", radios); + } else if (!json_object_is_type(radios, json_type_array)) { + warn("%s: file: %s has wrong format\n", + __func__, AUTOCFG_FILE); + goto out_radio; + } + + /* Check if radio already exists in radio array */ + len = json_object_array_length(radios); + for (i = 0; i < len; i++) { + struct json_object *t; + uint8_t radio_mac[6]; + const char *p; + + t = json_object_array_get_idx(radios, i); + if (!t) { + warn("%s: couldn't get entry:%d from radio array", __func__, i); + continue; + } + + p = json_get_string(t, "macaddr"); + if (!p) { + warn("%s: couldn't get macaddr from entry:%d", __func__, i); + continue; + } + + if (!hwaddr_aton(p, radio_mac)) { + warn("%s: couldn't convert macaddr from entry:%d", __func__, i); + continue; + } + + if (!memcmp(macaddr, radio_mac, 6)) { + /* radio entry already present in file */ + radio_obj = t; + break; + } + } + + /* Create new radio object if not found */ + if (!radio_obj) { + char radiostr[18] = {0}; + struct json_object *val; + + radio_obj = json_object_new_object(); + if (WARN_ON(!radio_obj)) + goto out_radio; + + /* radio mac */ + hwaddr_ntoa(macaddr, radiostr); + val = json_object_new_string(radiostr); + if (!val) { + json_object_put(radio_obj); + goto out_radio; + } + json_object_object_add(radio_obj, "macaddr", val); + + /* Add radio object to radios array */ + json_object_array_add(radios, radio_obj); + } + + /* Get the sha256 string */ + ret = json_object_object_get_ex(radio_obj, "m2_sha256", &sha256_obj); + if (ret) { + /* Remove old SHA first */ + json_object_object_del(radio_obj, "m2_sha256"); + } + + /* Create sha256 string */ + sha256_str = calloc(sizeof(sha256) * 2 + 1, sizeof(char)); + if (!sha256_str) + goto out_radio; + + btostr(sha256, sizeof(sha256), sha256_str); + sha256_obj = json_object_new_string(sha256_str); + if (!sha256_obj) { + json_object_put(radio_obj); + goto free; + } + json_object_object_add(radio_obj, "m2_sha256", sha256_obj); + + /* Update restricted sha256 file */ + json_object_to_file(AUTOCFG_FILE, wsc_json); + +free: + free(sha256_str); +out_radio: + json_object_put(wsc_json); + + return 0; +} diff --git a/src/autoconfig.h b/src/autoconfig.h index 189fe5c37..4b6f54750 100644 --- a/src/autoconfig.h +++ b/src/autoconfig.h @@ -8,6 +8,8 @@ #define HEARTBEAT_AUTOCFG_INTERVAL 70 +#define AUTOCFG_FILE "/etc/multiap/wsc_m2.json" + enum autocfg_state { AUTOCFG_HEARTBEAT, AUTOCFG_ACTIVE @@ -26,4 +28,5 @@ bool autoconfig_has_wsc_changed(struct wsc_data *wsc, struct tlv *tlvs[][TLV_MAXNUM], size_t tlvs_size, uint8_t *sha256_out); +int autoconfig_radio_hash_to_file(uint8_t *macaddr, uint8_t *sha256); #endif -- GitLab From 746f6b129dd867ff9ae205597dd4eb387b1191ee Mon Sep 17 00:00:00 2001 From: Filip Matusiak <filip.matusiak@iopsys.eu> Date: Thu, 5 Jun 2025 10:14:37 +0200 Subject: [PATCH 3/5] wsc: use feature logging for autoconfig --- src/autoconfig.c | 47 +++++++++++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/src/autoconfig.c b/src/autoconfig.c index 5e8a8e96c..bcc3c6643 100644 --- a/src/autoconfig.c +++ b/src/autoconfig.c @@ -55,7 +55,7 @@ bool autoconfig_has_wsc_changed(struct wsc_data *data, bool ret = true; if (tlvs_size != MAX_TLV_TYPES) { - err("%s: Unsupported version of CMDU.\n", __func__); + agnt_dbg(LOG_APCFG, "%s: Unsupported version of CMDU.\n", __func__); return true; } @@ -145,14 +145,14 @@ bool autoconfig_has_wsc_changed(struct wsc_data *data, EVP_MD_CTX_free(ctx); - dbg("%s: current SHA256: %02x%02x%02x%02x%02x%02x...\n", __func__, - data->sha256[0], data->sha256[1], - data->sha256[2], data->sha256[3], - data->sha256[4], data->sha256[5]); + agnt_dbg(LOG_APCFG, "%s: current SHA256: %02x%02x%02x%02x%02x%02x...\n", __func__, + data->sha256[0], data->sha256[1], + data->sha256[2], data->sha256[3], + data->sha256[4], data->sha256[5]); - dbg("%s: new SHA256: %02x%02x%02x%02x%02x%02x...\n", __func__, - new_sha256[0], new_sha256[1], new_sha256[2], new_sha256[3], - new_sha256[4], new_sha256[5]); + agnt_dbg(LOG_APCFG, "%s: new SHA256: %02x%02x%02x%02x%02x%02x...\n", __func__, + new_sha256[0], new_sha256[1], new_sha256[2], new_sha256[3], + new_sha256[4], new_sha256[5]); memcpy(sha256_out, new_sha256, SHA256_LENGTH); @@ -193,14 +193,14 @@ int autoconfig_radio_hash_to_file(uint8_t *macaddr, uint8_t *sha256) /* Get the json object from the file */ wsc_json = json_object_from_file(AUTOCFG_FILE); if (!wsc_json) { - dbg("%s: failed to read json:%s, error:%s. "\ - "Try to generate new\n", __func__, - AUTOCFG_FILE, json_util_get_last_err()); + agnt_dbg(LOG_APCFG, "%s: failed to read json:%s, error:%s. "\ + "Try to generate new\n", __func__, + AUTOCFG_FILE, json_util_get_last_err()); wsc_json = json_object_new_object(); if (!wsc_json) { - warn("%s: failed to create json obj, error:%s. ", - __func__, json_util_get_last_err()); + agnt_warn(LOG_APCFG, "%s: failed to create json obj, error:%s. ", + __func__, json_util_get_last_err()); return -1; } } @@ -211,14 +211,14 @@ int autoconfig_radio_hash_to_file(uint8_t *macaddr, uint8_t *sha256) /* Create radios array if not found */ radios = json_object_new_array(); if (!radios) { - warn("%s: failed to add radio array, error:%s. ", - __func__, json_util_get_last_err()); + agnt_warn(LOG_APCFG, "%s: failed to add radio array, error:%s. ", + __func__, json_util_get_last_err()); goto out_radio; } json_object_object_add(wsc_json, "radios", radios); } else if (!json_object_is_type(radios, json_type_array)) { - warn("%s: file: %s has wrong format\n", - __func__, AUTOCFG_FILE); + agnt_warn(LOG_APCFG, "%s: file: %s has wrong format\n", + __func__, AUTOCFG_FILE); goto out_radio; } @@ -231,18 +231,18 @@ int autoconfig_radio_hash_to_file(uint8_t *macaddr, uint8_t *sha256) t = json_object_array_get_idx(radios, i); if (!t) { - warn("%s: couldn't get entry:%d from radio array", __func__, i); + agnt_dbg(LOG_APCFG, "%s: couldn't get entry:%d from radio array", __func__, i); continue; } p = json_get_string(t, "macaddr"); if (!p) { - warn("%s: couldn't get macaddr from entry:%d", __func__, i); + agnt_dbg(LOG_APCFG, "%s: couldn't get macaddr from entry:%d", __func__, i); continue; } if (!hwaddr_aton(p, radio_mac)) { - warn("%s: couldn't convert macaddr from entry:%d", __func__, i); + agnt_dbg(LOG_APCFG, "%s: couldn't convert macaddr from entry:%d", __func__, i); continue; } @@ -298,6 +298,13 @@ int autoconfig_radio_hash_to_file(uint8_t *macaddr, uint8_t *sha256) /* Update restricted sha256 file */ json_object_to_file(AUTOCFG_FILE, wsc_json); + agnt_dbg(LOG_APCFG, "%s: Wrote autoconfig SHA256: %02x%02x%02x%02x%02x%02x... to file %s\n", + __func__, + sha256[0], sha256[1], + sha256[2], sha256[3], + sha256[4], sha256[5], + AUTOCFG_FILE); + free: free(sha256_str); out_radio: -- GitLab From a897908b99bc168f6336361126deada847e4bef0 Mon Sep 17 00:00:00 2001 From: Filip Matusiak <filip.matusiak@iopsys.eu> Date: Thu, 5 Jun 2025 11:31:48 +0200 Subject: [PATCH 4/5] wsc: Add function to read WSC M2 md5sum from file Also read sha256 hash from file once upon restart --- src/agent.c | 7 ++++ src/agent_map.c | 1 + src/autoconfig.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++-- src/autoconfig.h | 3 +- 4 files changed, 95 insertions(+), 4 deletions(-) diff --git a/src/agent.c b/src/agent.c index 47c5aa7bb..3ccf52e20 100644 --- a/src/agent.c +++ b/src/agent.c @@ -5775,6 +5775,7 @@ struct wifi_radio_element *agent_add_radio(struct agent *a, char *name) timer_init(&re->bk.steer_timeout, bsta_steer_cb); timer_init(&re->unassoc_sta_meas_timer, agent_unassoc_sta_meas_timer_cb); + return re; } @@ -5785,6 +5786,7 @@ static int init_wifi_radios(struct agent *a) int ret = 0; list_for_each_entry(rcfg, &a->cfg.radiolist, list) { + uint8_t zero_block[SHA256_LENGTH] = {0}; wifi_object_t r_wobj = WIFI_OBJECT_INVALID; struct wifi_radio_element *re; char r_objname[32] = {0}; @@ -5804,6 +5806,11 @@ static int init_wifi_radios(struct agent *a) dbg("%s: getting radio:%s status\n", __func__, re->name); ret = ubus_call_object(a, r_wobj, "status", parse_radio, re); + if (!ret && !memcmp(zero_block, re->autconfig.sha256, SHA256_LENGTH)) { + if (autoconfig_radio_hash_from_file(re->macaddr, re->autconfig.sha256)) + dbg("%s: Autoconfig SHA256 for radio %s is unset\n", __func__, re->name); + } + /* Get fresh opclass preferences after scan */ wifi_radio_update_opclass_preferences(a, re, 1); agent_set_post_scan_action_pref(a, re, true); diff --git a/src/agent_map.c b/src/agent_map.c index 9bdb3d6ee..af2d4983c 100644 --- a/src/agent_map.c +++ b/src/agent_map.c @@ -2425,6 +2425,7 @@ int handle_ap_autoconfig_wsc(void *agent, struct cmdu_buff *rx_cmdu, return -1; dbg("|%s:%d| found radio = %s\n", __func__, __LINE__, radio->name); + if (!autoconfig_has_wsc_changed(&radio->autconfig, tv, AP_AUTOCONFIGURATION_WSC_M2_NUM_OF_TLV_TYPES, new_sha256)) { diff --git a/src/autoconfig.c b/src/autoconfig.c index bcc3c6643..0c76d2f6e 100644 --- a/src/autoconfig.c +++ b/src/autoconfig.c @@ -151,7 +151,8 @@ bool autoconfig_has_wsc_changed(struct wsc_data *data, data->sha256[4], data->sha256[5]); agnt_dbg(LOG_APCFG, "%s: new SHA256: %02x%02x%02x%02x%02x%02x...\n", __func__, - new_sha256[0], new_sha256[1], new_sha256[2], new_sha256[3], + new_sha256[0], new_sha256[1], + new_sha256[2], new_sha256[3], new_sha256[4], new_sha256[5]); memcpy(sha256_out, new_sha256, SHA256_LENGTH); @@ -160,6 +161,7 @@ bool autoconfig_has_wsc_changed(struct wsc_data *data, error_cleanup: EVP_MD_CTX_free(ctx); + return ret; } @@ -283,11 +285,12 @@ int autoconfig_radio_hash_to_file(uint8_t *macaddr, uint8_t *sha256) } /* Create sha256 string */ - sha256_str = calloc(sizeof(sha256) * 2 + 1, sizeof(char)); + sha256_str = calloc(SHA256_LENGTH * 2 + 1, sizeof(char)); if (!sha256_str) goto out_radio; - btostr(sha256, sizeof(sha256), sha256_str); + btostr(sha256, SHA256_LENGTH, sha256_str); + sha256_obj = json_object_new_string(sha256_str); if (!sha256_obj) { json_object_put(radio_obj); @@ -312,3 +315,82 @@ out_radio: return 0; } + +int autoconfig_radio_hash_from_file(uint8_t *macaddr, uint8_t *sha256_out) +{ + struct blob_buf radios = { 0 }; + struct blob_attr *b; + static const struct blobmsg_policy attr[] = { + [0] = { .name = "radios", .type = BLOBMSG_TYPE_ARRAY }, + }; + struct blob_attr *tb[ARRAY_SIZE(attr)]; + int rem; + int ret = 0; + + blob_buf_init(&radios, 0); + + if (!blobmsg_add_json_from_file(&radios, AUTOCFG_FILE)) { + agnt_dbg(LOG_APCFG, "Failed to parse %s\n", AUTOCFG_FILE); + ret = -1; + goto out; + } + + blobmsg_parse(attr, ARRAY_SIZE(attr), tb, blob_data(radios.head), blob_len(radios.head)); + + if (!tb[0]) { + ret = -1; + goto out; + } + + blobmsg_for_each_attr(b, tb[0], rem) { + static const struct blobmsg_policy radio_attr[2] = { + [0] = { .name = "macaddr", .type = BLOBMSG_TYPE_STRING }, + [1] = { .name = "m2_sha256", .type = BLOBMSG_TYPE_STRING }, + }; + struct blob_attr *tb1[ARRAY_SIZE(radio_attr)]; + char radio_str[18] = {0}; + uint8_t radio_mac[6] = {0}; + char *sh256_str; + int blen; + + blobmsg_parse(radio_attr, ARRAY_SIZE(radio_attr), tb1, blobmsg_data(b), blob_len(b)); + if (!tb1[0] || !tb1[1]) + continue; + + strncpy(radio_str, blobmsg_data(tb1[0]), sizeof(radio_str) - 1); + if (!hwaddr_aton(radio_str, radio_mac)) { + agnt_dbg(LOG_APCFG, "Failed to convert macaddr %s\n", radio_str); + continue; + } + + if (memcmp(macaddr, radio_mac, 6)) + continue; + + sh256_str = blobmsg_get_string(tb1[1]); + if (!sh256_str) { + agnt_err(LOG_APCFG, "%s: No valid sha256\n", __func__); + ret = -1; + break; + } + + blen = strlen(sh256_str) / 2; + if (blen > SHA256_LENGTH) { + agnt_err(LOG_APCFG, "%s: SHA256 string too long\n", __func__); + ret = -1; + break; + } + + strtob(sh256_str, blen, sha256_out); + } + + dbg("%s: Read autoconfig SHA256 for radio " MACFMT ": %02x%02x%02x%02x%02x%02x...\n", + __func__, MAC2STR(macaddr), + sha256_out[0], sha256_out[1], + sha256_out[2], sha256_out[3], + sha256_out[4], sha256_out[5]); + +out: + blob_buf_free(&radios); + + return ret; +} diff --git a/src/autoconfig.h b/src/autoconfig.h index 4b6f54750..dd1bb1025 100644 --- a/src/autoconfig.h +++ b/src/autoconfig.h @@ -24,9 +24,10 @@ struct wsc_data { void autoconfig_clean_wsc_hash(struct wsc_data *wsc); void autoconfig_update_wsc_hash(struct wsc_data *data, uint8_t *new_sha256); -bool autoconfig_has_wsc_changed(struct wsc_data *wsc, +bool autoconfig_has_wsc_changed(struct wsc_data *data, struct tlv *tlvs[][TLV_MAXNUM], size_t tlvs_size, uint8_t *sha256_out); int autoconfig_radio_hash_to_file(uint8_t *macaddr, uint8_t *sha256); +int autoconfig_radio_hash_from_file(uint8_t *macaddr, uint8_t *sha256_out); #endif -- GitLab From 970a78d5c3b5db9df955bcbfb8bae0a471ba020f Mon Sep 17 00:00:00 2001 From: Filip Matusiak <filip.matusiak@iopsys.eu> Date: Mon, 23 Jun 2025 16:53:13 +0200 Subject: [PATCH 5/5] wsc: separate AP/BSTA MLD hash from WSC M2 one - use diff bitmap for comparing WSC M2 frame --- src/agent.c | 5 + src/agent_map.c | 21 +++-- src/autoconfig.c | 232 +++++++++++++++++++++++++++++++++++++++++------ src/autoconfig.h | 23 ++++- 4 files changed, 243 insertions(+), 38 deletions(-) diff --git a/src/agent.c b/src/agent.c index 3ccf52e20..5c93d7b78 100644 --- a/src/agent.c +++ b/src/agent.c @@ -8116,6 +8116,11 @@ void run_agent(void *opts) agent_get_system_info(w); +#if (EASYMESH_VERSION >= 6) + autoconfig_ap_mld_hash_from_file(w->ap_mld_cfg_sha256); + autoconfig_bsta_mld_hash_from_file(w->bsta_mld_cfg_sha256); +#endif + /* w->cfg.enabled */ agent_publish_object(w, MAPAGENT_OBJECT); agent_publish_dbg_object(w, MAPAGENT_DBG_OBJECT); diff --git a/src/agent_map.c b/src/agent_map.c index af2d4983c..4886ca4bf 100644 --- a/src/agent_map.c +++ b/src/agent_map.c @@ -2396,6 +2396,7 @@ int handle_ap_autoconfig_wsc(void *agent, struct cmdu_buff *rx_cmdu, #ifdef CHECK_PARTIAL_WIFI_RELOAD bool full_reconf_required = false; #endif /* CHECK_PARTIAL_WIFI_RELOAD */ + int diff = DIFF_NONE; trace("%s: --->\n", __func__); @@ -2426,9 +2427,11 @@ int handle_ap_autoconfig_wsc(void *agent, struct cmdu_buff *rx_cmdu, dbg("|%s:%d| found radio = %s\n", __func__, __LINE__, radio->name); - if (!autoconfig_has_wsc_changed(&radio->autconfig, tv, + diff = autoconfig_wsc_m2_diff(radio, tv, AP_AUTOCONFIGURATION_WSC_M2_NUM_OF_TLV_TYPES, - new_sha256)) { + new_sha256); + + if (diff == DIFF_NONE || diff == DIFF_ERR) { info("%s: The same config received, skip radio configuration for: " MACFMT "\n", __func__, MAC2STR(radio->macaddr)); @@ -2631,11 +2634,11 @@ int handle_ap_autoconfig_wsc(void *agent, struct cmdu_buff *rx_cmdu, if (tv[AP_AUTOCONFIGURATION_WSC_M2_AP_MLD_CONFIG_IDX][0]) { mlo_process_ap_mld_config(a, tv[AP_AUTOCONFIGURATION_WSC_M2_AP_MLD_CONFIG_IDX][0]->data); - update_sha256_hash(tv[AP_AUTOCONFIGURATION_WSC_M2_AP_MLD_CONFIG_IDX][0]->data, - tlv_length(tv[AP_AUTOCONFIGURATION_WSC_M2_AP_MLD_CONFIG_IDX][0]), - a->ap_mld_cfg_sha256); a->reconfig_reason |= AGENT_RECONFIG_REASON_MLD_ID; timer_set(&a->reload_scheduler, RELOAD_TIMEOUT * 1000); + + if (diff & DIFF_AP_MLD) + autoconfig_ap_mld_hash_to_file(a->ap_mld_cfg_sha256); } if (tv[AP_AUTOCONFIGURATION_WSC_M2_BACKHAUL_STA_MLD_CONFIG_IDX][0]) { @@ -2645,16 +2648,18 @@ int handle_ap_autoconfig_wsc(void *agent, struct cmdu_buff *rx_cmdu, tv[AP_AUTOCONFIGURATION_WSC_M2_BACKHAUL_STA_MLD_CONFIG_IDX][0]->data); if (ret) wifi_teardown_map_bsta_mld(a, radio->band); - update_sha256_hash(tv[AP_AUTOCONFIGURATION_WSC_M2_BACKHAUL_STA_MLD_CONFIG_IDX][0]->data, - tlv_length(tv[AP_AUTOCONFIGURATION_WSC_M2_BACKHAUL_STA_MLD_CONFIG_IDX][0]), - a->ap_mld_cfg_sha256); a->reconfig_reason |= AGENT_RECONFIG_REASON_MLD_ID; timer_set(&a->reload_scheduler, RELOAD_TIMEOUT * 1000); + + if (diff & DIFF_BSTA_MLD) + autoconfig_bsta_mld_hash_to_file(a->bsta_mld_cfg_sha256); } else { dbg("%s: Missing BSTA MLD config TLV, teardown\n", __func__); wifi_teardown_map_bsta_mld(a, radio->band); } + + #endif dbg("|%s:%d| radio (%s) was configured! Apply heartbeat for this radio\n", diff --git a/src/autoconfig.c b/src/autoconfig.c index 0c76d2f6e..38a422913 100644 --- a/src/autoconfig.c +++ b/src/autoconfig.c @@ -15,6 +15,7 @@ #include <cmdu.h> #include <openssl/evp.h> +#include "agent.h" #include "autoconfig.h" #include "utils/debug.h" @@ -29,10 +30,10 @@ void autoconfig_update_wsc_hash(struct wsc_data *data, uint8_t *new_sha256) memcpy(&data->sha256, new_sha256, SHA256_LENGTH); } -bool autoconfig_has_wsc_changed(struct wsc_data *data, +int autoconfig_wsc_m2_diff(struct wifi_radio_element *re, struct tlv *tlvs[][TLV_MAXNUM], size_t tlvs_size, - uint8_t *sha256_out) + uint8_t *wsc_m2_sha256) { enum { AP_RADIO_ID = 0, @@ -52,11 +53,12 @@ bool autoconfig_has_wsc_changed(struct wsc_data *data, uint8_t bssid[6]; EVP_MD_CTX *ctx; int i; - bool ret = true; + int diff = DIFF_NONE; + struct wsc_data *data = &re->autconfig; if (tlvs_size != MAX_TLV_TYPES) { agnt_dbg(LOG_APCFG, "%s: Unsupported version of CMDU.\n", __func__); - return true; + return DIFF_ERR; } memcpy(bssid, tlvs[AP_RADIO_ID][0]->data, 6); @@ -64,7 +66,7 @@ bool autoconfig_has_wsc_changed(struct wsc_data *data, /* Calculate SHA-256 hash from all TLVs data */ ctx = EVP_MD_CTX_new(); if (!ctx) - return true; + return DIFF_ERR; if (!EVP_DigestInit_ex(ctx, EVP_sha256(), NULL)) goto error_cleanup; @@ -81,6 +83,7 @@ bool autoconfig_has_wsc_changed(struct wsc_data *data, 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); + int ret; if (!m2_tmp) goto error_cleanup; @@ -94,10 +97,8 @@ bool autoconfig_has_wsc_changed(struct wsc_data *data, free(m2_tmp); - if (ret) { - ret = false; + if (ret) goto error_cleanup; - } if (!EVP_DigestUpdate(ctx, &wps_out, sizeof(wps_out))) { free(ext_out); @@ -126,43 +127,59 @@ bool autoconfig_has_wsc_changed(struct wsc_data *data, goto error_cleanup; } -#if (EASYMESH_VERSION >= 6) - if (tlvs[AP_MLD_CONFIG][0] && - !EVP_DigestUpdate(ctx, tlvs[AP_MLD_CONFIG][0]->data, - tlv_length(tlvs[AP_MLD_CONFIG][0]))) { - goto error_cleanup; - } - - if (tlvs[BACKHAUL_STA_MLD_CONFIG][0] && - !EVP_DigestUpdate(ctx, tlvs[BACKHAUL_STA_MLD_CONFIG][0]->data, - tlv_length(tlvs[BACKHAUL_STA_MLD_CONFIG][0]))) { - goto error_cleanup; - } -#endif - if (!EVP_DigestFinal_ex(ctx, new_sha256, NULL)) goto error_cleanup; EVP_MD_CTX_free(ctx); - agnt_dbg(LOG_APCFG, "%s: current SHA256: %02x%02x%02x%02x%02x%02x...\n", __func__, + agnt_dbg(LOG_APCFG, "%s: current WSC M2 SHA256: %02x%02x%02x%02x%02x%02x...\n", __func__, data->sha256[0], data->sha256[1], data->sha256[2], data->sha256[3], data->sha256[4], data->sha256[5]); - agnt_dbg(LOG_APCFG, "%s: new SHA256: %02x%02x%02x%02x%02x%02x...\n", __func__, + agnt_dbg(LOG_APCFG, "%s: new WSC M2 SHA256: %02x%02x%02x%02x%02x%02x...\n", __func__, new_sha256[0], new_sha256[1], new_sha256[2], new_sha256[3], new_sha256[4], new_sha256[5]); - memcpy(sha256_out, new_sha256, SHA256_LENGTH); + memcpy(wsc_m2_sha256, new_sha256, SHA256_LENGTH); + if (memcmp(data->sha256, new_sha256, SHA256_LENGTH)) + diff |= DIFF_WSC_M2; - return memcmp(data->sha256, new_sha256, SHA256_LENGTH); +#if (EASYMESH_VERSION >= 6) + if (tlvs[AP_MLD_CONFIG][0]) { + bool ap_mld_updated = false; + + ap_mld_updated = update_sha256_hash(tlvs[AP_MLD_CONFIG][0]->data, + tlv_length(tlvs[AP_MLD_CONFIG][0]), + re->agent->ap_mld_cfg_sha256); + + if (ap_mld_updated) { + agnt_dbg(LOG_APCFG, "%s: AP MLD SHA256 has changed\n", __func__); + diff |= DIFF_AP_MLD; + } else + agnt_dbg(LOG_APCFG, "%s: no change of AP MLD SHA256\n", __func__); + } + + if (tlvs[BACKHAUL_STA_MLD_CONFIG][0]) { + bool bsta_mld_updated = false; + + bsta_mld_updated = update_sha256_hash(tlvs[BACKHAUL_STA_MLD_CONFIG][0]->data, + tlv_length(tlvs[BACKHAUL_STA_MLD_CONFIG][0]), + re->agent->bsta_mld_cfg_sha256); + if (bsta_mld_updated) { + agnt_dbg(LOG_APCFG, "%s: BSTA MLD SHA256 has changed\n", __func__); + diff |= DIFF_BSTA_MLD; + } else + agnt_dbg(LOG_APCFG, "%s: no change of BSTA MLD SHA256\n", __func__); + } +#endif + return diff; error_cleanup: EVP_MD_CTX_free(ctx); - return ret; + return DIFF_ERR; } static const char *json_get_string(struct json_object *object, const char *key) @@ -180,6 +197,165 @@ static const char *json_get_string(struct json_object *object, const char *key) return json_object_get_string(value); } +#if (EASYMESH_VERSION >= 6) +enum autocfg_mld { + AP_MLD, + BSTA_MLD, +}; + +static int autoconfig_mld_hash_to_file(uint8_t *sha256, int type) +{ + struct json_object *wsc_json; + struct json_object *sha256_obj; + char type_str[16] = {}; + char *sha256_str; + json_bool found_in_file; + + dbg("%s: file:%s\n", __func__, AUTOCFG_FILE); + + strncpy(type_str, + ((type == AP_MLD) ? "ap_mld_sha256" : "bsta_mld_sha256"), + sizeof(type_str) - 1); + + /* Get the json object from the file */ + wsc_json = json_object_from_file(AUTOCFG_FILE); + if (!wsc_json) { + agnt_dbg(LOG_APCFG, "%s: failed to read json:%s, error:%s. "\ + "Try to generate new\n", __func__, + AUTOCFG_FILE, json_util_get_last_err()); + + wsc_json = json_object_new_object(); + if (!wsc_json) { + agnt_warn(LOG_APCFG, "%s: failed to create json obj, error:%s. ", + __func__, json_util_get_last_err()); + return -1; + } + } + + found_in_file = json_object_object_get_ex(wsc_json, type_str, &sha256_obj); + if (found_in_file) { + /* Remove old SHA first */ + json_object_object_del(wsc_json, type_str); + } + + /* Create sha256 string */ + sha256_str = calloc(SHA256_LENGTH * 2 + 1, sizeof(char)); + if (!sha256_str) + goto out; + btostr(sha256, SHA256_LENGTH, sha256_str); + + sha256_obj = json_object_new_string(sha256_str); + if (WARN_ON(!sha256_obj)) { + agnt_warn(LOG_APCFG, + "%s: failed to add %s, error:%s. ", + __func__, type_str, json_util_get_last_err()); + goto free; + } + + json_object_object_add(wsc_json, type_str, sha256_obj); + + /* Update sha256 file */ + json_object_to_file(AUTOCFG_FILE, wsc_json); + + agnt_dbg(LOG_APCFG, "%s: Wrote %s SHA256: %02x%02x%02x%02x%02x%02x... to file %s\n", + __func__, ((type == AP_MLD) ? "AP MLD" : "BSTA MLD"), + sha256[0], sha256[1], + sha256[2], sha256[3], + sha256[4], sha256[5], + AUTOCFG_FILE); + +free: + free(sha256_str); +out: + json_object_put(wsc_json); + + return 0; +} + +static int get_mld_hash_from_file(uint8_t *sha256_out, int type, const struct blobmsg_policy *attr) +{ + struct blob_attr *tb[1]; + struct blob_buf wsc_m2_file = {0}; + char *sha256_str; + int blen; + int ret = 0; + + blob_buf_init(&wsc_m2_file, 0); + + if (!blobmsg_add_json_from_file(&wsc_m2_file, AUTOCFG_FILE)) { + agnt_dbg(LOG_APCFG, "Failed to parse %s\n", AUTOCFG_FILE); + ret = -1; + goto free; + } + + blobmsg_parse(attr, 1, tb, blob_data(wsc_m2_file.head), blob_len(wsc_m2_file.head)); + + if (!tb[0]) { + ret = -1; + goto free; + } + + sha256_str = blobmsg_get_string(tb[0]); + if (!sha256_str) { + agnt_err(LOG_APCFG, "%s: No valid %s SHA256\n", __func__, + ((type == AP_MLD) ? "AP MLD" : "BSTA MLD")); + ret = -1; + goto free; + } + + blen = strlen(sha256_str) / 2; + if (blen > SHA256_LENGTH) { + agnt_err(LOG_APCFG, "%s: %s SHA256 too long\n", __func__, + ((type == AP_MLD) ? "AP MLD" : "BSTA MLD")); + ret = -1; + goto free; + } + + strtob(sha256_str, blen, sha256_out); + + dbg("%s: Read %s SHA256: %02x%02x%02x%02x%02x%02x...\n", __func__, + ((type == AP_MLD) ? "AP MLD" : "BSTA MLD"), + sha256_out[0], sha256_out[1], + sha256_out[2], sha256_out[3], + sha256_out[4], sha256_out[5]); + +free: + blob_buf_free(&wsc_m2_file); + + return ret; +} + +/* keep the AP MLD md5sum in non-persistent storage */ +int autoconfig_ap_mld_hash_to_file(uint8_t *sha256) +{ + return autoconfig_mld_hash_to_file(sha256, AP_MLD); +} + +int autoconfig_ap_mld_hash_from_file(uint8_t *sha256_out) +{ + static const struct blobmsg_policy attr_ap[] = { + [0] = { .name = "ap_mld_sha256", .type = BLOBMSG_TYPE_STRING }, + }; + + return get_mld_hash_from_file(sha256_out, AP_MLD, attr_ap); +} + +/* keep the BSTA MLD md5sum in non-persistent storage */ +int autoconfig_bsta_mld_hash_to_file(uint8_t *sha256) +{ + return autoconfig_mld_hash_to_file(sha256, BSTA_MLD); +} + +int autoconfig_bsta_mld_hash_from_file(uint8_t *sha256_out) +{ + static const struct blobmsg_policy attr_bsta[] = { + [0] = { .name = "bsta_mld_sha256", .type = BLOBMSG_TYPE_STRING }, + }; + + return get_mld_hash_from_file(sha256_out, BSTA_MLD, attr_bsta); +} +#endif /* EASYMESH_VERSION >= 6 */ + /* keep the WSC M2 md5sum in non-persistent storage */ int autoconfig_radio_hash_to_file(uint8_t *macaddr, uint8_t *sha256) { @@ -298,7 +474,7 @@ int autoconfig_radio_hash_to_file(uint8_t *macaddr, uint8_t *sha256) } json_object_object_add(radio_obj, "m2_sha256", sha256_obj); - /* Update restricted sha256 file */ + /* Update sha256 file */ json_object_to_file(AUTOCFG_FILE, wsc_json); agnt_dbg(LOG_APCFG, "%s: Wrote autoconfig SHA256: %02x%02x%02x%02x%02x%02x... to file %s\n", diff --git a/src/autoconfig.h b/src/autoconfig.h index dd1bb1025..93f45dd36 100644 --- a/src/autoconfig.h +++ b/src/autoconfig.h @@ -10,11 +10,23 @@ #define AUTOCFG_FILE "/etc/multiap/wsc_m2.json" +struct wifi_radio_element; + enum autocfg_state { AUTOCFG_HEARTBEAT, AUTOCFG_ACTIVE }; +enum autocfg_diff { + DIFF_NONE = 0, + DIFF_WSC_M2 = 1 << 0, +#if (EASYMESH_VERSION >= 6) + DIFF_AP_MLD = 1 << 1, + DIFF_BSTA_MLD = 1 << 2, +#endif + DIFF_ERR = 255, +}; + struct wsc_data { uint8_t *m1_frame; uint16_t m1_size; @@ -24,10 +36,17 @@ struct wsc_data { void autoconfig_clean_wsc_hash(struct wsc_data *wsc); void autoconfig_update_wsc_hash(struct wsc_data *data, uint8_t *new_sha256); -bool autoconfig_has_wsc_changed(struct wsc_data *data, +int autoconfig_wsc_m2_diff(struct wifi_radio_element *re, struct tlv *tlvs[][TLV_MAXNUM], size_t tlvs_size, - uint8_t *sha256_out); + uint8_t *wsc_m2_sha256); + int autoconfig_radio_hash_to_file(uint8_t *macaddr, uint8_t *sha256); int autoconfig_radio_hash_from_file(uint8_t *macaddr, uint8_t *sha256_out); +#if (EASYMESH_VERSION >= 6) +int autoconfig_ap_mld_hash_to_file(uint8_t *sha256); +int autoconfig_ap_mld_hash_from_file(uint8_t *sha256_out); +int autoconfig_bsta_mld_hash_to_file(uint8_t *sha256); +int autoconfig_bsta_mld_hash_from_file(uint8_t *sha256_out); +#endif #endif -- GitLab