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