diff --git a/libuavcan/include/uavcan/transport/transfer_listener.hpp b/libuavcan/include/uavcan/transport/transfer_listener.hpp index e71d50f1e3..99f4efd11e 100644 --- a/libuavcan/include/uavcan/transport/transfer_listener.hpp +++ b/libuavcan/include/uavcan/transport/transfer_listener.hpp @@ -50,6 +50,11 @@ public: */ virtual void release() { } + /** + * Whether this is a rogue transfer + */ + virtual bool isRogueTransfer() const { return false; } + MonotonicTime getMonotonicTimestamp() const { return ts_mono_; } UtcTime getUtcTimestamp() const { return ts_utc_; } TransferType getTransferType() const { return transfer_type_; } @@ -68,6 +73,7 @@ class UAVCAN_EXPORT SingleFrameIncomingTransfer : public IncomingTransfer public: explicit SingleFrameIncomingTransfer(const RxFrame& frm); virtual int read(unsigned offset, uint8_t* data, unsigned len) const; + virtual bool isRogueTransfer() const; }; /** @@ -93,6 +99,7 @@ class UAVCAN_EXPORT TransferListenerBase : public LinkedListNode& receivers_; ITransferBufferManager& bufmgr_; TransferPerfCounter& perf_; + bool allow_rogue_transfers_; class TimedOutReceiverPredicate { @@ -119,17 +126,25 @@ protected: , receivers_(receivers) , bufmgr_(bufmgr) , perf_(perf) + , allow_rogue_transfers_(false) { } virtual ~TransferListenerBase() { } void handleReception(TransferReceiver& receiver, const RxFrame& frame, TransferBufferAccessor& tba); + void handleRogueTransferReception(const RxFrame& frame); virtual void handleIncomingTransfer(IncomingTransfer& transfer) = 0; public: const DataTypeDescriptor& getDataTypeDescriptor() const { return data_type_; } + /** + * By default, rogue transfers will be ignored. + * This option allows to enable reception of rogue transfers. + */ + void allowRogueTransfers() { allow_rogue_transfers_ = true; } + void cleanup(MonotonicTime ts); virtual void handleFrame(const RxFrame& frame); diff --git a/libuavcan/src/transport/uc_transfer_listener.cpp b/libuavcan/src/transport/uc_transfer_listener.cpp index d193977390..8cba8d6226 100644 --- a/libuavcan/src/transport/uc_transfer_listener.cpp +++ b/libuavcan/src/transport/uc_transfer_listener.cpp @@ -50,6 +50,11 @@ int SingleFrameIncomingTransfer::read(unsigned offset, uint8_t* data, unsigned l return int(len); } +bool SingleFrameIncomingTransfer::isRogueTransfer() const +{ + return (getTransferType() == TransferTypeMessageBroadcast) && getSrcNodeID().isBroadcast(); +} + /* * MultiFrameIncomingTransfer */ @@ -172,6 +177,16 @@ void TransferListenerBase::handleReception(TransferReceiver& receiver, const RxF } } +void TransferListenerBase::handleRogueTransferReception(const RxFrame& frame) +{ + if (allow_rogue_transfers_) + { + perf_.addRxTransfer(); + SingleFrameIncomingTransfer it(frame); + handleIncomingTransfer(it); + } +} + void TransferListenerBase::cleanup(MonotonicTime ts) { receivers_.removeWhere(TimedOutReceiverPredicate(ts, bufmgr_)); @@ -180,26 +195,40 @@ void TransferListenerBase::cleanup(MonotonicTime ts) void TransferListenerBase::handleFrame(const RxFrame& frame) { - const TransferBufferManagerKey key(frame.getSrcNodeID(), frame.getTransferType()); - - TransferReceiver* recv = receivers_.access(key); - if (recv == NULL) + if (frame.getSrcNodeID().isUnicast()) // Normal transfer { - if (!frame.isFirst()) - { - return; - } + const TransferBufferManagerKey key(frame.getSrcNodeID(), frame.getTransferType()); - TransferReceiver new_recv; - recv = receivers_.insert(key, new_recv); + TransferReceiver* recv = receivers_.access(key); if (recv == NULL) { - UAVCAN_TRACE("TransferListener", "Receiver registration failed; frame %s", frame.toString().c_str()); - return; + if (!frame.isFirst()) + { + return; + } + + TransferReceiver new_recv; + recv = receivers_.insert(key, new_recv); + if (recv == NULL) + { + UAVCAN_TRACE("TransferListener", "Receiver registration failed; frame %s", frame.toString().c_str()); + return; + } } + TransferBufferAccessor tba(bufmgr_, key); + handleReception(*recv, frame, tba); + } + else if (frame.getSrcNodeID().isBroadcast() && + frame.isFirst() && + frame.isLast() && + frame.getDstNodeID().isBroadcast()) // Rogue transfer + { + handleRogueTransferReception(frame); + } + else + { + UAVCAN_TRACE("TransferListenerBase", "Invalid frame: %s", frame.toString().c_str()); // Invalid frame } - TransferBufferAccessor tba(bufmgr_, key); - handleReception(*recv, frame, tba); } } diff --git a/libuavcan/test/transport/transfer_listener.cpp b/libuavcan/test/transport/transfer_listener.cpp index 5a4265e168..33ebf82770 100644 --- a/libuavcan/test/transport/transfer_listener.cpp +++ b/libuavcan/test/transport/transfer_listener.cpp @@ -243,3 +243,38 @@ TEST(TransferListener, MaximumTransferLength) ASSERT_TRUE(subscriber.isEmpty()); } + + +TEST(TransferListener, RogueTransfers) +{ + const uavcan::DataTypeDescriptor type(uavcan::DataTypeKindMessage, 123, uavcan::DataTypeSignature(123456789), "A"); + + uavcan::PoolManager<1> poolmgr; + uavcan::TransferPerfCounter perf; + TestListener<0, 0, 0> subscriber(perf, type, poolmgr); + + TransferListenerEmulator emulator(subscriber, type); + const Transfer transfers[] = + { + emulator.makeTransfer(uavcan::TransferTypeMessageUnicast, 0, "12345678"), // Invalid - not broadcast + emulator.makeTransfer(uavcan::TransferTypeMessageBroadcast, 0, "12345678"), // Valid + emulator.makeTransfer(uavcan::TransferTypeMessageBroadcast, 0, "123456789"), // Invalid - not SFT + emulator.makeTransfer(uavcan::TransferTypeMessageBroadcast, 0, "") // Valid + }; + + emulator.send(transfers); + + // Nothing will be received, because rogue transfers are disabled by default + ASSERT_TRUE(subscriber.isEmpty()); + + subscriber.allowRogueTransfers(); + + // Re-send everything again + emulator.send(transfers); + + // Now the rogue transfers are enabled + ASSERT_TRUE(subscriber.matchAndPop(transfers[1])); // Only SFT broadcast will be accepted + ASSERT_TRUE(subscriber.matchAndPop(transfers[3])); + + ASSERT_TRUE(subscriber.isEmpty()); +} diff --git a/libuavcan/test/transport/transfer_test_helpers.hpp b/libuavcan/test/transport/transfer_test_helpers.hpp index 449c0afb9a..bbb25c4362 100644 --- a/libuavcan/test/transport/transfer_test_helpers.hpp +++ b/libuavcan/test/transport/transfer_test_helpers.hpp @@ -129,6 +129,14 @@ public: const Transfer rx(transfer, Base::getDataTypeDescriptor()); transfers_.push(rx); std::cout << "Received transfer: " << rx.toString() << std::endl; + + const bool single_frame = dynamic_cast(&transfer) != NULL; + + const bool rogue = single_frame && + transfer.getSrcNodeID().isBroadcast() && + (transfer.getTransferType() == uavcan::TransferTypeMessageBroadcast); + + ASSERT_EQ(rogue, transfer.isRogueTransfer()); } bool matchAndPop(const Transfer& reference)