mirror of https://github.com/infosecn1nja/C3.git
Add new -o overlapped mode to ChannelLinter
parent
bae99690e7
commit
20172dcf49
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue