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-2018 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     if (!rel)
330     {
331         return;
332     }
333     int i;
334     for (i = 0; i < rel->size; ++i)
335     {
336         struct reliable_entry *e = &rel->array[i];
337         free_buf(&e->buf);
338     }
339     free(rel);
340 }
341 
342 /* no active buffers? */
343 bool
reliable_empty(const struct reliable * rel)344 reliable_empty(const struct reliable *rel)
345 {
346     int i;
347     for (i = 0; i < rel->size; ++i)
348     {
349         const struct reliable_entry *e = &rel->array[i];
350         if (e->active)
351         {
352             return false;
353         }
354     }
355     return true;
356 }
357 
358 /* del acknowledged items from send buf */
359 void
reliable_send_purge(struct reliable * rel,const struct reliable_ack * ack)360 reliable_send_purge(struct reliable *rel, const struct reliable_ack *ack)
361 {
362     int i, j;
363     for (i = 0; i < ack->len; ++i)
364     {
365         packet_id_type pid = ack->packet_id[i];
366         for (j = 0; j < rel->size; ++j)
367         {
368             struct reliable_entry *e = &rel->array[j];
369             if (e->active && e->packet_id == pid)
370             {
371                 dmsg(D_REL_DEBUG,
372                      "ACK received for pid " packet_id_format ", deleting from send buffer",
373                      (packet_id_print_type)pid);
374 #if 0
375                 /* DEBUGGING -- how close were we timing out on ACK failure and resending? */
376                 {
377                     if (e->next_try)
378                     {
379                         const interval_t wake = e->next_try - now;
380                         msg(M_INFO, "ACK " packet_id_format ", wake=%d", pid, wake);
381                     }
382                 }
383 #endif
384                 e->active = false;
385             }
386             else if (e->active && e->packet_id < pid)
387             {
388                 /* We have received an ACK for a packet with a higher PID. Either
389                  * we have received ACKs out of or order or the packet has been
390                  * lost. We count the number of ACKs to determine if we should
391                  * resend it early. */
392                 e->n_acks++;
393             }
394         }
395     }
396 }
397 
398 /* print the current sequence of active packet IDs */
399 static const char *
reliable_print_ids(const struct reliable * rel,struct gc_arena * gc)400 reliable_print_ids(const struct reliable *rel, struct gc_arena *gc)
401 {
402     struct buffer out = alloc_buf_gc(256, gc);
403     int i;
404 
405     buf_printf(&out, "[" packet_id_format "]", (packet_id_print_type)rel->packet_id);
406     for (i = 0; i < rel->size; ++i)
407     {
408         const struct reliable_entry *e = &rel->array[i];
409         if (e->active)
410         {
411             buf_printf(&out, " " packet_id_format, (packet_id_print_type)e->packet_id);
412         }
413     }
414     return BSTR(&out);
415 }
416 
417 /* true if at least one free buffer available */
418 bool
reliable_can_get(const struct reliable * rel)419 reliable_can_get(const struct reliable *rel)
420 {
421     struct gc_arena gc = gc_new();
422     int i;
423     for (i = 0; i < rel->size; ++i)
424     {
425         const struct reliable_entry *e = &rel->array[i];
426         if (!e->active)
427         {
428             return true;
429         }
430     }
431     dmsg(D_REL_LOW, "ACK no free receive buffer available: %s", reliable_print_ids(rel, &gc));
432     gc_free(&gc);
433     return false;
434 }
435 
436 /* make sure that incoming packet ID isn't a replay */
437 bool
reliable_not_replay(const struct reliable * rel,packet_id_type id)438 reliable_not_replay(const struct reliable *rel, packet_id_type id)
439 {
440     struct gc_arena gc = gc_new();
441     int i;
442     if (reliable_pid_min(id, rel->packet_id))
443     {
444         goto bad;
445     }
446     for (i = 0; i < rel->size; ++i)
447     {
448         const struct reliable_entry *e = &rel->array[i];
449         if (e->active && e->packet_id == id)
450         {
451             goto bad;
452         }
453     }
454     gc_free(&gc);
455     return true;
456 
457 bad:
458     dmsg(D_REL_DEBUG, "ACK " packet_id_format " is a replay: %s", (packet_id_print_type)id, reliable_print_ids(rel, &gc));
459     gc_free(&gc);
460     return false;
461 }
462 
463 /* make sure that incoming packet ID won't deadlock the receive buffer */
464 bool
reliable_wont_break_sequentiality(const struct reliable * rel,packet_id_type id)465 reliable_wont_break_sequentiality(const struct reliable *rel, packet_id_type id)
466 {
467     struct gc_arena gc = gc_new();
468 
469     const int ret = reliable_pid_in_range2(id, rel->packet_id, rel->size);
470 
471     if (!ret)
472     {
473         dmsg(D_REL_LOW, "ACK " packet_id_format " breaks sequentiality: %s",
474              (packet_id_print_type)id, reliable_print_ids(rel, &gc));
475     }
476 
477     dmsg(D_REL_DEBUG, "ACK RWBS rel->size=%d rel->packet_id=%08x id=%08x ret=%d", rel->size, rel->packet_id, id, ret);
478 
479     gc_free(&gc);
480     return ret;
481 }
482 
483 /* grab a free buffer */
484 struct buffer *
reliable_get_buf(struct reliable * rel)485 reliable_get_buf(struct reliable *rel)
486 {
487     int i;
488     for (i = 0; i < rel->size; ++i)
489     {
490         struct reliable_entry *e = &rel->array[i];
491         if (!e->active)
492         {
493             ASSERT(buf_init(&e->buf, rel->offset));
494             return &e->buf;
495         }
496     }
497     return NULL;
498 }
499 
500 /* grab a free buffer, fail if buffer clogged by unacknowledged low packet IDs */
501 struct buffer *
reliable_get_buf_output_sequenced(struct reliable * rel)502 reliable_get_buf_output_sequenced(struct reliable *rel)
503 {
504     struct gc_arena gc = gc_new();
505     int i;
506     packet_id_type min_id = 0;
507     bool min_id_defined = false;
508     struct buffer *ret = NULL;
509 
510     /* find minimum active packet_id */
511     for (i = 0; i < rel->size; ++i)
512     {
513         const struct reliable_entry *e = &rel->array[i];
514         if (e->active)
515         {
516             if (!min_id_defined || reliable_pid_min(e->packet_id, min_id))
517             {
518                 min_id_defined = true;
519                 min_id = e->packet_id;
520             }
521         }
522     }
523 
524     if (!min_id_defined || reliable_pid_in_range1(rel->packet_id, min_id, rel->size))
525     {
526         ret = reliable_get_buf(rel);
527     }
528     else
529     {
530         dmsg(D_REL_LOW, "ACK output sequence broken: %s", reliable_print_ids(rel, &gc));
531     }
532     gc_free(&gc);
533     return ret;
534 }
535 
536 /* get active buffer for next sequentially increasing key ID */
537 struct buffer *
reliable_get_buf_sequenced(struct reliable * rel)538 reliable_get_buf_sequenced(struct reliable *rel)
539 {
540     int i;
541     for (i = 0; i < rel->size; ++i)
542     {
543         struct reliable_entry *e = &rel->array[i];
544         if (e->active && e->packet_id == rel->packet_id)
545         {
546             return &e->buf;
547         }
548     }
549     return NULL;
550 }
551 
552 /* return true if reliable_send would return a non-NULL result */
553 bool
reliable_can_send(const struct reliable * rel)554 reliable_can_send(const struct reliable *rel)
555 {
556     struct gc_arena gc = gc_new();
557     int i;
558     int n_active = 0, n_current = 0;
559     for (i = 0; i < rel->size; ++i)
560     {
561         const struct reliable_entry *e = &rel->array[i];
562         if (e->active)
563         {
564             ++n_active;
565             if (now >= e->next_try || e->n_acks >= N_ACK_RETRANSMIT)
566             {
567                 ++n_current;
568             }
569         }
570     }
571     dmsg(D_REL_DEBUG, "ACK reliable_can_send active=%d current=%d : %s",
572          n_active,
573          n_current,
574          reliable_print_ids(rel, &gc));
575 
576     gc_free(&gc);
577     return n_current > 0 && !rel->hold;
578 }
579 
580 /* return next buffer to send to remote */
581 struct buffer *
reliable_send(struct reliable * rel,int * opcode)582 reliable_send(struct reliable *rel, int *opcode)
583 {
584     int i;
585     struct reliable_entry *best = NULL;
586     const time_t local_now = now;
587 
588     for (i = 0; i < rel->size; ++i)
589     {
590         struct reliable_entry *e = &rel->array[i];
591 
592         /* If N_ACK_RETRANSMIT later packets have received ACKs, we assume
593          * that the packet was lost and resend it even if the timeout has
594          * not expired yet. */
595         if (e->active
596             && (e->n_acks >= N_ACK_RETRANSMIT || local_now >= e->next_try))
597         {
598             if (!best || reliable_pid_min(e->packet_id, best->packet_id))
599             {
600                 best = e;
601             }
602         }
603     }
604     if (best)
605     {
606 #ifdef EXPONENTIAL_BACKOFF
607         /* exponential backoff */
608         best->next_try = local_now + best->timeout;
609         best->timeout *= 2;
610 #else
611         /* constant timeout, no backoff */
612         best->next_try = local_now + best->timeout;
613 #endif
614         best->n_acks = 0;
615         *opcode = best->opcode;
616         dmsg(D_REL_DEBUG, "ACK reliable_send ID " packet_id_format " (size=%d to=%d)",
617              (packet_id_print_type)best->packet_id, best->buf.len,
618              (int)(best->next_try - local_now));
619         return &best->buf;
620     }
621     return NULL;
622 }
623 
624 /* schedule all pending packets for immediate retransmit */
625 void
reliable_schedule_now(struct reliable * rel)626 reliable_schedule_now(struct reliable *rel)
627 {
628     int i;
629     dmsg(D_REL_DEBUG, "ACK reliable_schedule_now");
630     rel->hold = false;
631     for (i = 0; i < rel->size; ++i)
632     {
633         struct reliable_entry *e = &rel->array[i];
634         if (e->active)
635         {
636             e->next_try = now;
637             e->timeout = rel->initial_timeout;
638         }
639     }
640 }
641 
642 /* in how many seconds should we wake up to check for timeout */
643 /* if we return BIG_TIMEOUT, nothing to wait for */
644 interval_t
reliable_send_timeout(const struct reliable * rel)645 reliable_send_timeout(const struct reliable *rel)
646 {
647     struct gc_arena gc = gc_new();
648     interval_t ret = BIG_TIMEOUT;
649     int i;
650     const time_t local_now = now;
651 
652     for (i = 0; i < rel->size; ++i)
653     {
654         const struct reliable_entry *e = &rel->array[i];
655         if (e->active)
656         {
657             if (e->next_try <= local_now)
658             {
659                 ret = 0;
660                 break;
661             }
662             else
663             {
664                 ret = min_int(ret, e->next_try - local_now);
665             }
666         }
667     }
668 
669     dmsg(D_REL_DEBUG, "ACK reliable_send_timeout %d %s",
670          (int) ret,
671          reliable_print_ids(rel, &gc));
672 
673     gc_free(&gc);
674     return ret;
675 }
676 
677 /*
678  * Enable an incoming buffer previously returned by a get function as active.
679  */
680 
681 void
reliable_mark_active_incoming(struct reliable * rel,struct buffer * buf,packet_id_type pid,int opcode)682 reliable_mark_active_incoming(struct reliable *rel, struct buffer *buf,
683                               packet_id_type pid, int opcode)
684 {
685     int i;
686     for (i = 0; i < rel->size; ++i)
687     {
688         struct reliable_entry *e = &rel->array[i];
689         if (buf == &e->buf)
690         {
691             e->active = true;
692 
693             /* packets may not arrive in sequential order */
694             e->packet_id = pid;
695 
696             /* check for replay */
697             ASSERT(!reliable_pid_min(pid, rel->packet_id));
698 
699             e->opcode = opcode;
700             e->next_try = 0;
701             e->timeout = 0;
702             e->n_acks = 0;
703             dmsg(D_REL_DEBUG, "ACK mark active incoming ID " packet_id_format, (packet_id_print_type)e->packet_id);
704             return;
705         }
706     }
707     ASSERT(0);                  /* buf not found in rel */
708 }
709 
710 /*
711  * Enable an outgoing buffer previously returned by a get function as active.
712  */
713 
714 void
reliable_mark_active_outgoing(struct reliable * rel,struct buffer * buf,int opcode)715 reliable_mark_active_outgoing(struct reliable *rel, struct buffer *buf, int opcode)
716 {
717     int i;
718     for (i = 0; i < rel->size; ++i)
719     {
720         struct reliable_entry *e = &rel->array[i];
721         if (buf == &e->buf)
722         {
723             /* Write mode, increment packet_id (i.e. sequence number)
724              * linearly and prepend id to packet */
725             packet_id_type net_pid;
726             e->packet_id = rel->packet_id++;
727             net_pid = htonpid(e->packet_id);
728             ASSERT(buf_write_prepend(buf, &net_pid, sizeof(net_pid)));
729             e->active = true;
730             e->opcode = opcode;
731             e->next_try = 0;
732             e->timeout = rel->initial_timeout;
733             dmsg(D_REL_DEBUG, "ACK mark active outgoing ID " packet_id_format, (packet_id_print_type)e->packet_id);
734             return;
735         }
736     }
737     ASSERT(0);                  /* buf not found in rel */
738 }
739 
740 /* delete a buffer previously activated by reliable_mark_active() */
741 void
reliable_mark_deleted(struct reliable * rel,struct buffer * buf,bool inc_pid)742 reliable_mark_deleted(struct reliable *rel, struct buffer *buf, bool inc_pid)
743 {
744     int i;
745     for (i = 0; i < rel->size; ++i)
746     {
747         struct reliable_entry *e = &rel->array[i];
748         if (buf == &e->buf)
749         {
750             e->active = false;
751             if (inc_pid)
752             {
753                 rel->packet_id = e->packet_id + 1;
754             }
755             return;
756         }
757     }
758     ASSERT(0);
759 }
760 
761 #if 0
762 
763 void
764 reliable_ack_debug_print(const struct reliable_ack *ack, char *desc)
765 {
766     int i;
767 
768     printf("********* struct reliable_ack %s\n", desc);
769     for (i = 0; i < ack->len; ++i)
770     {
771         printf("  %d: " packet_id_format "\n", i, (packet_id_print_type) ack->packet_id[i]);
772     }
773 }
774 
775 void
776 reliable_debug_print(const struct reliable *rel, char *desc)
777 {
778     int i;
779     update_time();
780 
781     printf("********* struct reliable %s\n", desc);
782     printf("  initial_timeout=%d\n", (int)rel->initial_timeout);
783     printf("  packet_id=" packet_id_format "\n", rel->packet_id);
784     printf("  now=%" PRIi64 "\n", (int64_t)now);
785     for (i = 0; i < rel->size; ++i)
786     {
787         const struct reliable_entry *e = &rel->array[i];
788         if (e->active)
789         {
790             printf("  %d: packet_id=" packet_id_format " len=%d", i, e->packet_id, e->buf.len);
791             printf(" next_try=%" PRIi64, (int64_t)e->next_try);
792             printf("\n");
793         }
794     }
795 }
796 
797 #endif /* if 0 */
798