Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
CemiFrame: change handling of Length information, _length is now always filled and used -> this fixes the PA programming issue over TP

Tunneling:
- broadcast frames from local application layer are sent via Tunnel-Hook in DLL. Only send from secondary DLL (TP) to prevent duplication of frames in tunnel mode after the fix in CemiFrame
-  frames from local application layer to the tunnel are sent to prim or sec if based on the destination address. If the tunnel address does not match own PA, the frame is routed to IP DLL and there not sent to the tunnel => change NetworkLayerCoupler::routeDataIndividual and check if destination is a tunnel

fixes OpenKNX/OAM-IP-Router#9
in IP DLL when assigning PAs to tunnel, check if the property has the relevant address, otherwise use own PA as base for tunnel PA
  • Loading branch information
Ing-Dom committed Feb 11, 2024
1 parent d368000 commit 6f368cc
Show file tree
Hide file tree
Showing 9 changed files with 86 additions and 13 deletions.
29 changes: 18 additions & 11 deletions src/knx/cemi_frame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,17 @@
/*
cEMI Frame Format
+---------+--------+--------+--------+--------+---------+---------+--------+---------+
| Header | Msg |Add.Info| Ctrl 1 | Ctrl 2 | Source | Dest. | Data | APDU |
| | Code | Length | | | Address | Address | Length | |
+--------+--------+--------+--------+---------+---------+--------+---------+
| _data |
+--------+--------+--------+--------+---------+---------+--------+---------+
| LPDU |
+--------+--------+--------+--------+---------+---------+--------+---------+
| NPDU |
+---------+--------+--------+--------+--------+---------+---------+--------+---------+
6 bytes 1 byte 1 byte 1 byte 1 byte 2 bytes 2 bytes 1 byte 2 bytes
| Header | Msg |Add.Info| Ctrl 1 | Ctrl 2 | Source | Dest. | Data | TPDU |
| | Code | Length | | | Address | Address | Length | APDU |
+---------+--------+--------+--------+--------+---------+---------+--------+---------+
6 bytes 1 byte 1 byte 1 byte 1 byte 2 bytes 2 bytes 1 byte n bytes
Header = See below the structure of a cEMI header
Message Code = See below. On Appendix A is the list of all existing EMI and cEMI codes
Expand Down Expand Up @@ -85,11 +91,11 @@ CemiFrame::CemiFrame(uint8_t apduLength)
_apdu(_data + APDU_LPDU_DIFF, *this)
{
_ctrl1 = _data + CEMI_HEADER_SIZE;
_length = 0;

memset(_data, 0, apduLength + APDU_LPDU_DIFF);
_ctrl1[0] |= Broadcast;
_npdu.octetCount(apduLength);
_length = _npdu.length() + NPDU_LPDU_DIFF;
}

CemiFrame::CemiFrame(const CemiFrame & other)
Expand All @@ -116,6 +122,7 @@ CemiFrame& CemiFrame::operator=(CemiFrame other)
return *this;
}


MessageCode CemiFrame::messageCode() const
{
return (MessageCode)_data[0];
Expand All @@ -128,12 +135,7 @@ void CemiFrame::messageCode(MessageCode msgCode)

uint16_t CemiFrame::totalLenght() const
{
if(messageCode() != L_data_con && messageCode() != L_data_ind && messageCode() != L_data_req)
return _length; // we dont have an npu on any other messagecode

uint16_t tmp =
_npdu.length() + NPDU_LPDU_DIFF;
return tmp;
return _length;
}

uint16_t CemiFrame::telegramLengthtTP() const
Expand Down Expand Up @@ -375,8 +377,13 @@ bool CemiFrame::valid() const
{
print("length issue, length: ");
print(_length);
print(" addInfoLen: ");
print(addInfoLen);
print(" apduLen: ");
print(apduLen);
print(" expected length: ");
println(addInfoLen + apduLen + NPDU_LPDU_DIFF + 2);
printHex("Frame: ", _data, _length, true);

return false;
}
Expand Down
8 changes: 7 additions & 1 deletion src/knx/data_link_layer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ void DataLinkLayer::dataIndicationToTunnel(CemiFrame& frame)
{
println("default dataIndicationToTunnel");
}

bool DataLinkLayer::isTunnelAddress(uint16_t addr)
{
println("default IsTunnelAddress");
return false;
}
#endif

void DataLinkLayer::dataRequestFromTunnel(CemiFrame& frame)
Expand Down Expand Up @@ -210,7 +216,7 @@ bool DataLinkLayer::sendTelegram(NPDU & npdu, AckType ack, uint16_t destinationA
#endif
tmpFrame.confirm(ConfirmNoError);

if(frame.sourceAddress() == _deviceObject.individualAddress())
if(frame.sourceAddress() == _deviceObject.individualAddress() && _networkLayerEntity.getEntityIndex() == 1) // only send to tunnel if we are the secondary interface
_cemiServer->dataIndicationToTunnel(tmpFrame);
#endif

Expand Down
1 change: 1 addition & 0 deletions src/knx/data_link_layer.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ class DataLinkLayer
virtual void dataRequestToTunnel(CemiFrame& frame);
virtual void dataConfirmationToTunnel(CemiFrame& frame);
virtual void dataIndicationToTunnel(CemiFrame& frame);
virtual bool isTunnelAddress(uint16_t addr);
#endif
#endif

Expand Down
37 changes: 36 additions & 1 deletion src/knx/ip_data_link_layer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,18 @@ void IpDataLinkLayer::sendFrameToTunnel(KnxIpTunnelConnection *tunnel, CemiFrame

_platform.sendBytesUniCast(tunnel->IpAddress, tunnel->PortData, req.data(), req.totalLength());
}

bool IpDataLinkLayer::isTunnelAddress(uint16_t addr)
{
if(addr == 0)
return false; // 0.0.0 is not a valid tunnel address and is used as default value

for(int i = 0; i < KNX_TUNNELING; i++)
if(tunnels[i].IndividualAddress == addr)
return true;

return false;
}
#endif

void IpDataLinkLayer::loop()
Expand Down Expand Up @@ -435,7 +447,30 @@ void IpDataLinkLayer::loopHandleConnectRequest(uint8_t* buffer, uint16_t length)
}

bool hasDoublePA = false;
const uint8_t *addresses = _ipParameters.propertyData(PID_ADDITIONAL_INDIVIDUAL_ADDRESSES);

// read current elements in PID_ADDITIONAL_INDIVIDUAL_ADDRESSES
uint8_t count = 1;
uint16_t propval = 0;
_ipParameters.readProperty(PID_ADDITIONAL_INDIVIDUAL_ADDRESSES, 0, count, (uint8_t*)&propval);
const uint8_t *addresses;
if(propval == KNX_TUNNELING)
{
addresses = _ipParameters.propertyData(PID_ADDITIONAL_INDIVIDUAL_ADDRESSES);
}
else // no tunnel PA configured, that means device is unconfigured and has 15.15.0
{
uint8_t addrbuffer[KNX_TUNNELING*2];
addresses = (uint8_t*)addrbuffer;
for(int i = 0; i < KNX_TUNNELING; i++)
{
addrbuffer[i*2+1] = i+1;
addrbuffer[i*2] = _deviceObject.individualAddress() / 0x0100;
}
#ifdef KNX_LOG_TUNNELING
println("no Tunnel-PAs configured, using own subnet");
#endif
}

for(int i = 0; i < KNX_TUNNELING; i++)
{
uint16_t pa = 0;
Expand Down
1 change: 1 addition & 0 deletions src/knx/ip_data_link_layer.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class IpDataLinkLayer : public DataLinkLayer
void dataRequestToTunnel(CemiFrame& frame) override;
void dataConfirmationToTunnel(CemiFrame& frame) override;
void dataIndicationToTunnel(CemiFrame& frame) override;
bool isTunnelAddress(uint16_t addr) override;
#endif

private:
Expand Down
14 changes: 14 additions & 0 deletions src/knx/network_layer_coupler.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "network_layer_coupler.h"
#include "data_link_layer.h"
#include "device_object.h"
#include "router_object.h"
#include "tpdu.h"
Expand Down Expand Up @@ -347,6 +348,11 @@ void NetworkLayerCoupler::routeDataIndividual(AckType ack, uint16_t destination,

// if destination is not within our scope then send via primary interface, else via secondary interface
uint8_t destIfidx = (Z != netaddr) ? kPrimaryIfIndex : kSecondaryIfIndex;
#ifdef KNX_TUNNELING
if(destIfidx == kPrimaryIfIndex)
if(isTunnelAddress(destination))
destIfidx = kSecondaryIfIndex;
#endif
//print("NetworkLayerCoupler::routeDataIndividual local to s or p: ");
//println(destIfidx);
_netLayerEntities[destIfidx].sendDataRequest(npdu, ack, destination, source, priority, AddressType::IndividualAddress, Broadcast);
Expand Down Expand Up @@ -580,3 +586,11 @@ void NetworkLayerCoupler::dataSystemBroadcastRequest(AckType ack, HopCountType h
println(broadcastType);
_netLayerEntities[kSecondaryIfIndex].sendDataRequest(tmpFrame.npdu(), ack, 0, _deviceObj.individualAddress(), priority, GroupAddress, broadcastType);
}

#ifdef KNX_TUNNELING
bool NetworkLayerCoupler::isTunnelAddress(uint16_t destination)
{
// tunnels are managed within the IpDataLinkLayer - kPrimaryIfIndex
return _netLayerEntities[kPrimaryIfIndex].dataLinkLayer().isTunnelAddress(destination);
}
#endif
3 changes: 3 additions & 0 deletions src/knx/network_layer_coupler.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ class NetworkLayerCoupler : public NetworkLayer

void evaluateCouplerType();
bool isGroupAddressInFilterTable(uint16_t groupAddress);
#ifdef KNX_TUNNELING
bool isTunnelAddress(uint16_t destination);
#endif

// Support a maximum of two physical interfaces for couplers
NetworkLayerEntity _netLayerEntities[2];
Expand Down
5 changes: 5 additions & 0 deletions src/knx/network_layer_entity.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ DataLinkLayer& NetworkLayerEntity::dataLinkLayer()
return *_dataLinkLayer;
}

NetworkLayer& NetworkLayerEntity::networkLayer()
{
return _netLayer;
}

DptMedium NetworkLayerEntity::mediumType() const
{
return _dataLinkLayer->mediumType();
Expand Down
1 change: 1 addition & 0 deletions src/knx/network_layer_entity.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class NetworkLayerEntity

void dataLinkLayer(DataLinkLayer& layer);
DataLinkLayer& dataLinkLayer();
NetworkLayer& networkLayer();

DptMedium mediumType() const;
uint8_t getEntityIndex();
Expand Down

0 comments on commit 6f368cc

Please sign in to comment.