OpenVPN
test_packet_id.c
Go to the documentation of this file.
1/*
2 * OpenVPN -- An application to securely tunnel IP networks
3 * over a single UDP port, with support for SSL/TLS-based
4 * session authentication and key exchange,
5 * packet encryption, packet authentication, and
6 * packet compression.
7 *
8 * Copyright (C) 2016-2026 Sentyron B.V. <openvpn@sentyron.com>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2
12 * as published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program (see the file COPYING included with this
21 * distribution); if not, see <https://www.gnu.org/licenses/>.
22 */
23
24#ifdef HAVE_CONFIG_H
25#include "config.h"
26#endif
27
28#include "syshead.h"
29
30#include <stdarg.h>
31#include <stddef.h>
32#include <setjmp.h>
33#include <cmocka.h>
34
35#include "packet_id.h"
36#include "reliable.h"
37#include "test_common.h"
38
40{
41 struct
42 {
43 uint32_t buf_id;
44 uint32_t buf_time;
48 struct gc_arena gc;
49};
50
51static int
53{
54 struct test_packet_id_write_data *data = calloc(1, sizeof(struct test_packet_id_write_data));
55
56 if (!data)
57 {
58 return -1;
59 }
60
61 data->test_buf.data = (void *)&data->test_buf_data;
62 data->test_buf.capacity = sizeof(data->test_buf_data);
63 data->gc = gc_new();
64
65 *state = data;
66 return 0;
67}
68
69static int
71{
72 struct test_packet_id_write_data *data = *state;
73 gc_free(&data->gc);
74 free(*state);
75 return 0;
76}
77
78static void
80{
81 struct test_packet_id_write_data *data = *state;
82
83 now = 5010;
84 assert_true(packet_id_write(&data->pis, &data->test_buf, false, false));
85 assert_int_equal(data->pis.id, 1);
86 assert_int_equal(data->test_buf_data.buf_id, htonl(1));
87 assert_int_equal(data->test_buf_data.buf_time, 0);
88}
89
90static void
92{
93 struct test_packet_id_write_data *data = *state;
94
95 now = 5010;
96 assert_true(packet_id_write(&data->pis, &data->test_buf, true, false));
97 assert_int_equal(data->pis.id, 1);
98 assert_int_equal(data->pis.time, now);
99 assert_int_equal(data->test_buf_data.buf_id, htonl(1));
100 assert_int_equal(data->test_buf_data.buf_time, htonl((uint32_t)now));
101}
102
103static void
105{
106 struct test_packet_id_write_data *data = *state;
107
108 data->test_buf.offset = sizeof(packet_id_type);
109 now = 5010;
110 assert_true(packet_id_write(&data->pis, &data->test_buf, false, true));
111 assert_int_equal(data->pis.id, 1);
112 assert_int_equal(data->test_buf_data.buf_id, htonl(1));
113 assert_int_equal(data->test_buf_data.buf_time, 0);
114}
115
116static void
118{
119 struct test_packet_id_write_data *data = *state;
120
121 data->test_buf.offset = sizeof(data->test_buf_data);
122 now = 5010;
123 assert_true(packet_id_write(&data->pis, &data->test_buf, true, true));
124 assert_int_equal(data->pis.id, 1);
125 assert_int_equal(data->pis.time, now);
126 assert_int_equal(data->test_buf_data.buf_id, htonl(1));
127 assert_int_equal(data->test_buf_data.buf_time, htonl((uint32_t)now));
128}
129
130static void
132{
133 struct test_packet_id_write_data *data = *state;
134
135 /* maximum 32-bit packet id */
136 data->pis.id = (packet_id_type)(~0);
137 assert_false(packet_id_write(&data->pis, &data->test_buf, false, false));
138}
139
140static void
142{
143 struct test_packet_id_write_data *data = *state;
144
145 /* maximum 32-bit packet id */
146 data->pis.id = (packet_id_type)(~0);
147 data->pis.time = 5006;
148
149 /* Write fails if time did not change */
150 now = 5006;
151 assert_false(packet_id_write(&data->pis, &data->test_buf, true, false));
152
153 /* Write succeeds if time moved forward */
154 now = 5010;
155 assert_true(packet_id_write(&data->pis, &data->test_buf, true, false));
156
157 assert_int_equal(data->pis.id, 1);
158 assert_int_equal(data->pis.time, now);
159 assert_int_equal(data->test_buf_data.buf_id, htonl(1));
160 assert_int_equal(data->test_buf_data.buf_time, htonl((uint32_t)now));
161}
162
163static void
165{
166 struct reliable *rel = malloc(sizeof(struct reliable));
167 assert_non_null(rel);
168 reliable_init(rel, 100, 50, 8, false);
169
170 rel->array[5].active = true;
171 rel->array[5].packet_id = 100;
172
173 rel->packet_id = 103;
174
175 assert_int_equal(5, reliable_get_num_output_sequenced_available(rel));
176
177 rel->array[6].active = true;
178 rel->array[6].packet_id = 97;
179 assert_int_equal(2, reliable_get_num_output_sequenced_available(rel));
180
181 /* test ids close to int/unsigned int barrier */
182
183 rel->array[5].active = true;
184 rel->array[5].packet_id = (0x80000000u - 3);
185 rel->array[6].active = false;
186 rel->packet_id = (0x80000000u - 1);
187
188 assert_int_equal(6, reliable_get_num_output_sequenced_available(rel));
189
190 rel->array[5].active = true;
191 rel->array[5].packet_id = (0x80000000u - 3);
192 rel->packet_id = 0x80000001u;
193
194 assert_int_equal(4, reliable_get_num_output_sequenced_available(rel));
195
196
197 /* test wrapping */
198 rel->array[5].active = true;
199 rel->array[5].packet_id = (0xffffffffu - 3);
200 rel->array[6].active = false;
201 rel->packet_id = (0xffffffffu - 1);
202
203 assert_int_equal(6, reliable_get_num_output_sequenced_available(rel));
204
205 rel->array[2].packet_id = 0;
206 rel->array[2].active = true;
207
208 assert_int_equal(6, reliable_get_num_output_sequenced_available(rel));
209
210 rel->packet_id = 3;
211 assert_int_equal(1, reliable_get_num_output_sequenced_available(rel));
212
213 reliable_free(rel);
214}
215
216static void
218{
219 struct test_packet_id_write_data *data = *state;
220
221 struct buffer buf = alloc_buf_gc(128, &data->gc);
222
223 /* test normal writing of packet id to the buffer */
224 assert_true(packet_id_write_epoch(&data->pis, 0x23, &buf));
225
226 assert_int_equal(buf.len, 8);
227 uint8_t expected_header[8] = { 0x00, 0x23, 0, 0, 0, 0, 0, 1 };
229
230 /* too small buffer should error out */
231 struct buffer buf_short = alloc_buf_gc(5, &data->gc);
233
234 /* test a true 48 bit packet id */
235 data->pis.id = 0xfa079ab9d2e8;
236 struct buffer buf_48 = alloc_buf_gc(128, &data->gc);
238 uint8_t expected_header_48[8] = { 0xff, 0xfe, 0xfa, 0x07, 0x9a, 0xb9, 0xd2, 0xe9 };
240
241 /* test writing/checking the 48 bit per epoch packet counter
242 * overflow */
243 data->pis.id = 0xfffffffffffe;
244 struct buffer buf_of = alloc_buf_gc(128, &data->gc);
246 uint8_t expected_header_of[8] = { 0xf0, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
248
249 /* This is go over 2^48 - 1 and should error out. */
251
252 /* Now read back the packet ids and check if they are the same as what we
253 * have written */
254 struct packet_id_net pin;
255 assert_int_equal(packet_id_read_epoch(&pin, &buf), 0x23);
256 assert_int_equal(pin.id, 1);
257
258 assert_int_equal(packet_id_read_epoch(&pin, &buf_48), 0xfffe);
259 assert_int_equal(pin.id, 0xfa079ab9d2e9);
260
261 assert_int_equal(packet_id_read_epoch(&pin, &buf_of), 0xf00f);
262 assert_int_equal(pin.id, 0xffffffffffff);
263}
264
265static void
267{
268 struct reliable_ack ack = { .len = 4, .packet_id = { 2, 1, 3, 2 } };
269
270 struct reliable_ack mru_ack = { 0 };
271
272 /* Test copying to empty ack structure */
275 assert_int_equal(mru_ack.packet_id[0], 2);
276 assert_int_equal(mru_ack.packet_id[1], 1);
277 assert_int_equal(mru_ack.packet_id[2], 3);
278
279 /* Copying again should not change the result */
282 assert_int_equal(mru_ack.packet_id[0], 2);
283 assert_int_equal(mru_ack.packet_id[1], 1);
284 assert_int_equal(mru_ack.packet_id[2], 3);
285
286 /* Copying just the first two element should not change the order
287 * as they are still the most recent*/
290 assert_int_equal(mru_ack2.packet_id[0], 2);
291 assert_int_equal(mru_ack2.packet_id[1], 1);
292 assert_int_equal(mru_ack2.packet_id[2], 3);
293
294 /* Adding just two packets shoudl ignore the 42 in array and
295 * reorder the order in the MRU */
296 struct reliable_ack ack2 = { .len = 3, .packet_id = { 3, 2, 42 } };
298 assert_int_equal(mru_ack2.packet_id[0], 3);
299 assert_int_equal(mru_ack2.packet_id[1], 2);
300 assert_int_equal(mru_ack2.packet_id[2], 1);
301
302 /* Copying a zero array into it should also change nothing */
303 struct reliable_ack empty_ack = { .len = 0 };
306 assert_int_equal(mru_ack.packet_id[0], 2);
307 assert_int_equal(mru_ack.packet_id[1], 1);
308 assert_int_equal(mru_ack.packet_id[2], 3);
309
310 /* Or should just 0 elements of the ack */
313 assert_int_equal(mru_ack.packet_id[0], 2);
314 assert_int_equal(mru_ack.packet_id[1], 1);
315 assert_int_equal(mru_ack.packet_id[2], 3);
316
317 struct reliable_ack ack3 = { .len = 7, .packet_id = { 5, 6, 7, 8, 9, 10, 11 } };
318
319 /* Adding multiple acks tests if the a full array is handled correctly */
321
322 struct reliable_ack expected_ack = { .len = 8, .packet_id = { 5, 6, 7, 8, 9, 10, 11, 2 } };
324
325 assert_memory_equal(mru_ack.packet_id, expected_ack.packet_id, sizeof(expected_ack.packet_id));
326}
327
328static void
330{
331 struct reliable rel = { 0 };
332 rel.packet_id = 1;
333
334 assert_true(validate_packet_id_window(&rel, 0));
335
336 /* packet id 1 is outside the window as it is the *next* packet id */
337 assert_false(validate_packet_id_window(&rel, 1));
338
339 /* wrapped around packet id, "-2" */
340 assert_true(validate_packet_id_window(&rel, 0xFFFFFFFD));
341
342 /* wrapped around packet id, "-10" */
343 assert_true(validate_packet_id_window(&rel, 0xFFFFFFF6));
344
345 /* wrapped around packet id, "-11" */
346 assert_false(validate_packet_id_window(&rel, 0xFFFFFFF5));
347 assert_false(validate_packet_id_window(&rel, 0x80000000));
348
349 rel.packet_id = 0x80000000;
350
351 /* near the signed/usigned integer area */
352 assert_false(validate_packet_id_window(&rel, 0x80000001));
353 assert_true(validate_packet_id_window(&rel, 0x7fffffff));
354 assert_true(validate_packet_id_window(&rel, 0x7ffffff5));
355 assert_false(validate_packet_id_window(&rel, 0x7ffffff4));
356
357 rel.packet_id = 0xFFFFFFFD;
358 assert_false(validate_packet_id_window(&rel, 0xFFFFFFFD));
359 assert_false(validate_packet_id_window(&rel, 0));
360 assert_false(validate_packet_id_window(&rel, 1));
361 assert_false(validate_packet_id_window(&rel, 0xFFFFFFFE));
362 assert_false(validate_packet_id_window(&rel, 0xFFFFFFFF));
363 assert_true(validate_packet_id_window(&rel, 0xFFFFFFF3));
364 assert_true(validate_packet_id_window(&rel, 0xFFFFFFF2));
365 assert_false(validate_packet_id_window(&rel, 0xFFFFFFF1));
366
367 rel.packet_id = 500;
368 assert_false(validate_packet_id_window(&rel, 501));
369 assert_true(validate_packet_id_window(&rel, 497));
370 assert_true(validate_packet_id_window(&rel, 500 - (RELIABLE_CAPACITY - 1)));
371 assert_false(validate_packet_id_window(&rel, 500 - RELIABLE_CAPACITY));
372}
373
374
375/* The fix keeps the timeout well below this; the broken code grows past it.
376 * A plain number, so the test builds with or without the fix. */
377#define SANE_TIMEOUT_BOUND (10 * 1000 * 1000)
378
379static struct reliable *
381{
382 struct reliable *rel = malloc(sizeof(struct reliable));
383 assert_non_null(rel);
384 /* reliable_init() zeroes everything, so each test only sets the
385 * fields it actually needs. */
386 reliable_init(rel, 100, 50, 8, false);
387 rel->initial_timeout = 2;
388 return rel;
389}
390
391/*
392 * Each retransmit doubles the timeout. If a peer keeps forcing retransmits,
393 * the broken code doubles it forever: after about 30 rounds it overflows and
394 * turns zero or negative, and then the packet is resent nonstop (the flood).
395 * The fix caps the doubling. This test forces many retransmits and checks the
396 * timeout never overflows or grows without limit.
397 */
398static void
400{
401 (void)state;
402 now = 1000;
403
404 struct reliable *rel = test_reliable_new();
405
406 struct reliable_entry *e = &rel->array[0];
407 e->active = true;
408 e->packet_id = 1;
409 e->timeout = rel->initial_timeout;
410 rel->packet_id = 2;
411
412 for (int i = 0; i < 40; ++i)
413 {
414 /* make the packet due for a fast retransmit */
416
417 int opcode;
418 struct buffer *buf = reliable_send(rel, &opcode);
419 /* our one active packet is the one picked to send */
420 assert_ptr_equal(buf, &e->buf);
421
422 /* a zero or negative timeout would resend with no delay (the flood) */
423 assert_true(e->timeout > 0);
424 /* the timeout must stop growing, not double forever */
425 assert_true(e->timeout <= SANE_TIMEOUT_BOUND);
426 }
427
429}
430
431/*
432 * An ACK should only count if it is for a packet we actually sent. If the
433 * broken code accepts ACKs for packets that were never sent, a peer can force
434 * early retransmits at will (which then feeds the timeout overflow above).
435 * These two cases send such bogus ACKs and check they are ignored.
436 */
437static void
439{
440 (void)state;
441
442 /* Case (a): an ACK for pid 0x40000000, which we never sent (we only sent
443 * 0 and 1). The old "e->packet_id < pid" check treats it as newer and
444 * counts it. It should be ignored. */
445 {
446 struct reliable *rel = test_reliable_new();
447 struct reliable_entry *e = &rel->array[0];
448 e->active = true;
449 e->packet_id = 1;
450 rel->packet_id = 2; /* only pids 0 and 1 were ever sent */
451
452 struct reliable_ack ack = { .len = 1, .packet_id = { 0x40000000 } };
454
455 /* the bogus ACK must not be counted */
456 assert_int_equal(e->n_acks, 0);
457 /* and must not drop our real packet */
458 assert_true(e->active);
460 }
461
462 /* Case (b): an ACK for pid 0xFFFFFFFF. It is inside the send window, so
463 * it passes the window check, but ids wrap around and 0xFFFFFFFF is really
464 * older than our pid 1. A plain "<" thinks it is newer and counts it; the
465 * wraparound-aware comparison must not. */
466 {
467 struct reliable *rel = test_reliable_new();
468 struct reliable_entry *e = &rel->array[0];
469 e->active = true;
470 e->packet_id = 1;
471 rel->packet_id = 2;
472
473 struct reliable_ack ack = { .len = 1, .packet_id = { 0xFFFFFFFF } };
475
476 /* the bogus ACK must not be counted */
477 assert_int_equal(e->n_acks, 0);
478 /* and must not drop our real packet */
479 assert_true(e->active);
481 }
482}
483
484/*
485 * Sanity check: a real ACK must still work. Acknowledging a higher packet
486 * should drop that packet and count once towards resending the older one.
487 * The fix must not break this.
488 */
489static void
491{
492 (void)state;
493
494 struct reliable *rel = test_reliable_new();
495
496 struct reliable_entry *e0 = &rel->array[0];
497 e0->active = true;
498 e0->packet_id = 1;
499
500 struct reliable_entry *e1 = &rel->array[1];
501 e1->active = true;
502 e1->packet_id = 2;
503
504 rel->packet_id = 3; /* pids 0,1,2 sent; 1 and 2 still waiting */
505
506 struct reliable_ack ack = { .len = 1, .packet_id = { 2 } };
508
509 /* packet 2 was acked, so it is dropped */
510 assert_false(e1->active);
511 /* packet 1 is older, so it gets one ACK towards an early resend */
512 assert_int_equal(e0->n_acks, 1);
513 /* packet 1 was not acked, so it stays */
514 assert_true(e0->active);
515
517}
518
519int
520main(void)
521{
523 const struct CMUnitTest tests[] = {
524 cmocka_unit_test_setup_teardown(test_packet_id_write_short, test_packet_id_write_setup,
526 cmocka_unit_test_setup_teardown(test_packet_id_write_long, test_packet_id_write_setup,
528 cmocka_unit_test_setup_teardown(test_packet_id_write_short_prepend,
530 cmocka_unit_test_setup_teardown(test_packet_id_write_long_prepend,
532 cmocka_unit_test_setup_teardown(test_packet_id_write_short_wrap, test_packet_id_write_setup,
534 cmocka_unit_test_setup_teardown(test_packet_id_write_long_wrap, test_packet_id_write_setup,
536 cmocka_unit_test_setup_teardown(test_packet_id_write_epoch, test_packet_id_write_setup,
538
540 cmocka_unit_test(test_copy_acks_to_lru),
541 cmocka_unit_test(test_packet_id_window),
542 cmocka_unit_test(test_reliable_backoff_is_bounded),
544 cmocka_unit_test(test_reliable_purge_legitimate_ack)
545
546 };
547
548 return cmocka_run_group_tests_name("packet_id tests", tests, NULL, NULL);
549}
struct buffer alloc_buf_gc(size_t size, struct gc_arena *gc)
Allocate a buffer of the given size under garbage collection.
Definition buffer.c:77
#define BPTR(buf)
Return a pointer to the start of the buffer content.
Definition buffer.h:139
static void gc_free(struct gc_arena *a)
Free all allocations in a garbage collection arena.
Definition buffer.h:1976
static struct gc_arena gc_new(void)
Allocate and return a new, empty garbage collection arena.
Definition buffer.h:1960
void reliable_free(struct reliable *rel)
Free allocated memory associated with a reliable structure and the pointer itself.
Definition reliable.c:364
#define N_ACK_RETRANSMIT
We retry sending a packet early if this many later packets have been ACKed.
Definition reliable.h:54
int validate_packet_id_window(struct reliable *rel, packet_id_type pid)
check that pid is inside the window of possible outstanding packets of size RELIABLE_CAPACITY,...
Definition reliable.c:394
void reliable_send_purge(struct reliable *rel, const struct reliable_ack *ack)
Remove acknowledged packets from a reliable structure.
Definition reliable.c:402
struct buffer * reliable_send(struct reliable *rel, int *opcode)
Get the next packet to send to the remote peer.
Definition reliable.c:671
#define RELIABLE_CAPACITY
The maximum number of packets that the reliability layer for one VPN tunnel in one direction can stor...
Definition reliable.h:49
void reliable_init(struct reliable *rel, int buf_size, int offset, int array_size, bool hold)
Initialize a reliable structure.
Definition reliable.c:348
void copy_acks_to_mru(struct reliable_ack *ack, struct reliable_ack *ack_mru, int n)
Copies the first n acks from ack to ack_mru.
Definition reliable.c:204
int reliable_get_num_output_sequenced_available(struct reliable *rel)
Counts the number of free buffers in output that can be potentially used for sending.
Definition reliable.c:566
time_t now
Definition otime.c:33
uint16_t packet_id_read_epoch(struct packet_id_net *pin, struct buffer *buf)
Reads the packet ID containing both the epoch and the per-epoch counter from the buf.
Definition packet_id.c:650
bool packet_id_write_epoch(struct packet_id_send *p, uint16_t epoch, struct buffer *buf)
Writes the packet ID containing both the epoch and the packet id to the buffer specified by buf.
Definition packet_id.c:673
bool packet_id_write(struct packet_id_send *p, struct buffer *buf, bool long_form, bool prepend)
Write a packet ID to buf, and update the packet ID state.
Definition packet_id.c:380
uint32_t packet_id_type
Definition packet_id.h:45
Reliability Layer module header file.
Wrapper structure for dynamically allocated memory.
Definition buffer.h:71
int capacity
Size in bytes of memory allocated by malloc().
Definition buffer.h:72
uint8_t * data
Pointer to the allocated memory.
Definition buffer.h:78
int len
Length in bytes of the actual content within the allocated memory.
Definition buffer.h:76
int offset
Offset in bytes of the actual content within the allocated memory.
Definition buffer.h:74
Garbage collection arena used to keep track of dynamically allocated memory.
Definition buffer.h:127
Data structure for describing the packet id that is received/send to the network.
Definition packet_id.h:191
uint64_t id
Definition packet_id.h:194
uint64_t id
Definition packet_id.h:153
The acknowledgment structure in which packet IDs are stored for later acknowledgment.
Definition reliable.h:75
The structure in which the reliability layer stores a single incoming or outgoing packet.
Definition reliable.h:88
int opcode
Definition reliable.h:96
size_t n_acks
Definition reliable.h:93
packet_id_type packet_id
Definition reliable.h:92
bool active
Definition reliable.h:89
interval_t timeout
Definition reliable.h:90
The reliability layer storage structure for one VPN tunnel's control channel in one direction.
Definition reliable.h:105
struct reliable_entry array[RELIABLE_CAPACITY]
Definition reliable.h:111
packet_id_type packet_id
Packet ID for the next packet to be sent out.
Definition reliable.h:108
interval_t initial_timeout
Definition reliable.h:107
struct packet_id_send pis
struct test_packet_id_write_data::@31 test_buf_data
static void openvpn_unit_test_setup(void)
Sets up the environment for unit tests like making both stderr and stdout non-buffered to avoid messa...
Definition test_common.h:61
static int test_packet_id_write_setup(void **state)
static void test_packet_id_write_epoch(void **state)
static void test_packet_id_write_long_prepend(void **state)
static void test_packet_id_write_short_wrap(void **state)
static void test_packet_id_write_short_prepend(void **state)
static void test_packet_id_window(void **state)
static void test_reliable_backoff_is_bounded(void **state)
#define SANE_TIMEOUT_BOUND
static void test_reliable_purge_ignores_forged_acks(void **state)
static void test_packet_id_write_long(void **state)
static int test_packet_id_write_teardown(void **state)
int main(void)
static void test_reliable_purge_legitimate_ack(void **state)
static void test_get_num_output_sequenced_available(void **state)
static struct reliable * test_reliable_new(void)
static void test_copy_acks_to_lru(void **state)
static void test_packet_id_write_long_wrap(void **state)
static void test_packet_id_write_short(void **state)