2b779d5532
- Implement secure transport mechanisms in `gateway_knx_secure_transport.cpp` for handling secure sessions, including AES encryption, session key generation, and secure packet wrapping and unwrapping. - Introduce `OamRouterRuntime` in `oam_router_runtime.cpp` to manage OAM router identity, individual addresses, and tunnel frame handling. - Enhance secure session management with functions for session allocation, authentication, and secure packet processing. - Ensure compatibility with existing KNXnet/IP protocols while adding support for secure communications. Signed-off-by: Tony <tonylu@tony-cloud.com>
375 lines
17 KiB
C++
375 lines
17 KiB
C++
#include "gateway_knx_private.hpp"
|
|
|
|
namespace gateway {
|
|
|
|
namespace {
|
|
|
|
GatewayKnxCloudRemoteConfig GatewayKnxCloudRemoteConfigFromValue(const DaliValue* value) {
|
|
GatewayKnxCloudRemoteConfig config;
|
|
if (value == nullptr || value->asObject() == nullptr) {
|
|
return config;
|
|
}
|
|
const auto& object = *value->asObject();
|
|
config.enabled = ObjectBoolAny(object, {"enabled", "cloudRemoteEnabled",
|
|
"cloud_remote_enabled"})
|
|
.value_or(config.enabled);
|
|
config.mode = ObjectStringAny(object, {"mode", "remoteMode", "remote_mode"})
|
|
.value_or(config.mode);
|
|
config.relay_endpoint = ObjectStringAny(object, {"relayEndpoint", "relay_endpoint",
|
|
"endpoint"})
|
|
.value_or(config.relay_endpoint);
|
|
config.mqtt_topic_prefix = ObjectStringAny(object, {"mqttTopicPrefix", "mqtt_topic_prefix",
|
|
"topicPrefix", "topic_prefix"})
|
|
.value_or(config.mqtt_topic_prefix);
|
|
config.auth_token_ref = ObjectStringAny(object, {"authTokenRef", "auth_token_ref",
|
|
"tokenRef", "token_ref"})
|
|
.value_or(config.auth_token_ref);
|
|
config.require_secure_tunnel = ObjectBoolAny(object, {"requireSecureTunnel",
|
|
"require_secure_tunnel"})
|
|
.value_or(config.require_secure_tunnel);
|
|
config.udp_punch_enabled = ObjectBoolAny(object, {"udpPunchEnabled", "udp_punch_enabled"})
|
|
.value_or(config.udp_punch_enabled);
|
|
return config;
|
|
}
|
|
|
|
DaliValue GatewayKnxCloudRemoteConfigToValue(const GatewayKnxCloudRemoteConfig& config) {
|
|
DaliValue::Object out;
|
|
out["enabled"] = config.enabled;
|
|
out["mode"] = config.mode;
|
|
out["relayEndpoint"] = config.relay_endpoint;
|
|
out["mqttTopicPrefix"] = config.mqtt_topic_prefix;
|
|
out["authTokenRef"] = config.auth_token_ref;
|
|
out["requireSecureTunnel"] = config.require_secure_tunnel;
|
|
out["udpPunchEnabled"] = config.udp_punch_enabled;
|
|
return DaliValue(std::move(out));
|
|
}
|
|
|
|
} // namespace
|
|
|
|
std::optional<GatewayKnxOamRouterConfig> GatewayKnxOamRouterConfigFromValue(
|
|
const DaliValue* value) {
|
|
if (value == nullptr || value->asObject() == nullptr) {
|
|
return std::nullopt;
|
|
}
|
|
const auto& object = *value->asObject();
|
|
GatewayKnxOamRouterConfig config;
|
|
config.enabled = ObjectBoolAny(object, {"enabled", "oamRouterEnabled",
|
|
"oam_router_enabled"})
|
|
.value_or(config.enabled);
|
|
config.ets_database_enabled = ObjectBoolAny(object, {"etsDatabaseEnabled",
|
|
"ets_database_enabled"})
|
|
.value_or(config.ets_database_enabled);
|
|
config.secure_tunnel_enabled = ObjectBoolAny(object, {"secureTunnelEnabled",
|
|
"secure_tunnel_enabled"})
|
|
.value_or(config.secure_tunnel_enabled);
|
|
config.secure_routing_enabled = ObjectBoolAny(object, {"secureRoutingEnabled",
|
|
"secure_routing_enabled"})
|
|
.value_or(config.secure_routing_enabled);
|
|
config.individual_address = static_cast<uint16_t>(std::clamp(
|
|
ObjectIntAny(object, {"individualAddress", "individual_address", "routerAddress",
|
|
"router_address"})
|
|
.value_or(config.individual_address),
|
|
0, 0xffff));
|
|
config.tunnel_address_base = static_cast<uint16_t>(std::clamp(
|
|
ObjectIntAny(object, {"tunnelAddressBase", "tunnel_address_base",
|
|
"tunnelBaseAddress", "tunnel_base_address"})
|
|
.value_or(config.tunnel_address_base),
|
|
0, 0xffff));
|
|
config.programming_button_gpio = std::clamp(
|
|
ObjectIntAny(object, {"programmingButtonGpio", "programming_button_gpio"})
|
|
.value_or(config.programming_button_gpio),
|
|
-1, 48);
|
|
config.programming_button_active_low =
|
|
ObjectBoolAny(object, {"programmingButtonActiveLow", "programming_button_active_low"})
|
|
.value_or(config.programming_button_active_low);
|
|
config.programming_led_gpio = std::clamp(
|
|
ObjectIntAny(object, {"programmingLedGpio", "programming_led_gpio"})
|
|
.value_or(config.programming_led_gpio),
|
|
-1, 48);
|
|
config.programming_led_active_high =
|
|
ObjectBoolAny(object, {"programmingLedActiveHigh", "programming_led_active_high"})
|
|
.value_or(config.programming_led_active_high);
|
|
config.cloud_remote = GatewayKnxCloudRemoteConfigFromValue(
|
|
ObjectValueAny(object, {"cloudRemote", "cloud_remote", "remoteAccess",
|
|
"remote_access"}));
|
|
return config;
|
|
}
|
|
|
|
DaliValue GatewayKnxOamRouterConfigToValue(const GatewayKnxOamRouterConfig& config) {
|
|
DaliValue::Object out;
|
|
out["enabled"] = config.enabled;
|
|
out["etsDatabaseEnabled"] = config.ets_database_enabled;
|
|
out["secureTunnelEnabled"] = config.secure_tunnel_enabled;
|
|
out["secureRoutingEnabled"] = config.secure_routing_enabled;
|
|
out["individualAddress"] = static_cast<int>(config.individual_address);
|
|
out["tunnelAddressBase"] = static_cast<int>(config.tunnel_address_base);
|
|
out["programmingButtonGpio"] = config.programming_button_gpio;
|
|
out["programmingButtonActiveLow"] = config.programming_button_active_low;
|
|
out["programmingLedGpio"] = config.programming_led_gpio;
|
|
out["programmingLedActiveHigh"] = config.programming_led_active_high;
|
|
out["cloudRemote"] = GatewayKnxCloudRemoteConfigToValue(config.cloud_remote);
|
|
return DaliValue(std::move(out));
|
|
}
|
|
|
|
std::optional<GatewayKnxConfig> GatewayKnxConfigFromValue(const DaliValue* value) {
|
|
if (value == nullptr || value->asObject() == nullptr) {
|
|
return std::nullopt;
|
|
}
|
|
const auto& object = *value->asObject();
|
|
GatewayKnxConfig config;
|
|
config.dali_router_enabled = ObjectBoolAny(object, {"daliRouterEnabled", "dali_router_enabled"})
|
|
.value_or(config.dali_router_enabled);
|
|
config.ip_router_enabled = ObjectBoolAny(object, {"ipRouterEnabled", "ip_router_enabled"})
|
|
.value_or(config.ip_router_enabled);
|
|
config.tunnel_enabled = ObjectBoolAny(object, {"tunnelEnabled", "tunnel_enabled"})
|
|
.value_or(config.tunnel_enabled);
|
|
config.multicast_enabled = ObjectBoolAny(object, {"multicastEnabled", "multicast_enabled"})
|
|
.value_or(config.multicast_enabled);
|
|
if (const auto mode = ObjectStringAny(object, {"mappingMode", "mapping_mode"})) {
|
|
config.mapping_mode = GatewayKnxMappingModeFromString(mode.value());
|
|
}
|
|
config.ets_database_enabled = ObjectBoolAny(object, {"etsDatabaseEnabled", "ets_database_enabled"})
|
|
.value_or(config.ets_database_enabled);
|
|
config.ets_associations = ParseEtsAssociations(object);
|
|
config.main_group = static_cast<uint8_t>(
|
|
std::clamp(ObjectIntAny(object, {"mainGroup", "main_group"}).value_or(config.main_group),
|
|
0, 31));
|
|
config.dali_bus_id = static_cast<uint8_t>(std::clamp(
|
|
ObjectIntAny(object, {"daliBusId", "dali_bus_id", "targetDaliBusId",
|
|
"target_dali_bus_id"})
|
|
.value_or(config.dali_bus_id),
|
|
0, 15));
|
|
config.udp_port = static_cast<uint16_t>(std::clamp(
|
|
ObjectIntAny(object, {"udpPort", "port", "udp_port"}).value_or(config.udp_port), 1,
|
|
65535));
|
|
config.multicast_address = ObjectStringAny(object, {"multicastAddress", "multicast_address"})
|
|
.value_or(config.multicast_address);
|
|
config.ip_interface_individual_address = static_cast<uint16_t>(std::clamp(
|
|
ObjectIntAny(object, {"ipInterfaceIndividualAddress",
|
|
"ip_interface_individual_address",
|
|
"ipInterfaceAddress",
|
|
"ip_interface_address"})
|
|
.value_or(config.ip_interface_individual_address),
|
|
0, 0xffff));
|
|
config.individual_address = static_cast<uint16_t>(std::clamp(
|
|
ObjectIntAny(object, {"individualAddress",
|
|
"individual_address",
|
|
"knxDaliGatewayIndividualAddress",
|
|
"knx_dali_gateway_individual_address",
|
|
"deviceIndividualAddress",
|
|
"device_individual_address"})
|
|
.value_or(config.individual_address),
|
|
0, 0xffff));
|
|
config.programming_button_gpio = std::clamp(
|
|
ObjectIntAny(object, {"programmingButtonGpio", "programming_button_gpio"})
|
|
.value_or(config.programming_button_gpio),
|
|
-1, 48);
|
|
config.programming_button_active_low =
|
|
ObjectBoolAny(object, {"programmingButtonActiveLow", "programming_button_active_low"})
|
|
.value_or(config.programming_button_active_low);
|
|
config.programming_led_gpio = std::clamp(
|
|
ObjectIntAny(object, {"programmingLedGpio", "programming_led_gpio"})
|
|
.value_or(config.programming_led_gpio),
|
|
-1, 48);
|
|
config.programming_led_active_high =
|
|
ObjectBoolAny(object, {"programmingLedActiveHigh", "programming_led_active_high"})
|
|
.value_or(config.programming_led_active_high);
|
|
if (const auto oam_router = GatewayKnxOamRouterConfigFromValue(
|
|
ObjectValueAny(object, {"oamRouter", "oam_router", "routerApplication",
|
|
"router_application"}))) {
|
|
config.oam_router = oam_router.value();
|
|
} else {
|
|
config.oam_router.enabled = ObjectBoolAny(object, {"oamRouterEnabled",
|
|
"oam_router_enabled"})
|
|
.value_or(config.oam_router.enabled);
|
|
}
|
|
|
|
const auto* tp_uart = getObjectValue(object, "tpUart");
|
|
if (tp_uart == nullptr) {
|
|
tp_uart = getObjectValue(object, "tp_uart");
|
|
}
|
|
if (tp_uart != nullptr && tp_uart->asObject() != nullptr) {
|
|
const auto& serial = *tp_uart->asObject();
|
|
config.tp_uart.uart_port = std::clamp(
|
|
ObjectIntAny(serial, {"uartPort", "uart_port"}).value_or(config.tp_uart.uart_port), -1,
|
|
2);
|
|
config.tp_uart.tx_pin = ObjectIntAny(serial, {"txPin", "tx_pin"}).value_or(config.tp_uart.tx_pin);
|
|
config.tp_uart.rx_pin = ObjectIntAny(serial, {"rxPin", "rx_pin"}).value_or(config.tp_uart.rx_pin);
|
|
config.tp_uart.baudrate = static_cast<uint32_t>(std::max(
|
|
1200, ObjectIntAny(serial, {"baudrate", "baud"}).value_or(config.tp_uart.baudrate)));
|
|
config.tp_uart.rx_buffer_size = static_cast<size_t>(std::max(
|
|
128, ObjectIntAny(serial, {"rxBufferSize", "rx_buffer_size"})
|
|
.value_or(static_cast<int>(config.tp_uart.rx_buffer_size))));
|
|
config.tp_uart.tx_buffer_size = static_cast<size_t>(std::max(
|
|
128, ObjectIntAny(serial, {"txBufferSize", "tx_buffer_size"})
|
|
.value_or(static_cast<int>(config.tp_uart.tx_buffer_size))));
|
|
config.tp_uart.startup_timeout_ms = static_cast<uint32_t>(std::max(
|
|
0, ObjectIntAny(serial, {"startupTimeoutMs", "startup_timeout_ms"})
|
|
.value_or(static_cast<int>(config.tp_uart.startup_timeout_ms))));
|
|
config.tp_uart.read_timeout_ms = static_cast<uint32_t>(std::max(
|
|
1, ObjectIntAny(serial, {"readTimeoutMs", "read_timeout_ms"})
|
|
.value_or(static_cast<int>(config.tp_uart.read_timeout_ms))));
|
|
config.tp_uart.nine_bit_mode = ObjectBoolAny(
|
|
serial, {"nineBitMode", "nine_bit_mode", "use9BitMode", "use_9_bit_mode"})
|
|
.value_or(config.tp_uart.nine_bit_mode);
|
|
}
|
|
return config;
|
|
}
|
|
|
|
DaliValue GatewayKnxConfigToValue(const GatewayKnxConfig& config) {
|
|
DaliValue::Object out;
|
|
out["daliRouterEnabled"] = config.dali_router_enabled;
|
|
out["ipRouterEnabled"] = config.ip_router_enabled;
|
|
out["tunnelEnabled"] = config.tunnel_enabled;
|
|
out["multicastEnabled"] = config.multicast_enabled;
|
|
out["etsDatabaseEnabled"] = config.ets_database_enabled;
|
|
out["mappingMode"] = GatewayKnxMappingModeToString(config.mapping_mode);
|
|
out["mainGroup"] = static_cast<int>(config.main_group);
|
|
out["daliBusId"] = static_cast<int>(config.dali_bus_id);
|
|
out["udpPort"] = static_cast<int>(config.udp_port);
|
|
out["multicastAddress"] = config.multicast_address;
|
|
out["ipInterfaceIndividualAddress"] =
|
|
static_cast<int>(config.ip_interface_individual_address);
|
|
out["individualAddress"] = static_cast<int>(config.individual_address);
|
|
out["programmingButtonGpio"] = config.programming_button_gpio;
|
|
out["programmingButtonActiveLow"] = config.programming_button_active_low;
|
|
out["programmingLedGpio"] = config.programming_led_gpio;
|
|
out["programmingLedActiveHigh"] = config.programming_led_active_high;
|
|
out["oamRouter"] = GatewayKnxOamRouterConfigToValue(config.oam_router);
|
|
DaliValue::Object serial;
|
|
serial["uartPort"] = config.tp_uart.uart_port;
|
|
serial["txPin"] = config.tp_uart.tx_pin;
|
|
serial["rxPin"] = config.tp_uart.rx_pin;
|
|
serial["baudrate"] = static_cast<int>(config.tp_uart.baudrate);
|
|
serial["rxBufferSize"] = static_cast<int>(config.tp_uart.rx_buffer_size);
|
|
serial["txBufferSize"] = static_cast<int>(config.tp_uart.tx_buffer_size);
|
|
serial["startupTimeoutMs"] = static_cast<int>(config.tp_uart.startup_timeout_ms);
|
|
serial["readTimeoutMs"] = static_cast<int>(config.tp_uart.read_timeout_ms);
|
|
serial["nineBitMode"] = config.tp_uart.nine_bit_mode;
|
|
out["tpUart"] = std::move(serial);
|
|
DaliValue::Array ets_associations;
|
|
ets_associations.reserve(config.ets_associations.size());
|
|
for (const auto& association : config.ets_associations) {
|
|
DaliValue::Object entry;
|
|
entry["groupAddress"] = static_cast<int>(association.group_address);
|
|
entry["groupObjectNumber"] = static_cast<int>(association.group_object_number);
|
|
ets_associations.emplace_back(std::move(entry));
|
|
}
|
|
out["etsAssociations"] = std::move(ets_associations);
|
|
return DaliValue(std::move(out));
|
|
}
|
|
|
|
bool GatewayKnxConfigUsesTpUart(const GatewayKnxConfig& config) {
|
|
return config.ip_router_enabled && config.tp_uart.uart_port >= 0;
|
|
}
|
|
|
|
const char* GatewayKnxMappingModeToString(GatewayKnxMappingMode mode) {
|
|
switch (mode) {
|
|
case GatewayKnxMappingMode::kEtsDatabase:
|
|
return "ets_database";
|
|
case GatewayKnxMappingMode::kGwReg1Direct:
|
|
return "gw_reg1_direct";
|
|
case GatewayKnxMappingMode::kManual:
|
|
return "manual";
|
|
case GatewayKnxMappingMode::kFormula:
|
|
default:
|
|
return "formula";
|
|
}
|
|
}
|
|
|
|
GatewayKnxMappingMode GatewayKnxMappingModeFromString(const std::string& value) {
|
|
const std::string normalized = NormalizeModeString(value);
|
|
if (normalized == "gwreg1direct" || normalized == "gwreg1" ||
|
|
normalized == "gwreg1channel" || normalized == "channelindex") {
|
|
return GatewayKnxMappingMode::kGwReg1Direct;
|
|
}
|
|
if (normalized == "manual" || normalized == "database" || normalized == "db") {
|
|
return GatewayKnxMappingMode::kManual;
|
|
}
|
|
if (normalized == "etsdatabase" || normalized == "ets" || normalized == "openknx") {
|
|
return GatewayKnxMappingMode::kEtsDatabase;
|
|
}
|
|
return GatewayKnxMappingMode::kFormula;
|
|
}
|
|
|
|
const char* GatewayKnxDataTypeToString(GatewayKnxDaliDataType data_type) {
|
|
switch (data_type) {
|
|
case GatewayKnxDaliDataType::kSwitch:
|
|
return "switch";
|
|
case GatewayKnxDaliDataType::kBrightness:
|
|
return "brightness";
|
|
case GatewayKnxDaliDataType::kBrightnessRelative:
|
|
return "brightness_relative";
|
|
case GatewayKnxDaliDataType::kColorTemperature:
|
|
return "color_temperature";
|
|
case GatewayKnxDaliDataType::kRgb:
|
|
return "rgb";
|
|
case GatewayKnxDaliDataType::kScene:
|
|
return "scene";
|
|
case GatewayKnxDaliDataType::kUnknown:
|
|
default:
|
|
return "unknown";
|
|
}
|
|
}
|
|
|
|
const char* GatewayKnxTargetKindToString(GatewayKnxDaliTargetKind kind) {
|
|
switch (kind) {
|
|
case GatewayKnxDaliTargetKind::kBroadcast:
|
|
return "broadcast";
|
|
case GatewayKnxDaliTargetKind::kShortAddress:
|
|
return "short_address";
|
|
case GatewayKnxDaliTargetKind::kGroup:
|
|
return "group";
|
|
case GatewayKnxDaliTargetKind::kNone:
|
|
default:
|
|
return "none";
|
|
}
|
|
}
|
|
|
|
std::optional<GatewayKnxDaliDataType> GatewayKnxDaliDataTypeForMiddleGroup(
|
|
uint8_t middle_group) {
|
|
switch (middle_group) {
|
|
case 1:
|
|
return GatewayKnxDaliDataType::kSwitch;
|
|
case 2:
|
|
return GatewayKnxDaliDataType::kBrightness;
|
|
case 3:
|
|
return GatewayKnxDaliDataType::kColorTemperature;
|
|
case 4:
|
|
return GatewayKnxDaliDataType::kRgb;
|
|
default:
|
|
return std::nullopt;
|
|
}
|
|
}
|
|
|
|
std::optional<GatewayKnxDaliTarget> GatewayKnxDaliTargetForSubgroup(uint8_t sub_group) {
|
|
if (sub_group == 0) {
|
|
return GatewayKnxDaliTarget{GatewayKnxDaliTargetKind::kBroadcast, 127};
|
|
}
|
|
if (sub_group >= 1 && sub_group <= 64) {
|
|
return GatewayKnxDaliTarget{GatewayKnxDaliTargetKind::kShortAddress,
|
|
static_cast<int>(sub_group - 1)};
|
|
}
|
|
if (sub_group >= 65 && sub_group <= 80) {
|
|
return GatewayKnxDaliTarget{GatewayKnxDaliTargetKind::kGroup,
|
|
static_cast<int>(sub_group - 65)};
|
|
}
|
|
return std::nullopt;
|
|
}
|
|
|
|
uint16_t GatewayKnxGroupAddress(uint8_t main_group, uint8_t middle_group,
|
|
uint8_t sub_group) {
|
|
return static_cast<uint16_t>(((main_group & 0x1f) << 11) |
|
|
((middle_group & 0x07) << 8) | sub_group);
|
|
}
|
|
|
|
std::string GatewayKnxGroupAddressString(uint16_t group_address) {
|
|
const int main = (group_address >> 11) & 0x1f;
|
|
const int middle = (group_address >> 8) & 0x07;
|
|
const int sub = group_address & 0xff;
|
|
return std::to_string(main) + "/" + std::to_string(middle) + "/" +
|
|
std::to_string(sub);
|
|
}
|
|
|
|
} // namespace gateway
|