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) << std::endl;
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) << std::endl;
62 EXPECT_EQ(actual2.str(), expected2);
63}
64
65TEST(reliable, ack_dup)
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) << std::endl;
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) << std::endl;
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) << std::endl;
105
106 std::string expected3{"0400000002000000040000000300000006\n"};
107 EXPECT_EQ(actual.str(), expected3);
108}
109
110TEST(reliable, simple_packet)
111{
112 std::ostringstream actual;
113 ReliableAck ack{};
114
115 BufferAllocated buf(256);
116 buf.init_headroom(128);
117
118 ack.push_back(0);
119
120 ack.prepend(buf, false);
121
122
123 ack.push_back(1);
124 ack.push_back(2);
125 ack.push_back(3);
126}
127
128
129OPENVPN_SIMPLE_EXCEPTION(receive_sequence);
130
131struct Packet
132{
134 {
135 }
136 explicit Packet(const BufferPtr &buf_arg)
137 : buf(buf_arg)
138 {
139 }
140 operator bool() const
141 {
142 return bool(buf);
143 }
144 void reset()
145 {
146 buf.reset();
147 }
148
150};
151
154
160
161void print_msg(const Time::Duration t,
162 const char *title,
163 BufferPtr &buf,
165 std::stringstream &case_detail)
166{
167 case_detail << t.raw() << ' ' << title
168 << '[' << id << "] " << (char *)buf->data()
169 << std::endl;
170}
171
172
173void test(MTRand &rand,
174 const Time base,
175 const Time::Duration end,
176 const Time::Duration step,
177 const Time::Duration end_sends,
178 const openvpn::PacketIDControl::id_t relsize,
179 const size_t wiresize,
180 const unsigned int reorder_prob,
181 const unsigned int drop_prob,
182 std::stringstream &case_detail)
183{
184 ReliableRecv recv(relsize);
185 ReliableSend send(relsize);
186
187 std::deque<Message> wire; // simulate transmission wire
188 ReliableAck acklist{}; // back-channel for receiver to send packet ACKs back to sender
189
190 Time retrans = Time::infinite();
191
192 long count = 0;
193 long iterations = 0;
194 Time::Duration t;
195
198
199 for (t = Time::Duration(); t < end; t += step)
200 {
201 ++iterations;
202 const Time now = base + t;
203
204 // sender processes ACKs received from receiver
205 while (!acklist.empty())
206 {
207 const openvpn::PacketIDControl::id_t id = acklist.front();
208 acklist.pop_front();
209 if (rand.randrange(40)) // with small probability, simulate a dropped ACK
210 // JMD_TODO: why wouldn't this have drop_prob probability
211 {
212 case_detail << t.raw() << " ACK [" << id << "]" << std::endl;
213 send.ack(id);
214 }
215 else
216 {
217 case_detail << t.raw() << " Simulate dropped ACK [" << id << "]" << std::endl;
218 }
219 }
220
221 // scan the sender history for un-ACKed packets that need to be retransmitted
222 if (now >= retrans)
223 {
224 for (openvpn::PacketIDControl::id_t i = send.head_id(); i < send.tail_id(); ++i)
225 {
226 ReliableSend::Message &m = send.ref_by_id(i);
227 if (m.ready_retransmit(now))
228 {
229 Message msg;
230 msg.buffer = m.packet.buf;
231 msg.id = m.id();
232 print_msg(t, "RESEND", msg.buffer, msg.id, case_detail);
233 wire.push_back(msg);
234
235 m.reset_retransmit(now, Time::Duration());
236
237 // reschedule for future retransmission test
238 const Time::Duration dur = send.until_retransmit(now);
239 retrans = now + dur;
240 }
241 }
242 }
243
244 // sender constructs a packet if send object is ready to accept it
245 if (send.ready() && t < end_sends)
246 {
247 ++count;
248 std::ostringstream os;
249 os << "Test packet #" << count;
250 const std::string s = os.str();
251 auto buffer = BufferAllocatedRc::Create((unsigned char *)s.c_str(), s.length() + 1, BufAllocFlags::NO_FLAGS);
252 ReliableSend::Message &m = send.send(now, Time::Duration());
253 m.packet.buf = buffer;
254 Message msg;
255 msg.buffer = buffer;
256 send_id = msg.id = m.id();
257 print_msg(t, "SEND", msg.buffer, msg.id, case_detail);
258 wire.push_back(msg);
259
260 // at a future point in time, we will check the sender history for potential retransmits
261 retrans = now + send.until_retransmit(now);
262
263 // simulate packets being received out of order
264 if (!rand.randrange(reorder_prob) && wire.size() >= 2)
265 {
266 const size_t i1 = rand.randrange(wire.size());
267 const size_t i2 = rand.randrange(wire.size());
268 if (i1 != i2)
269 {
270 case_detail << t.raw()
271 << " Simulate packet reordering "
272 << i1 << " <-> " << i2 << std::endl;
273 std::swap(wire[i1], wire[i2]);
274 }
275 }
276 }
277
278 // simulate receiving packet
279 while (wire.size() >= wiresize || (!wire.empty() && !rand.randrange(8)))
280 {
281 Message msg = wire.front();
282 wire.pop_front();
283
284 case_detail << t.raw() << " Received packet [" << msg.id << "]" << std::endl;
285
286 // simulate dropped packet
287 if (rand.randrange(drop_prob))
288 {
289 // pass packet to reliable sequencing object
290 const unsigned int recv_flags = recv.receive(Packet(msg.buffer), msg.id);
291 if (recv_flags & ReliableRecv::ACK_TO_SENDER)
292 acklist.push_back(msg.id);
293 }
294 else
295 {
296 case_detail << t.raw()
297 << " Simulate dropped packet [" << msg.id << "]" << std::endl;
298 }
299 }
300
301 // is sequenced receive packet available?
302 while (recv.ready())
303 {
304 ReliableRecv::Message &m = recv.next_sequenced();
305 print_msg(t, "RECV", m.packet.buf, m.id(), case_detail);
306 if (m.id() != rec_id)
307 throw receive_sequence();
308 else
309 rec_id = m.id() + 1;
310
311 recv.advance();
312 }
313 }
314 case_detail << "Case Summary:\nrelsize=" << relsize
315 << " wiresize=" << wiresize
316 << " reorder=" << reorder_prob
317 << " drop=" << drop_prob
318 << " final_t=" << t.raw()
319 << " iterations=" << iterations
320 << " count=" << count
321 << " [" << send_id << '/' << (rec_id ? rec_id - 1 : 0) << ']'
322 << std::endl;
323 if (send_id != (rec_id ? rec_id - 1 : 0))
324 throw receive_sequence();
325}
326
335
336TEST(reliable, simulation)
337{
338 MTRand rand;
339 std::vector<test_params> sim_cases = {
340 {1, 4, 4, 10, 16},
341 {2, 2, 4, 10, 16},
342 {3, 4, 8, 10, 16},
343 {4, 4, 4, 2, 2},
344 };
345 const Time::Duration end = Time::Duration::seconds(1000);
346 const Time::Duration step = Time::Duration::binary_ms(100);
347 const Time::Duration end_sends = end - Time::Duration::seconds(5);
348 for (auto &sim_case : sim_cases)
349 {
350 const Time base = Time::now();
351 std::stringstream case_detail;
352 try
353 {
354 case_detail << "Test case " << sim_case.test_case << std::endl;
355 test(rand,
356 base,
357 end,
358 step,
359 end_sends,
360 sim_case.relsize,
361 sim_case.wiresize,
362 sim_case.reorder_prob,
363 sim_case.drop_prob,
364 case_detail);
365 }
366 catch (const std::exception &e)
367 {
368 ASSERT_TRUE(false) << "Exception: " << e.what() << "\nDetail:\n"
369 << case_detail.rdbuf();
370 }
371 }
372}
373
374/*
375// following are adapted from the original unit tests in common; preserved here
376// to show the ranges of test parameters, relsize, wiresize, reorder_prob, and
377// drop_prob that the original author inttended
378
379TEST(reliable, test1)
380{
381 MTRand rand;
382 const Time base = Time::now();
383 const Time::Duration end = Time::Duration::seconds(10000);
384 const Time::Duration step = Time::Duration::binary_ms(100);
385 const Time::Duration end_sends = end - Time::Duration::seconds(10);
386 test(rand, base, end, step, end_sends, 4, 4, 10, 16);
387}
388
389TEST(reliable, test2)
390{
391 MTRand rand;
392 const Time base = Time::now();
393 const Time::Duration end = Time::Duration::seconds(1000);
394 const Time::Duration step = Time::Duration::binary_ms(100);
395 const Time::Duration end_sends = end - Time::Duration::seconds(10);
396
397 for (id_t relsize = 2; relsize <= 8; relsize += 2)
398 for (size_t wiresize = 2; wiresize <= 8; wiresize += 2)
399 for (unsigned int reorder_prob = 2; reorder_prob <= 64; reorder_prob *= 2)
400 for (unsigned int drop_prob = 2; drop_prob <= 64; drop_prob *= 2)
401 test(rand, base, end, step, end_sends, relsize, wiresize, reorder_prob, drop_prob);
402}
403*/
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:75
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)
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
ReliableRecvTemplate< Packet > ReliableRecv
ReliableSendTemplate< Packet > ReliableSend
void print_msg(const Time::Duration t, const char *title, BufferPtr &buf, const openvpn::PacketIDControl::id_t id, std::stringstream &case_detail)
TEST(reliable, ack)
#define msg(flags,...)