Add new -o overlapped mode to ChannelLinter

master
Pawel Kurowski 2020-08-19 16:55:35 +02:00
parent bae99690e7
commit 20172dcf49
6 changed files with 228 additions and 31 deletions

View File

@ -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<StringVector> m_Command;
};

View File

@ -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 <ID> [ARGS... ], --command <ID> [ARGS... ]
Execute a command with a given <ID> and arguments [ARGS...]
@ -85,6 +90,7 @@ Examples:
m_Config.m_ComplementaryChannelArguments = m_ArgParser.retrieve<std::vector<std::string>>("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<StringVector>("command");

View File

@ -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<MockDeviceBridge> const& channel, std::shared_ptr<MockDeviceBridge> 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<ByteVector> 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<MockDeviceBridge> const& channel)
{
assert(m_Config.m_Command);

View File

@ -26,10 +26,38 @@ namespace FSecure::C3::Linter
void TestCommand(std::shared_ptr<MockDeviceBridge> 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<MockDeviceBridge> const& channel, std::shared_ptr<MockDeviceBridge> 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

View File

@ -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<FSecure::ByteVector> MockDeviceBridge::Receive(size_t minExpectedSize)
{
auto packets = std::vector<ByteVector>{};
auto receiver = GetChunkReceiver();
for (auto noProgressCounter = 0; noProgressCounter < 10; ++noProgressCounter)
{
std::this_thread::sleep_for(GetDevice()->GetUpdateDelay());
for (auto&& chunk : std::static_pointer_cast<C3::AbstractChannel>(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<C3::AbstractChannel&>(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<FSecure::ByteVector> const& MockDeviceBridge::ChunkReceiver::GetPackets()
{
return m_Packets;
}
}

View File

@ -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<ByteVector> 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<ByteVector> const& GetPackets();
private:
Device& m_Device;
QualityOfService& m_QoS;
std::vector<ByteVector> 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<Device> m_Device;