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
@@ -8,6 +8,9 @@ void GatewayKnxTpIpRouter::selectOpenKnxNetworkInterface(const sockaddr_in& remo
if (ets_device_ != nullptr) {
ets_device_->setNetworkInterface(netif.has_value() ? netif->netif : nullptr);
}
if (oam_router_ != nullptr) {
oam_router_->setNetworkInterface(netif.has_value() ? netif->netif : nullptr);
}
}
bool GatewayKnxTpIpRouter::handleOpenKnxTunnelFrame(const uint8_t* data, size_t len,
@@ -15,6 +18,20 @@ bool GatewayKnxTpIpRouter::handleOpenKnxTunnelFrame(const uint8_t* data, size_t
uint16_t response_service,
const uint8_t* suppress_routing_echo,
size_t suppress_routing_echo_len) {
bool route_to_oam = response_client != nullptr && response_client->oam_router_persona;
if (!route_to_oam && data != nullptr && len >= 2) {
std::vector<uint8_t> frame_data(data, data + len);
CemiFrame frame(frame_data.data(), static_cast<uint16_t>(frame_data.size()));
if (frame.valid() && oam_router_ != nullptr &&
MatchesOamRouterLocalIndividualAddress(frame, *oam_router_)) {
route_to_oam = true;
}
}
if (route_to_oam) {
return handleOamRouterTunnelFrame(data, len, response_client, response_service,
suppress_routing_echo, suppress_routing_echo_len);
}
SemaphoreGuard guard(openknx_lock_);
if (ets_device_ == nullptr) {
return false;
@@ -87,6 +104,82 @@ bool GatewayKnxTpIpRouter::handleOpenKnxTunnelFrame(const uint8_t* data, size_t
return consumed;
}
bool GatewayKnxTpIpRouter::handleOamRouterTunnelFrame(const uint8_t* data, size_t len,
TunnelClient* response_client,
uint16_t response_service,
const uint8_t* suppress_routing_echo,
size_t suppress_routing_echo_len) {
SemaphoreGuard guard(openknx_lock_);
if (oam_router_ == nullptr) {
return false;
}
std::vector<uint8_t> tunnel_confirmation;
const bool needs_tunnel_confirmation =
response_client != nullptr && response_client->connected &&
response_service == kServiceTunnellingRequest &&
BuildTunnelConfirmationFrame(data, len, &tunnel_confirmation);
bool sent_tunnel_confirmation = false;
const bool consumed = oam_router_->handleTunnelFrame(
data, len,
[this, response_client, response_service, needs_tunnel_confirmation,
&tunnel_confirmation, &sent_tunnel_confirmation,
suppress_routing_echo, suppress_routing_echo_len](const uint8_t* response,
size_t response_len) {
if (response == nullptr || response_len == 0) {
return;
}
const bool routing_context =
response_client == nullptr && response_service == kServiceRoutingIndication;
const auto message_code = CemiMessageCode(response, response_len);
if (routing_context && suppress_routing_echo != nullptr &&
IsLocalRoutingEchoIndication(response, response_len, suppress_routing_echo,
suppress_routing_echo_len)) {
return;
}
if (needs_tunnel_confirmation && !sent_tunnel_confirmation &&
message_code.has_value() && message_code.value() != L_data_con) {
sent_tunnel_confirmation = sendCemiFrameToClient(
*response_client, kServiceTunnellingRequest,
tunnel_confirmation.data(), tunnel_confirmation.size());
}
const uint16_t service = KnxIpServiceForCemi(response, response_len, response_service);
if (service == kServiceDeviceConfigurationRequest) {
if (response_client != nullptr && response_client->connected) {
sendCemiFrameToClient(*response_client, service, response, response_len);
} else if (routing_context) {
sendRoutingIndication(response, response_len);
}
return;
}
if (message_code.has_value() && message_code.value() == L_data_con) {
if (routing_context) {
return;
}
if (response_client != nullptr && response_client->connected) {
sent_tunnel_confirmation =
sendCemiFrameToClient(*response_client, service, response, response_len) ||
sent_tunnel_confirmation;
}
return;
}
if (routing_context) {
sendRoutingIndication(response, response_len);
return;
}
if (response_client != nullptr && response_client->connected) {
sendCemiFrameToClient(*response_client, service, response, response_len);
return;
}
sendTunnelIndication(response, response_len);
});
if (needs_tunnel_confirmation && consumed && !sent_tunnel_confirmation) {
sendCemiFrameToClient(*response_client, kServiceTunnellingRequest,
tunnel_confirmation.data(), tunnel_confirmation.size());
}
return consumed;
}
bool GatewayKnxTpIpRouter::transmitOpenKnxTpFrame(const uint8_t* data, size_t len) {
SemaphoreGuard guard(openknx_lock_);
if (ets_device_ == nullptr) {