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
+103 -2
View File
@@ -27,6 +27,7 @@ namespace gateway {
namespace openknx {
class EtsDeviceRuntime;
class OamRouterRuntime;
class TpuartUartInterface;
}
@@ -59,6 +60,40 @@ struct GatewayKnxEtsAssociation {
uint16_t group_object_number{0};
};
struct GatewayKnxCloudRemoteConfig {
bool enabled{false};
std::string mode{"mqtt"};
std::string relay_endpoint;
std::string mqtt_topic_prefix;
std::string auth_token_ref;
bool require_secure_tunnel{true};
bool udp_punch_enabled{false};
};
struct GatewayKnxOamRouterConfig {
bool enabled{false};
bool ets_database_enabled{true};
bool secure_tunnel_enabled{true};
bool secure_routing_enabled{true};
uint16_t individual_address{0xff02};
uint16_t tunnel_address_base{0xff10};
int programming_button_gpio{-1};
bool programming_button_active_low{true};
int programming_led_gpio{-1};
bool programming_led_active_high{true};
GatewayKnxCloudRemoteConfig cloud_remote;
};
struct GatewayKnxIpSecureCredentialMaterial {
bool activated{false};
bool backbone_key_available{false};
std::array<uint8_t, 16> backbone_key{};
bool device_authentication_key_available{false};
std::array<uint8_t, 16> device_authentication_key{};
std::vector<std::array<uint8_t, 16>> tunnel_user_keys;
uint64_t routing_sequence{0};
};
struct GatewayKnxConfig {
bool dali_router_enabled{true};
bool ip_router_enabled{false};
@@ -76,6 +111,7 @@ struct GatewayKnxConfig {
bool programming_button_active_low{true};
int programming_led_gpio{-1};
bool programming_led_active_high{true};
GatewayKnxOamRouterConfig oam_router;
std::vector<GatewayKnxEtsAssociation> ets_associations;
GatewayKnxTpUartConfig tp_uart;
};
@@ -134,6 +170,9 @@ struct GatewayKnxReg1ScanOptions {
std::optional<GatewayKnxConfig> GatewayKnxConfigFromValue(const DaliValue* value);
DaliValue GatewayKnxConfigToValue(const GatewayKnxConfig& config);
std::optional<GatewayKnxOamRouterConfig> GatewayKnxOamRouterConfigFromValue(
const DaliValue* value);
DaliValue GatewayKnxOamRouterConfigToValue(const GatewayKnxOamRouterConfig& config);
bool GatewayKnxConfigUsesTpUart(const GatewayKnxConfig& config);
const char* GatewayKnxMappingModeToString(GatewayKnxMappingMode mode);
@@ -228,6 +267,7 @@ class GatewayKnxTpIpRouter {
using GroupObjectWriteHandler = std::function<DaliBridgeResult(uint16_t group_object_number,
const uint8_t* data,
size_t len)>;
using RoutingSequenceStoreHandler = std::function<void(uint64_t sequence)>;
GatewayKnxTpIpRouter(GatewayKnxBridge& bridge,
std::string openknx_namespace = "openknx");
@@ -237,11 +277,16 @@ class GatewayKnxTpIpRouter {
void setCommissioningOnly(bool enabled);
void setGroupWriteHandler(GroupWriteHandler handler);
void setGroupObjectWriteHandler(GroupObjectWriteHandler handler);
void setOamIpSecureCredentials(const GatewayKnxIpSecureCredentialMaterial& credentials);
void setOamIpSecureRoutingSequenceStoreHandler(RoutingSequenceStoreHandler handler);
const GatewayKnxConfig& config() const;
bool tpUartOnline() const;
bool programmingMode();
esp_err_t setProgrammingMode(bool enabled);
esp_err_t toggleProgrammingMode();
bool oamProgrammingMode();
esp_err_t setOamProgrammingMode(bool enabled);
esp_err_t toggleOamProgrammingMode();
esp_err_t start(uint32_t task_stack_size, UBaseType_t task_priority);
esp_err_t stop();
@@ -285,6 +330,24 @@ class GatewayKnxTpIpRouter {
::sockaddr_in data_remote{};
std::vector<uint8_t> last_received_cemi;
std::vector<uint8_t> last_tunnel_confirmation_packet;
uint16_t secure_session_id{0};
bool oam_router_persona{false};
};
struct SecureSession {
bool active{false};
bool authenticated{false};
uint16_t session_id{0};
int tcp_sock{-1};
::sockaddr_in remote{};
std::array<uint8_t, 32> client_public_key{};
std::array<uint8_t, 32> server_public_key{};
std::array<uint8_t, 32> shared_secret{};
std::array<uint8_t, 16> session_key{};
uint64_t send_sequence{0};
uint64_t receive_sequence{0};
uint8_t user_id{0};
TickType_t last_activity_tick{0};
};
static void TaskEntry(void* arg);
@@ -297,6 +360,7 @@ class GatewayKnxTpIpRouter {
void handleTcpAccept();
void handleTcpClient(TcpClient& client);
void closeTcpClient(TcpClient& client);
void closeSecureSessionsForTcp(int sock);
std::unique_ptr<openknx::TpuartUartInterface> createOpenKnxTpUartInterface();
bool configureTpUart();
bool configureProgrammingGpio();
@@ -316,6 +380,12 @@ class GatewayKnxTpIpRouter {
void handleDisconnectRequest(const uint8_t* packet_data, size_t len, const ::sockaddr_in& remote);
void handleSecureService(uint16_t service, const uint8_t* body, size_t len,
const ::sockaddr_in& remote);
void handleSecureSessionRequest(const uint8_t* body, size_t len,
const ::sockaddr_in& remote);
void handleSecureWrapper(const uint8_t* body, size_t len,
const ::sockaddr_in& remote);
void handleSecureGroupSync(const uint8_t* body, size_t len,
const ::sockaddr_in& remote);
void sendTunnellingAck(uint8_t channel_id, uint8_t sequence, uint8_t status,
const ::sockaddr_in& remote);
void sendDeviceConfigurationAck(uint8_t channel_id, uint8_t sequence, uint8_t status,
@@ -335,9 +405,9 @@ class GatewayKnxTpIpRouter {
const ::sockaddr_in& remote, uint8_t connection_type,
uint16_t tunnel_address);
void sendRoutingIndication(const uint8_t* data, size_t len);
bool sendPacket(const std::vector<uint8_t>& packet, const ::sockaddr_in& remote) const;
bool sendPacket(const std::vector<uint8_t>& packet, const ::sockaddr_in& remote);
bool sendPacketToTunnelClient(const TunnelClient& client,
const std::vector<uint8_t>& packet) const;
const std::vector<uint8_t>& packet);
bool currentTransportAllowsTcpHpai() const;
std::optional<std::array<uint8_t, 8>> localHpaiForRemote(const ::sockaddr_in& remote,
bool tcp = false) const;
@@ -359,6 +429,22 @@ class GatewayKnxTpIpRouter {
const ::sockaddr_in& data_remote,
uint8_t connection_type);
void resetTunnelClient(TunnelClient& client);
SecureSession* findSecureSession(uint16_t session_id, const ::sockaddr_in& remote);
const SecureSession* findSecureSession(uint16_t session_id,
const ::sockaddr_in& remote) const;
SecureSession* allocateSecureSession(const ::sockaddr_in& remote);
SecureSession* activeSecureSession();
bool wrapSecurePacket(SecureSession& session, const std::vector<uint8_t>& inner,
std::vector<uint8_t>* wrapped);
bool wrapSecureRoutingPacket(const std::vector<uint8_t>& inner,
std::vector<uint8_t>* wrapped);
bool decryptSecureWrapper(SecureSession& session, const uint8_t* body, size_t len,
std::vector<uint8_t>* inner);
bool decryptSecureRoutingWrapper(const uint8_t* body, size_t len,
std::vector<uint8_t>* inner);
bool verifySecureSessionAuth(SecureSession& session, const uint8_t* packet,
size_t len, uint8_t* status);
bool secureCredentialsReady() const;
uint8_t nextTunnelChannelId() const;
uint16_t effectiveTunnelAddressForSlot(size_t slot) const;
void pruneStaleTunnelClients();
@@ -366,6 +452,10 @@ class GatewayKnxTpIpRouter {
TunnelClient* response_client, uint16_t response_service,
const uint8_t* suppress_routing_echo = nullptr,
size_t suppress_routing_echo_len = 0);
bool handleOamRouterTunnelFrame(const uint8_t* data, size_t len,
TunnelClient* response_client, uint16_t response_service,
const uint8_t* suppress_routing_echo = nullptr,
size_t suppress_routing_echo_len = 0);
bool handleOpenKnxBusFrame(const uint8_t* data, size_t len);
bool transmitOpenKnxTpFrame(const uint8_t* data, size_t len);
void selectOpenKnxNetworkInterface(const ::sockaddr_in& remote);
@@ -380,13 +470,16 @@ class GatewayKnxTpIpRouter {
void pollProgrammingButton();
void updateProgrammingLed();
void setProgrammingLed(bool on);
void setOamProgrammingLed(bool on);
GatewayKnxBridge& bridge_;
GroupWriteHandler group_write_handler_;
GroupObjectWriteHandler group_object_write_handler_;
RoutingSequenceStoreHandler routing_sequence_store_handler_;
std::string openknx_namespace_;
GatewayKnxConfig config_;
std::unique_ptr<openknx::EtsDeviceRuntime> ets_device_;
std::unique_ptr<openknx::OamRouterRuntime> oam_router_;
TaskHandle_t task_handle_{nullptr};
SemaphoreHandle_t openknx_lock_{nullptr};
SemaphoreHandle_t startup_semaphore_{nullptr};
@@ -403,14 +496,22 @@ class GatewayKnxTpIpRouter {
TickType_t network_refresh_tick_{0};
std::array<TcpClient, kMaxTcpClients> tcp_clients_{};
std::array<TunnelClient, kMaxTunnelClients> tunnel_clients_{};
std::array<SecureSession, kMaxTcpClients> secure_sessions_{};
std::unique_ptr<IpParameterObject> knx_ip_parameters_;
uint8_t last_tunnel_channel_id_{0};
uint16_t last_secure_session_id_{0};
uint16_t active_secure_session_id_{0};
GatewayKnxIpSecureCredentialMaterial oam_ip_secure_credentials_{};
bool tp_uart_online_{false};
bool commissioning_only_{false};
std::atomic_bool openknx_configured_{false};
bool programming_button_last_pressed_{false};
bool programming_led_state_{false};
TickType_t programming_button_last_toggle_tick_{0};
bool oam_programming_mode_{false};
bool oam_programming_button_last_pressed_{false};
bool oam_programming_led_state_{false};
TickType_t oam_programming_button_last_toggle_tick_{0};
std::string last_error_;
};