From 20172dcf4966ae9072e48baaaad716868dd34f9a Mon Sep 17 00:00:00 2001 From: Pawel Kurowski Date: Wed, 19 Aug 2020 16:55:35 +0200 Subject: [PATCH 1/2] Add new -o overlapped mode to ChannelLinter --- Src/ChannelLinter/AppConfig.hpp | 3 + Src/ChannelLinter/ArgumentParser.cpp | 6 ++ Src/ChannelLinter/ChannelLinter.cpp | 67 ++++++++++++++++---- Src/ChannelLinter/ChannelLinter.h | 34 ++++++++++- Src/ChannelLinter/MockDeviceBridge.cpp | 84 +++++++++++++++++++++----- Src/ChannelLinter/MockDeviceBridge.h | 65 ++++++++++++++++++++ 6 files changed, 228 insertions(+), 31 deletions(-) diff --git a/Src/ChannelLinter/AppConfig.hpp b/Src/ChannelLinter/AppConfig.hpp index 249d1cc..7ddb069 100644 --- a/Src/ChannelLinter/AppConfig.hpp +++ b/Src/ChannelLinter/AppConfig.hpp @@ -31,6 +31,9 @@ namespace FSecure::C3::Linter /// Whether application channel should test sending and receiving through the channel bool m_TestChannelIO; + /// Change test IO mode to perform Overlapped Read/Write on chunks. + bool m_OverlappedIO; + /// Command id and its arguments std::optional m_Command; }; diff --git a/Src/ChannelLinter/ArgumentParser.cpp b/Src/ChannelLinter/ArgumentParser.cpp index d1653ff..94ee042 100644 --- a/Src/ChannelLinter/ArgumentParser.cpp +++ b/Src/ChannelLinter/ArgumentParser.cpp @@ -21,6 +21,7 @@ namespace FSecure::C3::Linter m_ArgParser.addArgument("-c", "--complementary", '*'); m_ArgParser.addArgument("-i", "--test-io"); m_ArgParser.addArgument("-x", "--command", '+'); + m_ArgParser.addArgument("-o", "--overlapped"); m_ArgParser.useExceptions(true); } @@ -52,6 +53,10 @@ Options: If -c is not present, complementary channel arguments are deduced by swapping parameters from Capability/create/arguments arrays. + -o, --overlapped Changes way IO test is performed. + For each sent chunk of packet, complementary channel must read it. + Packet order test is not performed. + -x [ARGS... ], --command [ARGS... ] Execute a command with a given and arguments [ARGS...] @@ -85,6 +90,7 @@ Examples: m_Config.m_ComplementaryChannelArguments = m_ArgParser.retrieve>("complementary"); m_Config.m_TestChannelIO = m_ArgParser.exists("test-io"); + m_Config.m_OverlappedIO = m_ArgParser.exists("overlapped"); if (m_ArgParser.exists("command")) m_Config.m_Command = m_ArgParser.retrieve("command"); diff --git a/Src/ChannelLinter/ChannelLinter.cpp b/Src/ChannelLinter/ChannelLinter.cpp index 6f4e48e..1f55220 100644 --- a/Src/ChannelLinter/ChannelLinter.cpp +++ b/Src/ChannelLinter/ChannelLinter.cpp @@ -113,9 +113,10 @@ namespace FSecure::C3::Linter std::cout << "Creating complementary channel ... " << std::flush; auto complementaryArgs = GetComplementaryChannelArgs(); auto complementaryChannel = MakeChannel(complementaryArgs); + assert(complementaryChannel); std::cout << "OK" << std::endl; - TestChannelIO(channel, complementaryChannel); + TestChannelIO(*channel.get(), *complementaryChannel.get(), m_Config.m_OverlappedIO); } if (m_Config.m_Command) @@ -140,35 +141,44 @@ namespace FSecure::C3::Linter return channelBridge; } - void ChannelLinter::TestChannelIO(std::shared_ptr const& channel, std::shared_ptr const& complementary) + void ChannelLinter::TestChannelIO(MockDeviceBridge& channel, MockDeviceBridge& complementary, bool overlapped) { - assert(channel); - assert(complementary); + TestChannelMTU(channel, complementary, overlapped); + if (!overlapped) + TestChannelOrder(channel, complementary); + } + + void ChannelLinter::TestChannelMTU(MockDeviceBridge& channel, MockDeviceBridge& complementary, bool overlapped) + { for (size_t packetLen : { 8, 64, 1024, 1024 * 1024}) { std::cout << "Testing channel with " << packetLen << " bytes of data ... " << std::flush; - auto data = ByteVector(FSecure::Utils::GenerateRandomData(packetLen)); - channel->Send(data); - if (data != complementary->Receive()[0]) + auto data = ByteVector(FSecure::Utils::GenerateRandomData(packetLen)); + auto passed = overlapped ? TestOverlapped(channel, complementary, data) : TestSequential(channel, complementary, data); + if (!passed) throw std::exception("Data sent and received mismatch"); std::cout << "OK" << std::endl; } + } - auto numberOfTests = 10; - auto packetSize = 64; - std::cout << "Testing channel order with " << numberOfTests << " packets of " << packetSize << " bytes of data ... " << std::flush; + void ChannelLinter::TestChannelOrder(MockDeviceBridge& channel, MockDeviceBridge& complementary) + { + constexpr auto numberOfTests = 10; + constexpr auto packetSize = 64; std::vector sent; + std::cout << "Testing channel order with " << numberOfTests << " packets of " << packetSize << " bytes of data ... " << std::flush; + for (auto i = 0; i < numberOfTests; ++i) { sent.push_back(FSecure::Utils::GenerateRandomData(packetSize)); - channel->Send(sent[i]); + channel.Send(sent[i]); } - auto received = complementary->Receive(sent.size()); + auto received = complementary.Receive(sent.size()); received.resize(sent.size()); if (sent != received) @@ -177,6 +187,39 @@ namespace FSecure::C3::Linter std::cout << "OK" << std::endl; } + + bool ChannelLinter::TestOverlapped(MockDeviceBridge& channel, MockDeviceBridge& complementary, ByteView data) + { + auto sender = channel.GetChunkSender(data); + auto receiver = complementary.GetChunkReceiver(); + + for (auto noProgressCounter = 0; noProgressCounter < 10; ++noProgressCounter) + { + if (!sender.IsDone()) + if (sender.Send()) + noProgressCounter = 0; + + if (receiver.Receive()) + noProgressCounter = 0; + + if (receiver.Size()) + { + auto packets = receiver.GetPackets(); + return ByteView{ packets[0] } == data; + } + } + + return false; + } + + bool ChannelLinter::TestSequential(MockDeviceBridge& channel, MockDeviceBridge& complementary, ByteView data) + { + channel.Send(data); + auto received = complementary.Receive(); + return data == ByteView{ received[0] }; + } + + void ChannelLinter::TestCommand(std::shared_ptr const& channel) { assert(m_Config.m_Command); diff --git a/Src/ChannelLinter/ChannelLinter.h b/Src/ChannelLinter/ChannelLinter.h index f92b115..b0100c9 100644 --- a/Src/ChannelLinter/ChannelLinter.h +++ b/Src/ChannelLinter/ChannelLinter.h @@ -26,10 +26,38 @@ namespace FSecure::C3::Linter void TestCommand(std::shared_ptr const& channel); /// Test channel pair permeability - /// @param first of complementary channels - /// @param second of complementary channels + /// @param channel first of channels. Used to send data. + /// @param complementary second of channels. Used to receive data. + /// @param overlapped informs if operations should be performed overlapped, or first read must be preceded by write of all chunks . /// @throws if Channel::OnSend or Channel::OnReceive throws - void TestChannelIO(std::shared_ptr const& channel, std::shared_ptr const& ch2); + void TestChannelIO(MockDeviceBridge& channel, MockDeviceBridge& complementary, bool overlapped = false); + + /// Test maximal packet size that can be delivered by channel. + /// @param channel first of channels. Used to send data. + /// @param complementary second of channels. Used to receive data. + /// @param overlapped informs if operations should be performed overlapped, or first read must be preceded by write of all chunks . + /// @throws if Channel::OnSend or Channel::OnReceive throws + void TestChannelMTU(MockDeviceBridge& channel, MockDeviceBridge& complementary, bool overlapped = false); + + /// Test maximal packet size if read/write operations are overlapped. + /// @param channel first of channels. Used to send data. + /// @param complementary second of channels. Used to receive data. + /// @param overlapped informs if operations should be performed overlapped, or first read must be preceded by write of all chunks . + /// @throws if Channel::OnSend or Channel::OnReceive throws + bool TestOverlapped(MockDeviceBridge& channel, MockDeviceBridge& complementary, ByteView data); + + /// Test maximal packet size if all chunks must be written before first read. + /// @param channel first of channels. Used to send data. + /// @param complementary second of channels. Used to receive data. + /// @param overlapped informs if operations should be performed overlapped, or first read must be preceded by write of all chunks . + /// @throws if Channel::OnSend or Channel::OnReceive throws + bool TestSequential(MockDeviceBridge& channel, MockDeviceBridge& complementary, ByteView data); + + /// Test if packets are received in the same order as were passed to send. + /// @param channel first of channels. Used to send data. + /// @param complementary second of channels. Used to receive data. + /// @throws if Channel::OnSend or Channel::OnReceive throws + void TestChannelOrder(MockDeviceBridge& channel, MockDeviceBridge& complementary); /// Create channel from string channel arguments /// @param channel arguments diff --git a/Src/ChannelLinter/MockDeviceBridge.cpp b/Src/ChannelLinter/MockDeviceBridge.cpp index 0895aa5..ec35fce 100644 --- a/Src/ChannelLinter/MockDeviceBridge.cpp +++ b/Src/ChannelLinter/MockDeviceBridge.cpp @@ -92,14 +92,13 @@ namespace FSecure::C3::Linter void MockDeviceBridge::Send(ByteView blob) { - auto packetSplitter = m_QoS.GetPacketSplitter(blob); + auto sender = GetChunkSender(blob); for (auto noProgressCounter = 0; noProgressCounter < 10; ++noProgressCounter) { - auto sent = GetDevice()->OnSendToChannelInternal(packetSplitter.NextChunk()); - if (packetSplitter.Update(sent)) + if (sender.Send()) noProgressCounter = 0; - if (!packetSplitter.HasMore()) + if (sender.IsDone()) return; } @@ -108,23 +107,76 @@ namespace FSecure::C3::Linter std::vector MockDeviceBridge::Receive(size_t minExpectedSize) { - auto packets = std::vector{}; + auto receiver = GetChunkReceiver(); for (auto noProgressCounter = 0; noProgressCounter < 10; ++noProgressCounter) { - std::this_thread::sleep_for(GetDevice()->GetUpdateDelay()); - for (auto&& chunk : std::static_pointer_cast(GetDevice())->OnReceiveFromChannelInternal()) - { - if (m_QoS.PushReceivedChunk(chunk)) - noProgressCounter = 0; + if (receiver.Receive()) + noProgressCounter = 0; - if (auto packet = m_QoS.GetNextPacket(); !packet.empty()) // this form will ensure that packets are returned in same order they are available. - packets.emplace_back(std::move(packet)); - } - - if (packets.size() >= minExpectedSize) - return packets; + if (receiver.Size() >= minExpectedSize) + return receiver.GetPackets(); } throw std::runtime_error("Cannot receive data"); } + + FSecure::C3::Linter::MockDeviceBridge::ChunkSender MockDeviceBridge::GetChunkSender(ByteView blob) + { + return { *m_Device.get(), m_QoS, blob }; + } + + FSecure::C3::Linter::MockDeviceBridge::ChunkReceiver MockDeviceBridge::GetChunkReceiver() + { + return { *m_Device.get(), m_QoS }; + } + + MockDeviceBridge::ChunkSender::ChunkSender(Device& device, QualityOfService& qos, ByteView blob) + : m_Device{ device }, m_Splitter{ qos.GetPacketSplitter(blob) } + { + + } + + bool MockDeviceBridge::ChunkSender::Send() + { + auto sent = m_Device.OnSendToChannelInternal(m_Splitter.NextChunk()); + return m_Splitter.Update(sent); + } + + bool MockDeviceBridge::ChunkSender::IsDone() + { + return !m_Splitter.HasMore(); + } + + + MockDeviceBridge::ChunkReceiver::ChunkReceiver(Device& device, QualityOfService& qos) + : m_Device{ device }, m_QoS{ qos } + { + + } + + bool MockDeviceBridge::ChunkReceiver::Receive() + { + auto ret = false; + std::this_thread::sleep_for(m_Device.GetUpdateDelay()); + for (auto&& chunk : static_cast(m_Device).OnReceiveFromChannelInternal()) + { + if (m_QoS.PushReceivedChunk(chunk)) + ret = true; + + if (auto packet = m_QoS.GetNextPacket(); !packet.empty()) // this form will ensure that packets are returned in same order they are available. + m_Packets.emplace_back(std::move(packet)); + } + + return ret; + } + + size_t MockDeviceBridge::ChunkReceiver::Size() + { + return m_Packets.size(); + } + + std::vector const& MockDeviceBridge::ChunkReceiver::GetPackets() + { + return m_Packets; + } } diff --git a/Src/ChannelLinter/MockDeviceBridge.h b/Src/ChannelLinter/MockDeviceBridge.h index 4ecbdd3..0f631b9 100644 --- a/Src/ChannelLinter/MockDeviceBridge.h +++ b/Src/ChannelLinter/MockDeviceBridge.h @@ -78,6 +78,71 @@ namespace FSecure::C3::Linter /// @throws std::runtime_error if unable to return vector of required number of full packets. std::vector Receive(size_t minExpectedSize = 1); + /// Abstraction over sending chunks until all of data is transmitted. + /// This class does not participate in resource ownership. + /// Transmitting device, QoS object as well as data must be valid as long as ChunkSender is used. + class ChunkSender + { + public: + /// Constructor creating object responsible for sending blob of data in chunks. + /// @param device transmitting data over channel. + /// @param qos responsible for splitting data in chunks with correct identification headers. + /// @param blob data. + ChunkSender(Device& device, QualityOfService& qos, ByteView blob); + + /// Sends data. + /// @return true if at least one valid chunk was send. + bool Send(); + + /// All of the data was correctly send. + /// @return true if there is no more data. + bool IsDone(); + + private: + Device& m_Device; + QualityOfService::PacketSplitter m_Splitter; + }; + + /// Abstraction over receiving data. + /// This class does not participate in resource ownership. + /// Transmitting device and QoS object must be valid as long as ChunkReceiver is used. + class ChunkReceiver + { + public: + /// Constructor creating object responsible for receiving data in chunks. + /// @param device receiving data from channel. + /// @param qos responsible for merging data from chunks. + ChunkReceiver(Device& device, QualityOfService& qos); + + /// Receive data. + /// @return true if at least one valid chunk was received. + bool Receive(); + + /// Number of merged complete packets. + /// @return packet count. + size_t Size(); + + /// Get all packets. + /// Packet sequential number is not used for reordering. + /// Packets are returned in same order, as were available for merging from chunks. + /// @return complete packets. + std::vector const& GetPackets(); + + private: + Device& m_Device; + QualityOfService& m_QoS; + std::vector m_Packets; + }; + + /// Create ChunkSender using current bridge to send data. + /// @param blob data. + /// @return new ChunkSender. + ChunkSender GetChunkSender(ByteView blob); + + /// Create ChunkReceiver using current bridge to receive data. + /// @return ChunkReceiver. + ChunkReceiver GetChunkReceiver(); + private: /// Bridged device std::shared_ptr m_Device; From 8cf356c1a36a901c95a7027d9862b1baa375c2b1 Mon Sep 17 00:00:00 2001 From: Pawel Kurowski Date: Mon, 31 Aug 2020 13:20:01 +0200 Subject: [PATCH 2/2] LinterOverlapped fix review comments --- Src/ChannelLinter/ChannelLinter.cpp | 10 +++------- Src/ChannelLinter/MockDeviceBridge.cpp | 13 ++++--------- Src/ChannelLinter/MockDeviceBridge.h | 4 ---- 3 files changed, 7 insertions(+), 20 deletions(-) diff --git a/Src/ChannelLinter/ChannelLinter.cpp b/Src/ChannelLinter/ChannelLinter.cpp index 1f55220..78f5c0d 100644 --- a/Src/ChannelLinter/ChannelLinter.cpp +++ b/Src/ChannelLinter/ChannelLinter.cpp @@ -195,18 +195,14 @@ namespace FSecure::C3::Linter for (auto noProgressCounter = 0; noProgressCounter < 10; ++noProgressCounter) { - if (!sender.IsDone()) - if (sender.Send()) - noProgressCounter = 0; + if (!sender.IsDone() && sender.Send()) + noProgressCounter = 0; if (receiver.Receive()) noProgressCounter = 0; - if (receiver.Size()) - { - auto packets = receiver.GetPackets(); + if (auto && packets = receiver.GetPackets(); !packets.empty()) return ByteView{ packets[0] } == data; - } } return false; diff --git a/Src/ChannelLinter/MockDeviceBridge.cpp b/Src/ChannelLinter/MockDeviceBridge.cpp index ec35fce..78fa6f0 100644 --- a/Src/ChannelLinter/MockDeviceBridge.cpp +++ b/Src/ChannelLinter/MockDeviceBridge.cpp @@ -113,8 +113,8 @@ namespace FSecure::C3::Linter if (receiver.Receive()) noProgressCounter = 0; - if (receiver.Size() >= minExpectedSize) - return receiver.GetPackets(); + if (auto && packets = receiver.GetPackets(); packets.size() >= minExpectedSize) + return packets; } throw std::runtime_error("Cannot receive data"); @@ -122,12 +122,12 @@ namespace FSecure::C3::Linter FSecure::C3::Linter::MockDeviceBridge::ChunkSender MockDeviceBridge::GetChunkSender(ByteView blob) { - return { *m_Device.get(), m_QoS, blob }; + return { *m_Device, m_QoS, blob }; } FSecure::C3::Linter::MockDeviceBridge::ChunkReceiver MockDeviceBridge::GetChunkReceiver() { - return { *m_Device.get(), m_QoS }; + return { *m_Device, m_QoS }; } MockDeviceBridge::ChunkSender::ChunkSender(Device& device, QualityOfService& qos, ByteView blob) @@ -170,11 +170,6 @@ namespace FSecure::C3::Linter return ret; } - size_t MockDeviceBridge::ChunkReceiver::Size() - { - return m_Packets.size(); - } - std::vector const& MockDeviceBridge::ChunkReceiver::GetPackets() { return m_Packets; diff --git a/Src/ChannelLinter/MockDeviceBridge.h b/Src/ChannelLinter/MockDeviceBridge.h index 0f631b9..d02e2ba 100644 --- a/Src/ChannelLinter/MockDeviceBridge.h +++ b/Src/ChannelLinter/MockDeviceBridge.h @@ -118,10 +118,6 @@ namespace FSecure::C3::Linter /// @return true if at least one valid chunk was received. bool Receive(); - /// Number of merged complete packets. - /// @return packet count. - size_t Size(); - /// Get all packets. /// Packet sequential number is not used for reordering. /// Packets are returned in same order, as were available for merging from chunks.