Add secure transport and OAM router runtime implementations

- 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>
This commit is contained in:
Tony
2026-05-25 08:18:01 +08:00
parent 0467179f70
commit 2b779d5532
22 changed files with 2665 additions and 77 deletions
@@ -36,6 +36,16 @@ void GatewayKnxTpIpRouter::setGroupObjectWriteHandler(GroupObjectWriteHandler ha
group_object_write_handler_ = std::move(handler);
}
void GatewayKnxTpIpRouter::setOamIpSecureCredentials(
const GatewayKnxIpSecureCredentialMaterial& credentials) {
oam_ip_secure_credentials_ = credentials;
}
void GatewayKnxTpIpRouter::setOamIpSecureRoutingSequenceStoreHandler(
RoutingSequenceStoreHandler handler) {
routing_sequence_store_handler_ = std::move(handler);
}
const GatewayKnxConfig& GatewayKnxTpIpRouter::config() const { return config_; }
bool GatewayKnxTpIpRouter::tpUartOnline() const { return tp_uart_online_; }
@@ -69,6 +79,38 @@ esp_err_t GatewayKnxTpIpRouter::toggleProgrammingMode() {
return setProgrammingMode(!programmingMode());
}
bool GatewayKnxTpIpRouter::oamProgrammingMode() {
if (openknx_lock_ == nullptr) {
return false;
}
SemaphoreGuard guard(openknx_lock_);
return oam_router_ != nullptr ? oam_router_->programmingMode() : oam_programming_mode_;
}
esp_err_t GatewayKnxTpIpRouter::setOamProgrammingMode(bool enabled) {
if (openknx_lock_ == nullptr) {
last_error_ = "KNX runtime lock is unavailable";
return ESP_ERR_INVALID_STATE;
}
if (!config_.oam_router.enabled) {
last_error_ = "OAM KNX/IP router persona is disabled";
return ESP_ERR_NOT_SUPPORTED;
}
SemaphoreGuard guard(openknx_lock_);
oam_programming_mode_ = enabled;
if (oam_router_ != nullptr) {
oam_router_->setProgrammingMode(enabled);
}
setOamProgrammingLed(enabled);
ESP_LOGI(kTag, "OAM KNX/IP router programming mode %s namespace=%s",
enabled ? "enabled" : "disabled", openknx_namespace_.c_str());
return ESP_OK;
}
esp_err_t GatewayKnxTpIpRouter::toggleOamProgrammingMode() {
return setOamProgrammingMode(!oamProgrammingMode());
}
esp_err_t GatewayKnxTpIpRouter::start(uint32_t task_stack_size, UBaseType_t task_priority) {
if (started_ || task_handle_ != nullptr) {
return ESP_OK;
@@ -193,6 +235,19 @@ esp_err_t GatewayKnxTpIpRouter::initializeRuntime() {
bridge_.setRuntimeContext(ets_device_.get());
knx_ip_parameters_ = std::make_unique<IpParameterObject>(
ets_device_->deviceObject(), ets_device_->platform());
if (config_.oam_router.enabled) {
oam_router_ = std::make_unique<openknx::OamRouterRuntime>(
openknx_namespace_ + "_oam", config_.oam_router.individual_address,
config_.oam_router.tunnel_address_base);
if (oam_router_->available()) {
oam_router_->setProgrammingMode(oam_programming_mode_);
} else {
ESP_LOGW(kTag, "OAM router persona requested but BAU091A support is not compiled in");
oam_router_.reset();
}
} else {
oam_router_.reset();
}
openknx_configured_.store(ets_device_->configured());
ESP_LOGI(kTag,
"OpenKNX runtime namespace=%s configured=%d ipInterface=0x%04x "
@@ -200,6 +255,14 @@ esp_err_t GatewayKnxTpIpRouter::initializeRuntime() {
openknx_namespace_.c_str(), ets_device_->configured(),
effectiveIpInterfaceIndividualAddress(), ets_device_->individualAddress(),
ets_device_->tunnelClientAddress(), commissioning_only_);
if (oam_router_ != nullptr) {
ESP_LOGI(kTag,
"OAM router persona namespace=%s_oam configured=%d device=0x%04x tunnelClient=0x%04x secureTunnel=%d secureRouting=%d",
openknx_namespace_.c_str(), oam_router_->configured(),
oam_router_->individualAddress(), oam_router_->tunnelClientAddress(),
config_.oam_router.secure_tunnel_enabled,
config_.oam_router.secure_routing_enabled);
}
ets_device_->setFunctionPropertyHandlers(
[this](uint8_t object_index, uint8_t property_id, const uint8_t* data, size_t len,
std::vector<uint8_t>* response) {
@@ -300,6 +363,10 @@ void GatewayKnxTpIpRouter::taskLoop() {
if (ets_device_ != nullptr) {
pollProgrammingButton();
ets_device_->loop();
if (oam_router_ != nullptr) {
oam_router_->loop();
oam_programming_mode_ = oam_router_->programmingMode();
}
tp_uart_online_ = ets_device_->tpUartOnline();
updateProgrammingLed();
}
@@ -376,8 +443,11 @@ void GatewayKnxTpIpRouter::finishTask() {
{
SemaphoreGuard guard(openknx_lock_);
setProgrammingLed(false);
setOamProgrammingLed(false);
oam_programming_mode_ = false;
knx_ip_parameters_.reset();
bridge_.setRuntimeContext(nullptr);
oam_router_.reset();
ets_device_.reset();
openknx_configured_.store(false);
}
@@ -387,32 +457,57 @@ void GatewayKnxTpIpRouter::finishTask() {
}
void GatewayKnxTpIpRouter::pollProgrammingButton() {
if (config_.programming_button_gpio < 0 || ets_device_ == nullptr) {
const TickType_t now = xTaskGetTickCount();
if (config_.programming_button_gpio >= 0 && ets_device_ != nullptr) {
const int level = gpio_get_level(static_cast<gpio_num_t>(config_.programming_button_gpio));
const bool pressed = config_.programming_button_active_low ? level == 0 : level != 0;
if (pressed && !programming_button_last_pressed_ &&
now - programming_button_last_toggle_tick_ >= pdMS_TO_TICKS(200)) {
ets_device_->toggleProgrammingMode();
ESP_LOGI(kTag, "KNX programming mode %s namespace=%s",
ets_device_->programmingMode() ? "enabled" : "disabled",
openknx_namespace_.c_str());
programming_button_last_toggle_tick_ = now;
}
programming_button_last_pressed_ = pressed;
}
if (!config_.oam_router.enabled || config_.oam_router.programming_button_gpio < 0) {
return;
}
const int level = gpio_get_level(static_cast<gpio_num_t>(config_.programming_button_gpio));
const bool pressed = config_.programming_button_active_low ? level == 0 : level != 0;
const TickType_t now = xTaskGetTickCount();
if (pressed && !programming_button_last_pressed_ &&
now - programming_button_last_toggle_tick_ >= pdMS_TO_TICKS(200)) {
ets_device_->toggleProgrammingMode();
ESP_LOGI(kTag, "KNX programming mode %s namespace=%s",
ets_device_->programmingMode() ? "enabled" : "disabled",
const int oam_level = gpio_get_level(
static_cast<gpio_num_t>(config_.oam_router.programming_button_gpio));
const bool oam_pressed = config_.oam_router.programming_button_active_low
? oam_level == 0
: oam_level != 0;
if (oam_pressed && !oam_programming_button_last_pressed_ &&
now - oam_programming_button_last_toggle_tick_ >= pdMS_TO_TICKS(200)) {
oam_programming_mode_ = !oam_programming_mode_;
if (oam_router_ != nullptr) {
oam_router_->setProgrammingMode(oam_programming_mode_);
}
setOamProgrammingLed(oam_programming_mode_);
ESP_LOGI(kTag, "OAM KNX/IP router programming mode %s namespace=%s",
oam_programming_mode_ ? "enabled" : "disabled",
openknx_namespace_.c_str());
programming_button_last_toggle_tick_ = now;
oam_programming_button_last_toggle_tick_ = now;
}
programming_button_last_pressed_ = pressed;
oam_programming_button_last_pressed_ = oam_pressed;
}
void GatewayKnxTpIpRouter::updateProgrammingLed() {
if (config_.programming_led_gpio < 0 || ets_device_ == nullptr) {
return;
if (config_.programming_led_gpio >= 0 && ets_device_ != nullptr) {
const bool programming_mode = ets_device_->programmingMode();
if (programming_mode != programming_led_state_) {
setProgrammingLed(programming_mode);
}
}
const bool programming_mode = ets_device_->programmingMode();
if (programming_mode == programming_led_state_) {
return;
if (config_.oam_router.enabled && config_.oam_router.programming_led_gpio >= 0 &&
oam_programming_mode_ != oam_programming_led_state_) {
setOamProgrammingLed(oam_programming_mode_);
}
setProgrammingLed(programming_mode);
}
void GatewayKnxTpIpRouter::setProgrammingLed(bool on) {
@@ -425,6 +520,17 @@ void GatewayKnxTpIpRouter::setProgrammingLed(bool on) {
programming_led_state_ = on;
}
void GatewayKnxTpIpRouter::setOamProgrammingLed(bool on) {
if (config_.oam_router.programming_led_gpio < 0) {
oam_programming_led_state_ = on;
return;
}
const bool level = config_.oam_router.programming_led_active_high ? on : !on;
gpio_set_level(static_cast<gpio_num_t>(config_.oam_router.programming_led_gpio),
level ? 1 : 0);
oam_programming_led_state_ = on;
}
void GatewayKnxTpIpRouter::closeSockets() {
if (udp_sock_ >= 0) {
shutdown(udp_sock_, SHUT_RDWR);
@@ -609,6 +715,7 @@ void GatewayKnxTpIpRouter::closeTcpClient(TcpClient& client) {
resetTunnelClient(tunnel);
}
}
closeSecureSessionsForTcp(sock);
if (active_tcp_sock_ == sock) {
active_tcp_sock_ = -1;
}
@@ -754,6 +861,9 @@ bool GatewayKnxTpIpRouter::configureProgrammingGpio() {
programming_button_last_pressed_ = false;
programming_button_last_toggle_tick_ = 0;
programming_led_state_ = false;
oam_programming_button_last_pressed_ = false;
oam_programming_button_last_toggle_tick_ = 0;
oam_programming_led_state_ = false;
if (config_.programming_button_gpio >= 0) {
gpio_config_t button_config{};
@@ -792,6 +902,47 @@ bool GatewayKnxTpIpRouter::configureProgrammingGpio() {
setProgrammingLed(false);
}
if (config_.oam_router.enabled && config_.oam_router.programming_button_gpio >= 0) {
gpio_config_t button_config{};
button_config.pin_bit_mask =
1ULL << static_cast<uint32_t>(config_.oam_router.programming_button_gpio);
button_config.mode = GPIO_MODE_INPUT;
button_config.pull_up_en = config_.oam_router.programming_button_active_low
? GPIO_PULLUP_ENABLE
: GPIO_PULLUP_DISABLE;
button_config.pull_down_en = config_.oam_router.programming_button_active_low
? GPIO_PULLDOWN_DISABLE
: GPIO_PULLDOWN_ENABLE;
button_config.intr_type = GPIO_INTR_DISABLE;
const esp_err_t err = gpio_config(&button_config);
if (err != ESP_OK) {
last_error_ = EspErrDetail("failed to configure OAM KNX programming button GPIO" +
std::to_string(config_.oam_router.programming_button_gpio),
err);
ESP_LOGE(kTag, "%s", last_error_.c_str());
return false;
}
}
if (config_.oam_router.enabled && config_.oam_router.programming_led_gpio >= 0) {
gpio_config_t led_config{};
led_config.pin_bit_mask =
1ULL << static_cast<uint32_t>(config_.oam_router.programming_led_gpio);
led_config.mode = GPIO_MODE_OUTPUT;
led_config.pull_up_en = GPIO_PULLUP_DISABLE;
led_config.pull_down_en = GPIO_PULLDOWN_DISABLE;
led_config.intr_type = GPIO_INTR_DISABLE;
const esp_err_t err = gpio_config(&led_config);
if (err != ESP_OK) {
last_error_ = EspErrDetail("failed to configure OAM KNX programming LED GPIO" +
std::to_string(config_.oam_router.programming_led_gpio),
err);
ESP_LOGE(kTag, "%s", last_error_.c_str());
return false;
}
setOamProgrammingLed(false);
}
return true;
}