fix: enhance KNX OAM router functionality and security features

Signed-off-by: Tony <tonylu@tony-cloud.com>
This commit is contained in:
Tony
2026-05-29 01:31:58 +08:00
parent bb0fb01c00
commit f39ae6f0c6
13 changed files with 375 additions and 35 deletions
@@ -70,6 +70,12 @@ constexpr uint8_t kDaliGroupRawMax = 0x9F;
#define CONFIG_GATEWAY_KNX_INSTANCE_COUNT 1
#endif
constexpr uint32_t kKnxEnabledInstanceCount = CONFIG_GATEWAY_KNX_INSTANCE_COUNT;
constexpr uint32_t kKnxOamInstanceId = 16;
#if defined(CONFIG_GATEWAY_KNX_OAM_ROUTER_SUPPORTED)
constexpr bool kKnxOamInstanceSupported = true;
#else
constexpr bool kKnxOamInstanceSupported = false;
#endif
void ConfigureDaliCppLogging() {
static bool configured = false;
@@ -400,21 +406,49 @@ std::optional<int> QueryInt(std::string_view query, std::string_view primary,
}
std::optional<uint32_t> ValidKnxInstanceId(int value) {
if (value < 0 || static_cast<uint32_t>(value) >= kKnxEnabledInstanceCount) {
return std::nullopt;
if (value >= 0) {
const auto instance_id = static_cast<uint32_t>(value);
if (instance_id < kKnxEnabledInstanceCount ||
(kKnxOamInstanceSupported && instance_id == kKnxOamInstanceId)) {
return instance_id;
}
}
return static_cast<uint32_t>(value);
return std::nullopt;
}
std::string KnxInstanceIdRangeMessage() {
return "KNX instanceId must be in range 0.." +
std::to_string(kKnxEnabledInstanceCount - 1);
std::string message = "KNX instanceId must be in range 0.." +
std::to_string(kKnxEnabledInstanceCount - 1);
if (kKnxOamInstanceSupported) {
message += " or " + std::to_string(kKnxOamInstanceId) + " for OAM";
}
return message;
}
std::optional<uint32_t> QueryKnxInstanceId(std::string_view query) {
return ValidKnxInstanceId(QueryInt(query, "instanceId", "instance").value_or(0));
}
bool IsKnxOamInstanceId(uint32_t instance_id) {
return kKnxOamInstanceSupported && instance_id == kKnxOamInstanceId;
}
cJSON* KnxAvailableInstanceIdsCjson(bool oam_enabled) {
cJSON* instances = cJSON_CreateArray();
if (instances == nullptr) {
return nullptr;
}
for (uint32_t instance_id = 0; instance_id < kKnxEnabledInstanceCount; ++instance_id) {
cJSON_AddItemToArray(instances,
cJSON_CreateNumber(static_cast<double>(instance_id)));
}
if (kKnxOamInstanceSupported && oam_enabled) {
cJSON_AddItemToArray(instances,
cJSON_CreateNumber(static_cast<double>(kKnxOamInstanceId)));
}
return instances;
}
bool EraseOpenKnxRuntimeData(const std::string& nvs_namespace) {
if (nvs_namespace.empty()) {
return false;
@@ -2401,9 +2435,19 @@ struct GatewayBridgeService::ChannelRuntime {
}
const auto effective_knx =
knx_config.has_value() ? knx_config : service_config.default_knx_config;
const bool oam_instance_enabled =
effective_knx.has_value() && effective_knx->oam_router.enabled;
cJSON_AddNumberToObject(knx_json, "instanceId", static_cast<double>(instance_id));
cJSON_AddNumberToObject(knx_json, "enabledInstanceCount",
static_cast<double>(kKnxEnabledInstanceCount));
cJSON* instance_ids_json = KnxAvailableInstanceIdsCjson(oam_instance_enabled);
if (instance_ids_json != nullptr) {
cJSON_AddItemToObject(knx_json, "availableInstanceIds", instance_ids_json);
}
if (kKnxOamInstanceSupported) {
cJSON_AddNumberToObject(knx_json, "oamInstanceId",
static_cast<double>(kKnxOamInstanceId));
}
cJSON_AddBoolToObject(knx_json, "enabled", service_config.knx_enabled);
cJSON_AddBoolToObject(knx_json, "startupEnabled", service_config.knx_startup_enabled);
cJSON_AddBoolToObject(knx_json, "started", knx_started);
@@ -2457,14 +2501,19 @@ struct GatewayBridgeService::ChannelRuntime {
cJSON_AddStringToObject(security_json, "storage", "none");
#endif
#if defined(CONFIG_GATEWAY_KNX_DATA_SECURE_SUPPORTED)
const auto fdsk_info = openknx::LoadFactoryFdskInfoForInstance(instance_id);
const bool oam_instance = IsKnxOamInstanceId(instance_id);
const auto fdsk_info = oam_instance
? openknx::LoadOamFactoryFdskInfo()
: openknx::LoadFactoryFdskInfoForInstance(instance_id);
cJSON* fdsk_json = FactoryFdskInfoToCjson(fdsk_info, true);
if (fdsk_json != nullptr) {
cJSON_AddItemToObject(security_json, "factorySetupKey", fdsk_json);
}
cJSON* certificate_json =
FactoryCertificateToCjson(
openknx::BuildFactoryCertificatePayloadForInstance(instance_id), false);
oam_instance ? openknx::BuildOamFactoryCertificatePayload()
: openknx::BuildFactoryCertificatePayloadForInstance(instance_id),
false);
if (certificate_json != nullptr) {
cJSON_AddItemToObject(security_json, "factoryCertificate", certificate_json);
}
@@ -4963,6 +5012,7 @@ GatewayBridgeHttpResponse GatewayBridgeService::handlePost(
return ErrorResponse(ESP_ERR_INVALID_ARG, message.c_str());
}
const bool reset_oam = IsKnxOamInstanceId(instance_id.value());
const bool reset_runtime = instance_id.value() == 0;
const bool restart_router = reset_runtime &&
(runtime->knx_started ||
@@ -4975,7 +5025,9 @@ GatewayBridgeHttpResponse GatewayBridgeService::handlePost(
}
std::string nvs_namespace = runtime->openKnxNamespace();
if (instance_id.value() != 0) {
if (reset_oam) {
nvs_namespace = kGatewayKnxOamOpenKnxNamespace;
} else if (instance_id.value() != 0) {
nvs_namespace += "_" + std::to_string(instance_id.value());
}
if (!EraseOpenKnxRuntimeData(nvs_namespace)) {
@@ -4983,7 +5035,11 @@ GatewayBridgeHttpResponse GatewayBridgeService::handlePost(
}
openknx::FactoryFdskInfo info;
if (!openknx::ResetFactorySecurityForInstance(instance_id.value(), &info)) {
const bool security_reset = reset_oam
? openknx::ResetOamFactoryFdskCache(&info)
: openknx::ResetFactorySecurityForInstance(instance_id.value(),
&info);
if (!security_reset) {
return ErrorResponse(ESP_FAIL, "failed to restore KNX factory security key");
}
@@ -489,6 +489,16 @@ bool LoadFactoryFdsk(uint8_t* data, size_t len) {
return LoadFactoryFdskForContext(kReg1Context, data, len);
}
extern "C" bool knx_platform_get_fdsk_for_namespace(const char* nvs_namespace,
uint8_t* data,
size_t len) {
const std::string namespace_name = nvs_namespace == nullptr ? std::string() : nvs_namespace;
if (namespace_name.size() >= 4 && namespace_name.compare(namespace_name.size() - 4, 4, "_oam") == 0) {
return LoadFactoryFdskForContext(kOamContext, data, len);
}
return LoadFactoryFdsk(data, len);
}
FactoryFdskInfo LoadFactoryFdskInfo() {
return LoadFactoryFdskInfoForContext(kReg1Context);
}