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) 2002-2022 OpenVPN Inc <sales@openvpn.net>
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 along
20  *  with this program; if not, write to the Free Software Foundation, Inc.,
21  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22  */
23 
24 /*
25  * These routines implement a reliability layer on top of UDP,
26  * so that SSL/TLS can be run over UDP.
27  */
28 
29 #ifdef HAVE_CONFIG_H
30 #include "config.h"
31 #elif defined(_MSC_VER)
32 #include "config-msvc.h"
33 #endif
34 
35 #include "syshead.h"
36 
37 #include "buffer.h"
38 #include "error.h"
39 #include "common.h"
40 #include "reliable.h"
41 
42 #include "memdbg.h"
43 
44 /*
45  * verify that test - base < extent while allowing for base or test wraparound
46  */
47 static inline bool
reliable_pid_in_range1(const packet_id_type test,const packet_id_type base,const unsigned int extent)48 reliable_pid_in_range1(const packet_id_type test,
49                        const packet_id_type base,
50                        const unsigned int extent)
51 {
52     if (test >= base)
53     {
54         if (test - base < extent)
55         {
56             return true;
57         }
58     }
59     else
60     {
61         if ((test+0x80000000u) - (base+0x80000000u) < extent)
62         {
63             return true;
64         }
65     }
66 
67     return false;
68 }
69 
70 /*
71  * verify that test < base + extent while allowing for base or test wraparound
72  */
73 static inline bool
reliable_pid_in_range2(const packet_id_type test,const packet_id_type base,const unsigned int extent)74 reliable_pid_in_range2(const packet_id_type test,
75                        const packet_id_type base,
76                        const unsigned int extent)
77 {
78     if (base + extent >= base)
79     {
80         if (test < base + extent)
81         {
82             return true;
83         }
84     }
85     else
86     {
87         if ((test+0x80000000u) < (base+0x80000000u) + extent)
88         {
89             return true;
90         }
91     }
92 
93     return false;
94 }
95 
96 /*
97  * verify that p1 < p2  while allowing for p1 or p2 wraparound
98  */
99 static inline bool
reliable_pid_min(const packet_id_type p1,const packet_id_type p2)100 reliable_pid_min(const packet_id_type p1,
101                  const packet_id_type p2)
102 {
103     return !reliable_pid_in_range1(p1, p2, 0x80000000u);
104 }
105 
106 /* check if a particular packet_id is present in ack */
107 static inline bool
reliable_ack_packet_id_present(struct reliable_ack * ack,packet_id_type pid)108 reliable_ack_packet_id_present(struct reliable_ack *ack, packet_id_type pid)
109 {
110     int i;
111     for (i = 0; i < ack->len; ++i)
112     {
113         if (ack->packet_id[i] == pid)
114         {
115             return true;
116         }
117     }
118     return false;
119 }
120 
121 /* get a packet_id from buf */
122 bool
reliable_ack_read_packet_id(struct buffer * buf,packet_id_type * pid)123 reliable_ack_read_packet_id(struct buffer *buf, packet_id_type *pid)
124 {
125     packet_id_type net_pid;
126 
127     if (buf_read(buf, &net_pid, sizeof(net_pid)))
128     {
129         *pid = ntohpid(net_pid);
130         dmsg(D_REL_DEBUG, "ACK read ID " packet_id_format " (buf->len=%d)",
131              (packet_id_print_type)*pid, buf->len);
132         return true;
133     }
134 
135     dmsg(D_REL_LOW, "ACK read ID FAILED (buf->len=%d)", buf->len);
136     return false;
137 }
138 
139 /* acknowledge a packet_id by adding it to a struct reliable_ack */
140 bool
reliable_ack_acknowledge_packet_id(struct reliable_ack * ack,packet_id_type pid)141 reliable_ack_acknowledge_packet_id(struct reliable_ack *ack, packet_id_type pid)
142 {
143     if (!reliable_ack_packet_id_present(ack, pid) && ack->len < RELIABLE_ACK_SIZE)
144     {
145         ack->packet_id[ack->len++] = pid;
146         dmsg(D_REL_DEBUG, "ACK acknowledge ID " packet_id_format " (ack->len=%d)",
147              (packet_id_print_type)pid, ack->len);
148         return true;
149     }
150 
151     dmsg(D_REL_LOW, "ACK acknowledge ID " packet_id_format " FAILED (ack->len=%d)",
152          (packet_id_print_type)pid, ack->len);
153     return false;
154 }
155 
156 /* read a packet ID acknowledgement record from buf into ack */
157 bool
reliable_ack_read(struct reliable_ack * ack,struct buffer * buf,const struct session_id * sid)158 reliable_ack_read(struct reliable_ack *ack,
159                   struct buffer *buf, const struct session_id *sid)
160 {
161     struct gc_arena gc = gc_new();
162     int i;
163     uint8_t count;
164     packet_id_type net_pid;
165     packet_id_type pid;
166     struct session_id session_id_remote;
167 
168     if (!buf_read(buf, &count, sizeof(count)))
169     {
170         goto error;
171     }
172     for (i = 0; i < count; ++i)
173     {
174         if (!buf_read(buf, &net_pid, sizeof(net_pid)))
175         {
176             goto error;
177         }
178         if (ack->len >= RELIABLE_ACK_SIZE)
179         {
180             goto error;
181         }
182         pid = ntohpid(net_pid);
183         ack->packet_id[ack->len++] = pid;
184     }
185     if (count)
186     {
187         if (!session_id_read(&session_id_remote, buf))
188         {
189             goto error;
190         }
191         if (!session_id_defined(&session_id_remote)
192             || !session_id_equal(&session_id_remote, sid))
193         {
194             dmsg(D_REL_LOW,
195                  "ACK read BAD SESSION-ID FROM REMOTE, local=%s, remote=%s",
196                  session_id_print(sid, &gc), session_id_print(&session_id_remote, &gc));
197             goto error;
198         }
199     }
200     gc_free(&gc);
201     return true;
202 
203 error:
204     gc_free(&gc);
205     return false;
206 }
207 
208 #define ACK_SIZE(n) (sizeof(uint8_t) + ((n) ? SID_SIZE : 0) + sizeof(packet_id_type) * (n))
209 
210 /* write a packet ID acknowledgement record to buf, */
211 /* removing all acknowledged entries from ack */
212 bool
reliable_ack_write(struct reliable_ack * ack,struct buffer * buf,const struct session_id * sid,int max,bool prepend)213 reliable_ack_write(struct reliable_ack *ack,
214                    struct buffer *buf,
215                    const struct session_id *sid, int max, bool prepend)
216 {
217     int i, j;
218     uint8_t n;
219     struct buffer sub;
220 
221     n = ack->len;
222     if (n > max)
223     {
224         n = max;
225     }
226     sub = buf_sub(buf, ACK_SIZE(n), prepend);
227     if (!BDEF(&sub))
228     {
229         goto error;
230     }
231     ASSERT(buf_write(&sub, &n, sizeof(n)));
232     for (i = 0; i < n; ++i)
233     {
234         packet_id_type pid = ack->packet_id[i];
235         packet_id_type net_pid = htonpid(pid);
236         ASSERT(buf_write(&sub, &net_pid, sizeof(net_pid)));
237         dmsg(D_REL_DEBUG, "ACK write ID " packet_id_format " (ack->len=%d, n=%d)", (packet_id_print_type)pid, ack->len, n);
238     }
239     if (n)
240     {
241         ASSERT(session_id_defined(sid));
242         ASSERT(session_id_write(sid, &sub));
243         for (i = 0, j = n; j < ack->len; )
244         {
245             ack->packet_id[i++] = ack->packet_id[j++];
246         }
247         ack->len = i;
248     }
249 
250     return true;
251 
252 error:
253     return false;
254 }
255 
256 /* add to extra_frame the maximum number of bytes we will need for reliable_ack_write */
257 void
reliable_ack_adjust_frame_parameters(struct frame * frame,int max)258 reliable_ack_adjust_frame_parameters(struct frame *frame, int max)
259 {
260     frame_add_to_extra_frame(frame, ACK_SIZE(max));
261 }
262 
263 /* print a reliable ACK record coming off the wire */
264 const char *
reliable_ack_print(struct buffer * buf,bool verbose,struct gc_arena * gc)265 reliable_ack_print(struct buffer *buf, bool verbose, struct gc_arena *gc)
266 {
267     int i;
268     uint8_t n_ack;
269     struct session_id sid_ack;
270     packet_id_type pid;
271     struct buffer out = alloc_buf_gc(256, gc);
272 
273     buf_printf(&out, "[");
274     if (!buf_read(buf, &n_ack, sizeof(n_ack)))
275     {
276         goto done;
277     }
278     for (i = 0; i < n_ack; ++i)
279     {
280         if (!buf_read(buf, &pid, sizeof(pid)))
281         {
282             goto done;
283         }
284         pid = ntohpid(pid);
285         buf_printf(&out, " " packet_id_format, (packet_id_print_type)pid);
286     }
287     if (n_ack)
288     {
289         if (!session_id_read(&sid_ack, buf))
290         {
291             goto done;
292         }
293         if (verbose)
294         {
295             buf_printf(&out, " sid=%s", session_id_print(&sid_ack, gc));
296         }
297     }
298 
299 done:
300     buf_printf(&out, " ]");
301     return BSTR(&out);
302 }
303 
304 /*
305  * struct reliable member functions.
306  */
307 
308 void
reliable_init(struct reliable * rel,int buf_size,int offset,int array_size,bool hold)309 reliable_init(struct reliable *rel, int buf_size, int offset, int array_size, bool hold)
310 {
311     int i;
312 
313     CLEAR(*rel);
314     ASSERT(array_size > 0 && array_size <= RELIABLE_CAPACITY);
315     rel->hold = hold;
316     rel->size = array_size;
317     rel->offset = offset;
318     for (i = 0; i < rel->size; ++i)
319     {
320         struct reliable_entry *e = &rel->array[i];
321         e->buf = alloc_buf(buf_size);
322         ASSERT(buf_init(&e->buf, offset));
323     }
324 }
325 
326 void
reliable_free(struct reliable * rel)327 reliable_free(struct reliable *rel)
328 {
329     int i;
330     for (i = 0; i < rel->size; ++i)
331     {
332         struct reliable_entry *e = &rel->array[i];
333         free_buf(&e->buf);
334     }
335 }
336 
337 /* no active buffers? */
338 bool
reliable_empty(const struct reliable * rel)339 reliable_empty(const struct reliable *rel)
340 {
341     int i;
342     for (i = 0; i < rel->size; ++i)
343     {
344         const struct reliable_entry *e = &rel->array[i];
345         if (e->active)
346         {
347             return false;
348         }
349     }
350     return true;
351 }
352 
353 /* del acknowledged items from send buf */
354 void
reliable_send_purge(struct reliable * rel,const struct reliable_ack * ack)355 reliable_send_purge(struct reliable *rel, const struct reliable_ack *ack)
356 {
357     int i, j;
358     for (i = 0; i < ack->len; ++i)
359     {
360         packet_id_type pid = ack->packet_id[i];
361         for (j = 0; j < rel->size; ++j)
362         {
363             struct reliable_entry *e = &rel->array[j];
364             if (e->active && e->packet_id == pid)
365             {
366                 dmsg(D_REL_DEBUG,
367                      "ACK received for pid " packet_id_format ", deleting from send buffer",
368                      (packet_id_print_type)pid);
369 #if 0
370                 /* DEBUGGING -- how close were we timing out on ACK failure and resending? */
371                 {
372                     if (e->next_try)
373                     {
374                         const interval_t wake = e->next_try - now;
375                         msg(M_INFO, "ACK " packet_id_format ", wake=%d", pid, wake);
376                     }
377                 }
378 #endif
379                 e->active = false;
380                 break;
381             }
382         }
383     }
384 }
385 
386 /* print the current sequence of active packet IDs */
387 static const char *
reliable_print_ids(const struct reliable * rel,struct gc_arena * gc)388 reliable_print_ids(const struct reliable *rel, struct gc_arena *gc)
389 {
390     struct buffer out = alloc_buf_gc(256, gc);
391     int i;
392 
393     buf_printf(&out, "[" packet_id_format "]", (packet_id_print_type)rel->packet_id);
394     for (i = 0; i < rel->size; ++i)
395     {
396         const struct reliable_entry *e = &rel->array[i];
397         if (e->active)
398         {
399             buf_printf(&out, " " packet_id_format, (packet_id_print_type)e->packet_id);
400         }
401     }
402     return BSTR(&out);
403 }
404 
405 /* true if at least one free buffer available */
406 bool
reliable_can_get(const struct reliable * rel)407 reliable_can_get(const struct reliable *rel)
408 {
409     struct gc_arena gc = gc_new();
410     int i;
411     for (i = 0; i < rel->size; ++i)
412     {
413         const struct reliable_entry *e = &rel->array[i];
414         if (!e->active)
415         {
416             return true;
417         }
418     }
419     dmsg(D_REL_LOW, "ACK no free receive buffer available: %s", reliable_print_ids(rel, &gc));
420     gc_free(&gc);
421     return false;
422 }
423 
424 /* make sure that incoming packet ID isn't a replay */
425 bool
reliable_not_replay(const struct reliable * rel,packet_id_type id)426 reliable_not_replay(const struct reliable *rel, packet_id_type id)
427 {
428     struct gc_arena gc = gc_new();
429     int i;
430     if (reliable_pid_min(id, rel->packet_id))
431     {
432         goto bad;
433     }
434     for (i = 0; i < rel->size; ++i)
435     {
436         const struct reliable_entry *e = &rel->array[i];
437         if (e->active && e->packet_id == id)
438         {
439             goto bad;
440         }
441     }
442     gc_free(&gc);
443     return true;
444 
445 bad:
446     dmsg(D_REL_DEBUG, "ACK " packet_id_format " is a replay: %s", (packet_id_print_type)id, reliable_print_ids(rel, &gc));
447     gc_free(&gc);
448     return false;
449 }
450 
451 /* make sure that incoming packet ID won't deadlock the receive buffer */
452 bool
reliable_wont_break_sequentiality(const struct reliable * rel,packet_id_type id)453 reliable_wont_break_sequentiality(const struct reliable *rel, packet_id_type id)
454 {
455     struct gc_arena gc = gc_new();
456 
457     const int ret = reliable_pid_in_range2(id, rel->packet_id, rel->size);
458 
459     if (!ret)
460     {
461         dmsg(D_REL_LOW, "ACK " packet_id_format " breaks sequentiality: %s",
462              (packet_id_print_type)id, reliable_print_ids(rel, &gc));
463     }
464 
465     dmsg(D_REL_DEBUG, "ACK RWBS rel->size=%d rel->packet_id=%08x id=%08x ret=%d", rel->size, rel->packet_id, id, ret);
466 
467     gc_free(&gc);
468     return ret;
469 }
470 
471 /* grab a free buffer */
472 struct buffer *
reliable_get_buf(struct reliable * rel)473 reliable_get_buf(struct reliable *rel)
474 {
475     int i;
476     for (i = 0; i < rel->size; ++i)
477     {
478         struct reliable_entry *e = &rel->array[i];
479         if (!e->active)
480         {
481             ASSERT(buf_init(&e->buf, rel->offset));
482             return &e->buf;
483         }
484     }
485     return NULL;
486 }
487 
488 /* grab a free buffer, fail if buffer clogged by unacknowledged low packet IDs */
489 struct buffer *
reliable_get_buf_output_sequenced(struct reliable * rel)490 reliable_get_buf_output_sequenced(struct reliable *rel)
491 {
492     struct gc_arena gc = gc_new();
493     int i;
494     packet_id_type min_id = 0;
495     bool min_id_defined = false;
496     struct buffer *ret = NULL;
497 
498     /* find minimum active packet_id */
499     for (i = 0; i < rel->size; ++i)
500     {
501         const struct reliable_entry *e = &rel->array[i];
502         if (e->active)
503         {
504             if (!min_id_defined || reliable_pid_min(e->packet_id, min_id))
505             {
506                 min_id_defined = true;
507                 min_id = e->packet_id;
508             }
509         }
510     }
511 
512     if (!min_id_defined || reliable_pid_in_range1(rel->packet_id, min_id, rel->size))
513     {
514         ret = reliable_get_buf(rel);
515     }
516     else
517     {
518         dmsg(D_REL_LOW, "ACK output sequence broken: %s", reliable_print_ids(rel, &gc));
519     }
520     gc_free(&gc);
521     return ret;
522 }
523 
524 /* get active buffer for next sequentially increasing key ID */
525 struct buffer *
reliable_get_buf_sequenced(struct reliable * rel)526 reliable_get_buf_sequenced(struct reliable *rel)
527 {
528     int i;
529     for (i = 0; i < rel->size; ++i)
530     {
531         struct reliable_entry *e = &rel->array[i];
532         if (e->active && e->packet_id == rel->packet_id)
533         {
534             return &e->buf;
535         }
536     }
537     return NULL;
538 }
539 
540 /* return true if reliable_send would return a non-NULL result */
541 bool
reliable_can_send(const struct reliable * rel)542 reliable_can_send(const struct reliable *rel)
543 {
544     struct gc_arena gc = gc_new();
545     int i;
546     int n_active = 0, n_current = 0;
547     for (i = 0; i < rel->size; ++i)
548     {
549         const struct reliable_entry *e = &rel->array[i];
550         if (e->active)
551         {
552             ++n_active;
553             if (now >= e->next_try)
554             {
555                 ++n_current;
556             }
557         }
558     }
559     dmsg(D_REL_DEBUG, "ACK reliable_can_send active=%d current=%d : %s",
560          n_active,
561          n_current,
562          reliable_print_ids(rel, &gc));
563 
564     gc_free(&gc);
565     return n_current > 0 && !rel->hold;
566 }
567 
568 /* return next buffer to send to remote */
569 struct buffer *
reliable_send(struct reliable * rel,int * opcode)570 reliable_send(struct reliable *rel, int *opcode)
571 {
572     int i;
573     struct reliable_entry *best = NULL;
574     const time_t local_now = now;
575 
576     for (i = 0; i < rel->size; ++i)
577     {
578         struct reliable_entry *e = &rel->array[i];
579         if (e->active && local_now >= e->next_try)
580         {
581             if (!best || reliable_pid_min(e->packet_id, best->packet_id))
582             {
583                 best = e;
584             }
585         }
586     }
587     if (best)
588     {
589 #ifdef EXPONENTIAL_BACKOFF
590         /* exponential backoff */
591         best->next_try = local_now + best->timeout;
592         best->timeout *= 2;
593 #else
594         /* constant timeout, no backoff */
595         best->next_try = local_now + best->timeout;
596 #endif
597         *opcode = best->opcode;
598         dmsg(D_REL_DEBUG, "ACK reliable_send ID " packet_id_format " (size=%d to=%d)",
599              (packet_id_print_type)best->packet_id, best->buf.len,
600              (int)(best->next_try - local_now));
601         return &best->buf;
602     }
603     return NULL;
604 }
605 
606 /* schedule all pending packets for immediate retransmit */
607 void
reliable_schedule_now(struct reliable * rel)608 reliable_schedule_now(struct reliable *rel)
609 {
610     int i;
611     dmsg(D_REL_DEBUG, "ACK reliable_schedule_now");
612     rel->hold = false;
613     for (i = 0; i < rel->size; ++i)
614     {
615         struct reliable_entry *e = &rel->array[i];
616         if (e->active)
617         {
618             e->next_try = now;
619             e->timeout = rel->initial_timeout;
620         }
621     }
622 }
623 
624 /* in how many seconds should we wake up to check for timeout */
625 /* if we return BIG_TIMEOUT, nothing to wait for */
626 interval_t
reliable_send_timeout(const struct reliable * rel)627 reliable_send_timeout(const struct reliable *rel)
628 {
629     struct gc_arena gc = gc_new();
630     interval_t ret = BIG_TIMEOUT;
631     int i;
632     const time_t local_now = now;
633 
634     for (i = 0; i < rel->size; ++i)
635     {
636         const struct reliable_entry *e = &rel->array[i];
637         if (e->active)
638         {
639             if (e->next_try <= local_now)
640             {
641                 ret = 0;
642                 break;
643             }
644             else
645             {
646                 ret = min_int(ret, e->next_try - local_now);
647             }
648         }
649     }
650 
651     dmsg(D_REL_DEBUG, "ACK reliable_send_timeout %d %s",
652          (int) ret,
653          reliable_print_ids(rel, &gc));
654 
655     gc_free(&gc);
656     return ret;
657 }
658 
659 /*
660  * Enable an incoming buffer previously returned by a get function as active.
661  */
662 
663 void
reliable_mark_active_incoming(struct reliable * rel,struct buffer * buf,packet_id_type pid,int opcode)664 reliable_mark_active_incoming(struct reliable *rel, struct buffer *buf,
665                               packet_id_type pid, int opcode)
666 {
667     int i;
668     for (i = 0; i < rel->size; ++i)
669     {
670         struct reliable_entry *e = &rel->array[i];
671         if (buf == &e->buf)
672         {
673             e->active = true;
674 
675             /* packets may not arrive in sequential order */
676             e->packet_id = pid;
677 
678             /* check for replay */
679             ASSERT(!reliable_pid_min(pid, rel->packet_id));
680 
681             e->opcode = opcode;
682             e->next_try = 0;
683             e->timeout = 0;
684             dmsg(D_REL_DEBUG, "ACK mark active incoming ID " packet_id_format, (packet_id_print_type)e->packet_id);
685             return;
686         }
687     }
688     ASSERT(0);                  /* buf not found in rel */
689 }
690 
691 /*
692  * Enable an outgoing buffer previously returned by a get function as active.
693  */
694 
695 void
reliable_mark_active_outgoing(struct reliable * rel,struct buffer * buf,int opcode)696 reliable_mark_active_outgoing(struct reliable *rel, struct buffer *buf, int opcode)
697 {
698     int i;
699     for (i = 0; i < rel->size; ++i)
700     {
701         struct reliable_entry *e = &rel->array[i];
702         if (buf == &e->buf)
703         {
704             /* Write mode, increment packet_id (i.e. sequence number)
705              * linearly and prepend id to packet */
706             packet_id_type net_pid;
707             e->packet_id = rel->packet_id++;
708             net_pid = htonpid(e->packet_id);
709             ASSERT(buf_write_prepend(buf, &net_pid, sizeof(net_pid)));
710             e->active = true;
711             e->opcode = opcode;
712             e->next_try = 0;
713             e->timeout = rel->initial_timeout;
714             dmsg(D_REL_DEBUG, "ACK mark active outgoing ID " packet_id_format, (packet_id_print_type)e->packet_id);
715             return;
716         }
717     }
718     ASSERT(0);                  /* buf not found in rel */
719 }
720 
721 /* delete a buffer previously activated by reliable_mark_active() */
722 void
reliable_mark_deleted(struct reliable * rel,struct buffer * buf,bool inc_pid)723 reliable_mark_deleted(struct reliable *rel, struct buffer *buf, bool inc_pid)
724 {
725     int i;
726     for (i = 0; i < rel->size; ++i)
727     {
728         struct reliable_entry *e = &rel->array[i];
729         if (buf == &e->buf)
730         {
731             e->active = false;
732             if (inc_pid)
733             {
734                 rel->packet_id = e->packet_id + 1;
735             }
736             return;
737         }
738     }
739     ASSERT(0);
740 }
741 
742 #if 0
743 
744 void
745 reliable_ack_debug_print(const struct reliable_ack *ack, char *desc)
746 {
747     int i;
748 
749     printf("********* struct reliable_ack %s\n", desc);
750     for (i = 0; i < ack->len; ++i)
751     {
752         printf("  %d: " packet_id_format "\n", i, (packet_id_print_type) ack->packet_id[i]);
753     }
754 }
755 
756 void
757 reliable_debug_print(const struct reliable *rel, char *desc)
758 {
759     int i;
760     update_time();
761 
762     printf("********* struct reliable %s\n", desc);
763     printf("  initial_timeout=%d\n", (int)rel->initial_timeout);
764     printf("  packet_id=" packet_id_format "\n", rel->packet_id);
765     printf("  now=%" PRIi64 "\n", (int64_t)now);
766     for (i = 0; i < rel->size; ++i)
767     {
768         const struct reliable_entry *e = &rel->array[i];
769         if (e->active)
770         {
771             printf("  %d: packet_id=" packet_id_format " len=%d", i, e->packet_id, e->buf.len);
772             printf(" next_try=%" PRIi64, (int64_t)e->next_try);
773             printf("\n");
774         }
775     }
776 }
777 
778 #endif /* if 0 */
779