OpenVPN 3 Core Library
Loading...
Searching...
No Matches
test_reliable.cpp
Go to the documentation of this file.
1#include "test_common.hpp"
2
7
8#include <iostream>
9#include <string>
10#include <sstream>
11#include <deque>
12#include <algorithm>
13#include <limits>
14
22
23using namespace openvpn;
24
28
29TEST(Reliable, Ack)
30{
31 std::string expected{"0400000021000000160000000b00000001\n"};
32 std::ostringstream actual;
33
34 ReliableAck ack{};
35 ack.push_back(1);
36 ack.push_back(11);
37 ack.push_back(22);
38 ack.push_back(33);
39
40 ack.push_back(0x44);
41 ack.push_back(0x55);
42 ack.push_back(0x66);
43 ack.push_back(0x77);
44 ack.push_back(0x88);
45 ack.push_back(0x99);
46 ack.push_back(0x100);
47
48 BufferAllocated buf(256);
49 buf.init_headroom(128);
50 ack.prepend(buf, false);
51 actual << render_hex_generic(buf) << '\n';
52 EXPECT_EQ(actual.str(), expected);
53
54 std::string expected2{"080000002100000100000000990000008800000077000000660000005500000044\n"};
55
56 std::ostringstream actual2;
57
58 BufferAllocated buf2(256);
59 buf2.init_headroom(128);
60 ack.prepend(buf2, true);
61 actual2 << render_hex_generic(buf2) << '\n';
62 EXPECT_EQ(actual2.str(), expected2);
63}
64
65TEST(Reliable, AckDup)
66{
67 std::ostringstream actual;
68 ReliableAck ack{};
69
70 ack.push_back(1);
71 ack.push_back(2);
72 ack.push_back(3);
73 ack.push_back(4);
74
75 std::string expected{"0400000004000000030000000200000001\n"};
76
77 BufferAllocated buf(256);
78 buf.init_headroom(128);
79 ack.prepend(buf, false);
80 actual << render_hex_generic(buf) << '\n';
81 EXPECT_EQ(actual.str(), expected);
82 EXPECT_EQ(ack.resend_size(), 4U);
83
84 ack.push_back(3);
85 ack.push_back(6);
86
87 actual.str("");
88 std::string expected2{"0400000002000000040000000600000003\n"};
89
90 BufferAllocated buf2(256);
91 buf2.init_headroom(128);
92
93 ack.prepend(buf2, false);
94 actual << render_hex_generic(buf2) << '\n';
95 EXPECT_EQ(actual.str(), expected2);
96 /* The duplicate 3 should have be there only once */
97 EXPECT_EQ(ack.resend_size(), 5U);
98
99 actual.str("");
101 buf3.init_headroom(128);
102 ack.prepend(buf3, false);
103
104 actual << render_hex_generic(buf3) << '\n';
105
106 std::string expected3{"0400000002000000040000000300000006\n"};
107 EXPECT_EQ(actual.str(), expected3);
108}
109
110TEST(Reliable, SimplePacket)
111{
112 ReliableAck ack{};
113
114 BufferAllocated buf(256);
115 buf.init_headroom(128);
116
117 ack.push_back(0);
118
119 ack.prepend(buf, false);
120
121 ack.push_back(1);
122 ack.push_back(2);
123 ack.push_back(3);
124}
125
126
127OPENVPN_SIMPLE_EXCEPTION(receive_sequence);
128
129struct Packet
130{
131 Packet() = default;
132
133 explicit Packet(const BufferPtr &buf_arg)
134 : buf(buf_arg)
135 {
136 }
137 operator bool() const
138 {
139 return bool(buf);
140 }
141 void reset()
142 {
143 buf.reset();
144 }
145
147};
148
151
157
158void print_msg(const Time::Duration t,
159 const char *title,
160 BufferPtr &buf,
162 std::stringstream &case_detail)
163{
164 case_detail << t.raw() << ' ' << title
165 << '[' << id << "] " << (char *)buf->data()
166 << '\n';
167}
168
169
170void test(MTRand &rand,
171 const Time base,
172 const Time::Duration end,
173 const Time::Duration step,
174 const Time::Duration end_sends,
175 const openvpn::PacketIDControl::id_t relsize,
176 const size_t wiresize,
177 const unsigned int reorder_prob,
178 const unsigned int drop_prob,
179 std::stringstream &case_detail)
180{
181 ReliableRecv recv(relsize);
182 ReliableSend send(relsize);
183
184 std::deque<Message> wire; // simulate transmission wire
185 ReliableAck acklist{}; // back-channel for receiver to send packet ACKs back to sender
186
187 Time retrans = Time::infinite();
188
189 long count = 0;
190 long iterations = 0;
191 Time::Duration t;
192
195
196 for (t = Time::Duration(); t < end; t += step)
197 {
198 ++iterations;
199 const Time now = base + t;
200
201 // sender processes ACKs received from receiver
202 while (!acklist.empty())
203 {
204 const openvpn::PacketIDControl::id_t id = acklist.front();
205 acklist.pop_front();
206 if (rand.randrange(40)) // with small probability, simulate a dropped ACK
207 // JMD_TODO: why wouldn't this have drop_prob probability
208 {
209 case_detail << t.raw() << " ACK [" << id << "]\n";
210 send.ack(id);
211 }
212 else
213 {
214 case_detail << t.raw() << " Simulate dropped ACK [" << id << "]\n";
215 }
216 }
217
218 // scan the sender history for un-ACKed packets that need to be retransmitted
219 if (now >= retrans)
220 {
221 for (openvpn::PacketIDControl::id_t i = send.head_id(); i < send.tail_id(); ++i)
222 {
223 ReliableSend::Message &m = send.ref_by_id(i);
224 if (m.ready_retransmit(now))
225 {
226 Message msg;
227 msg.buffer = m.packet.buf;
228 msg.id = m.id();
229 print_msg(t, "RESEND", msg.buffer, msg.id, case_detail);
230 wire.push_back(msg);
231
232 m.reset_retransmit(now, Time::Duration());
233
234 // reschedule for future retransmission test
235 const Time::Duration dur = send.until_retransmit(now);
236 retrans = now + dur;
237 }
238 }
239 }
240
241 // sender constructs a packet if send object is ready to accept it
242 if (send.ready() && t < end_sends)
243 {
244 ++count;
245 std::ostringstream os;
246 os << "Test packet #" << count;
247 const std::string s = os.str();
248 auto buffer = BufferAllocatedRc::Create((unsigned char *)s.c_str(), s.length() + 1, BufAllocFlags::NO_FLAGS);
249 ReliableSend::Message &m = send.send(now, Time::Duration());
250 m.packet.buf = buffer;
251 Message msg;
252 msg.buffer = buffer;
253 send_id = msg.id = m.id();
254 print_msg(t, "SEND", msg.buffer, msg.id, case_detail);
255 wire.push_back(msg);
256
257 // at a future point in time, we will check the sender history for potential retransmits
258 retrans = now + send.until_retransmit(now);
259
260 // simulate packets being received out of order
261 if (!rand.randrange(reorder_prob) && wire.size() >= 2)
262 {
263 const size_t i1 = rand.randrange(wire.size());
264 const size_t i2 = rand.randrange(wire.size());
265 if (i1 != i2)
266 {
267 case_detail << t.raw()
268 << " Simulate packet reordering "
269 << i1 << " <-> " << i2 << '\n';
270 std::swap(wire[i1], wire[i2]);
271 }
272 }
273 }
274
275 // simulate receiving packet
276 while (wire.size() >= wiresize || (!wire.empty() && !rand.randrange(8)))
277 {
278 Message msg = wire.front();
279 wire.pop_front();
280
281 case_detail << t.raw() << " Received packet [" << msg.id << "]\n";
282
283 // simulate dropped packet
284 if (rand.randrange(drop_prob))
285 {
286 // pass packet to reliable sequencing object
287 const unsigned int recv_flags = recv.receive(Packet(msg.buffer), msg.id);
288 if (recv_flags & ReliableRecv::ACK_TO_SENDER)
289 acklist.push_back(msg.id);
290 }
291 else
292 {
293 case_detail << t.raw()
294 << " Simulate dropped packet [" << msg.id << "]\n";
295 }
296 }
297
298 // is sequenced receive packet available?
299 while (recv.ready())
300 {
301 ReliableRecv::Message &m = recv.next_sequenced();
302 print_msg(t, "RECV", m.packet.buf, m.id(), case_detail);
303 if (m.id() != rec_id)
304 throw receive_sequence();
305 else
306 rec_id = m.id() + 1;
307
308 recv.advance();
309 }
310 }
311 case_detail << "Case Summary:\nrelsize=" << relsize
312 << " wiresize=" << wiresize
313 << " reorder=" << reorder_prob
314 << " drop=" << drop_prob
315 << " final_t=" << t.raw()
316 << " iterations=" << iterations
317 << " count=" << count
318 << " [" << send_id << '/' << (rec_id ? rec_id - 1 : 0) << "]\n";
319 if (send_id != (rec_id ? rec_id - 1 : 0))
320 throw receive_sequence();
321}
322
331
332TEST(Reliable, Simulation)
333{
334 MTRand rand;
335 std::vector<test_params> sim_cases = {
336 {.test_case = 1, .relsize = 4, .wiresize = 4, .reorder_prob = 10, .drop_prob = 16},
337 {.test_case = 2, .relsize = 2, .wiresize = 4, .reorder_prob = 10, .drop_prob = 16},
338 {.test_case = 3, .relsize = 4, .wiresize = 8, .reorder_prob = 10, .drop_prob = 16},
339 {.test_case = 4, .relsize = 4, .wiresize = 4, .reorder_prob = 2, .drop_prob = 2},
340 };
341 const Time::Duration end = Time::Duration::seconds(1000);
342 const Time::Duration step = Time::Duration::binary_ms(100);
343 const Time::Duration end_sends = end - Time::Duration::seconds(5);
344 for (auto &sim_case : sim_cases)
345 {
346 const Time base = Time::now();
347 std::stringstream case_detail;
348 try
349 {
350 case_detail << "Test case " << sim_case.test_case << '\n';
351 test(rand,
352 base,
353 end,
354 step,
355 end_sends,
356 sim_case.relsize,
357 sim_case.wiresize,
358 sim_case.reorder_prob,
359 sim_case.drop_prob,
360 case_detail);
361 }
362 catch (const std::exception &e)
363 {
364 ASSERT_TRUE(false) << "Exception: " << e.what() << "\nDetail:\n"
365 << case_detail.rdbuf();
366 }
367 }
368}
369
370/*
371// following are adapted from the original unit tests in common; preserved here
372// to show the ranges of test parameters, relsize, wiresize, reorder_prob, and
373// drop_prob that the original author inttended
374
375TEST(Reliable, Test1)
376{
377 MTRand rand;
378 const Time base = Time::now();
379 const Time::Duration end = Time::Duration::seconds(10000);
380 const Time::Duration step = Time::Duration::binary_ms(100);
381 const Time::Duration end_sends = end - Time::Duration::seconds(10);
382 test(rand, base, end, step, end_sends, 4, 4, 10, 16);
383}
384
385TEST(Reliable, Test2)
386{
387 MTRand rand;
388 const Time base = Time::now();
389 const Time::Duration end = Time::Duration::seconds(1000);
390 const Time::Duration step = Time::Duration::binary_ms(100);
391 const Time::Duration end_sends = end - Time::Duration::seconds(10);
392
393 for (id_t relsize = 2; relsize <= 8; relsize += 2)
394 for (size_t wiresize = 2; wiresize <= 8; wiresize += 2)
395 for (unsigned int reorder_prob = 2; reorder_prob <= 64; reorder_prob *= 2)
396 for (unsigned int drop_prob = 2; drop_prob <= 64; drop_prob *= 2)
397 test(rand, base, end, step, end_sends, relsize, wiresize, reorder_prob, drop_prob);
398}
399*/
void init_headroom(const size_t headroom)
Initializes the headroom (offset) of the buffer.
Definition buffer.hpp:1151
void reset() noexcept
Points this RCPtr<T> to nullptr safely.
Definition rc.hpp:290
T randrange(const T end)
Return a uniformly distributed random number in the range [0, end)
Definition randapi.hpp:117
static Ptr Create(ArgsT &&...args)
Creates a new instance of RcEnable with the given arguments.
Definition make_rc.hpp:43
void push_back(id_t value)
Definition relack.hpp:39
unsigned int receive(const PACKET &packet, const id_t id)
Definition relrecv.hpp:57
void ack(const id_t id)
Definition relsend.hpp:147
Message & ref_by_id(const id_t id)
Definition relsend.hpp:95
Message & send(const Time &now, const Time::Duration &tls_timeout)
Definition relsend.hpp:132
Time::Duration until_retransmit(const Time &now)
Definition relsend.hpp:101
T raw() const
Definition time.hpp:409
static TimeType infinite()
Definition time.hpp:256
static TimeType now()
Definition time.hpp:305
#define OPENVPN_SIMPLE_EXCEPTION(C)
Definition exception.hpp:74
openvpn::reliable::id_t id_t
constexpr BufferFlags NO_FLAGS(0U)
no flags set
std::uint32_t id_t
Definition relcommon.hpp:22
std::string render_hex_generic(const V &data, const bool caps=false)
Definition hexstr.hpp:230
BufferPtr buffer
openvpn::PacketIDControl::id_t id
void reset()
Packet(const BufferPtr &buf_arg)
Packet()=default
BufferPtr buf
unsigned int reorder_prob
openvpn::PacketIDControl::id_t relsize
unsigned int drop_prob
std::ostringstream os
const std::string expected
void test()
Definition test_rc.cpp:80
TEST(Reliable, Ack)
void print_msg(const Time::Duration t, const char *title, BufferPtr &buf, const openvpn::PacketIDControl::id_t id, std::stringstream &case_detail)
#define msg(flags,...)