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:
@@ -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_;
|
||||
};
|
||||
|
||||
|
||||
@@ -32,6 +32,22 @@ constexpr const char* kTag = "gateway_knx";
|
||||
#define CONFIG_GATEWAY_KNX_OEM_HARDWARE_ID 0xA401
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_GATEWAY_KNX_OAM_ROUTER_OEM_MANUFACTURER_ID
|
||||
#define CONFIG_GATEWAY_KNX_OAM_ROUTER_OEM_MANUFACTURER_ID 0x00FA
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_GATEWAY_KNX_OAM_ROUTER_APPLICATION_NUMBER
|
||||
#define CONFIG_GATEWAY_KNX_OAM_ROUTER_APPLICATION_NUMBER 0xA11F
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_GATEWAY_KNX_OAM_ROUTER_APPLICATION_VERSION
|
||||
#define CONFIG_GATEWAY_KNX_OAM_ROUTER_APPLICATION_VERSION 0x07
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_GATEWAY_KNX_OAM_ROUTER_HARDWARE_ID
|
||||
#define CONFIG_GATEWAY_KNX_OAM_ROUTER_HARDWARE_ID 0x0001
|
||||
#endif
|
||||
|
||||
inline constexpr uint16_t kReg1DaliManufacturerId =
|
||||
static_cast<uint16_t>(CONFIG_GATEWAY_KNX_OEM_MANUFACTURER_ID);
|
||||
inline constexpr uint16_t kReg1DaliHardwareId =
|
||||
@@ -56,6 +72,33 @@ inline constexpr uint8_t kReg1DaliProgramVersion[5] = {
|
||||
static_cast<uint8_t>(kReg1DaliApplicationNumber & 0xff),
|
||||
kReg1DaliApplicationVersion};
|
||||
|
||||
inline constexpr uint32_t kReg1DaliSerialMacIncrement = 0;
|
||||
inline constexpr uint32_t kOamRouterSerialMacIncrement = 1;
|
||||
inline constexpr uint16_t kOamRouterDeviceDescriptor = 0x091A;
|
||||
inline constexpr uint16_t kOamRouterManufacturerId =
|
||||
static_cast<uint16_t>(CONFIG_GATEWAY_KNX_OAM_ROUTER_OEM_MANUFACTURER_ID);
|
||||
inline constexpr uint16_t kOamRouterHardwareId =
|
||||
static_cast<uint16_t>(CONFIG_GATEWAY_KNX_OAM_ROUTER_HARDWARE_ID);
|
||||
inline constexpr uint16_t kOamRouterApplicationNumber =
|
||||
static_cast<uint16_t>(CONFIG_GATEWAY_KNX_OAM_ROUTER_APPLICATION_NUMBER);
|
||||
inline constexpr uint8_t kOamRouterApplicationVersion =
|
||||
static_cast<uint8_t>(CONFIG_GATEWAY_KNX_OAM_ROUTER_APPLICATION_VERSION);
|
||||
inline constexpr uint8_t kOamRouterHardwareType[6] = {
|
||||
0x00,
|
||||
0x00,
|
||||
static_cast<uint8_t>((kOamRouterHardwareId >> 8) & 0xff),
|
||||
static_cast<uint8_t>(kOamRouterHardwareId & 0xff),
|
||||
kOamRouterApplicationVersion,
|
||||
0x00};
|
||||
inline constexpr uint8_t kOamRouterOrderNumber[10] = {
|
||||
'I', 'P', '-', 'R', 'o', 'u', 't', 'e', 'r', 0};
|
||||
inline constexpr uint8_t kOamRouterProgramVersion[5] = {
|
||||
static_cast<uint8_t>((kOamRouterManufacturerId >> 8) & 0xff),
|
||||
static_cast<uint8_t>(kOamRouterManufacturerId & 0xff),
|
||||
static_cast<uint8_t>((kOamRouterApplicationNumber >> 8) & 0xff),
|
||||
static_cast<uint8_t>(kOamRouterApplicationNumber & 0xff),
|
||||
kOamRouterApplicationVersion};
|
||||
|
||||
// RAII semaphore guard.
|
||||
class SemaphoreGuard {
|
||||
public:
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
|
||||
#include "esp_idf_platform.h"
|
||||
#include "ets_memory_loader.h"
|
||||
|
||||
#include "esp_netif.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "knx/cemi_frame.h"
|
||||
#include "knx/device_object.h"
|
||||
#include "knx/platform.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#if defined(ENABLE_BAU091A_PERSONA)
|
||||
#include "knx/bau091A.h"
|
||||
#endif
|
||||
|
||||
namespace gateway::openknx {
|
||||
|
||||
class OamRouterRuntime {
|
||||
public:
|
||||
using CemiFrameSender = std::function<void(const uint8_t* data, size_t len)>;
|
||||
|
||||
OamRouterRuntime(std::string nvs_namespace,
|
||||
uint16_t fallback_individual_address,
|
||||
uint16_t tunnel_client_address = 0);
|
||||
~OamRouterRuntime();
|
||||
|
||||
bool available() const;
|
||||
uint16_t individualAddress() const;
|
||||
uint16_t tunnelClientAddress() const;
|
||||
bool configured() const;
|
||||
bool programmingMode() const;
|
||||
void setProgrammingMode(bool enabled);
|
||||
void toggleProgrammingMode();
|
||||
EtsMemorySnapshot snapshot() const;
|
||||
|
||||
DeviceObject* deviceObject();
|
||||
Platform* platform();
|
||||
void setNetworkInterface(esp_netif_t* netif);
|
||||
bool handleTunnelFrame(const uint8_t* data, size_t len, CemiFrameSender sender);
|
||||
void loop();
|
||||
|
||||
private:
|
||||
static bool HandleOutboundCemiFrame(CemiFrame& frame, void* context);
|
||||
static void EmitTunnelFrame(CemiFrame& frame, void* context);
|
||||
static uint16_t DefaultTunnelClientAddress(uint16_t individual_address);
|
||||
bool shouldConsumeTunnelFrame(CemiFrame& frame) const;
|
||||
|
||||
std::string nvs_namespace_;
|
||||
CemiFrameSender sender_;
|
||||
#if defined(ENABLE_BAU091A_PERSONA)
|
||||
EspIdfPlatform platform_;
|
||||
Bau091A device_;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace gateway::openknx
|
||||
Reference in New Issue
Block a user