6fbf66fa |
/*
* OpenVPN -- An application to securely tunnel IP networks
* over a single UDP port, with support for SSL/TLS-based
* session authentication and key exchange,
* packet encryption, packet authentication, and
* packet compression.
* |
49979459 |
* Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net> |
6fbf66fa |
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* |
caa54ac3 |
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
6fbf66fa |
*/
/*
* These routines implement a reliability layer on top of UDP,
* so that SSL/TLS can be run over UDP.
*/
|
c110b289 |
#ifdef HAVE_CONFIG_H
#include "config.h"
#elif defined(_MSC_VER)
#include "config-msvc.h"
#endif
|
1bda73a7 |
#include "syshead.h" |
6fbf66fa |
#include "buffer.h"
#include "error.h"
#include "common.h"
#include "reliable.h"
#include "memdbg.h"
|
a1849f41 |
/*
* verify that test - base < extent while allowing for base or test wraparound
*/
static inline bool |
81d882d5 |
reliable_pid_in_range1(const packet_id_type test,
const packet_id_type base,
const unsigned int extent) |
a1849f41 |
{ |
81d882d5 |
if (test >= base) |
a1849f41 |
{ |
81d882d5 |
if (test - base < extent)
{
return true;
} |
a1849f41 |
} |
81d882d5 |
else |
5fc10872 |
{ |
81d882d5 |
if ((test+0x80000000u) - (base+0x80000000u) < extent)
{
return true;
} |
5fc10872 |
}
|
81d882d5 |
return false; |
5fc10872 |
}
/*
* verify that test < base + extent while allowing for base or test wraparound
*/
static inline bool |
81d882d5 |
reliable_pid_in_range2(const packet_id_type test,
const packet_id_type base,
const unsigned int extent) |
5fc10872 |
{ |
81d882d5 |
if (base + extent >= base) |
5fc10872 |
{ |
81d882d5 |
if (test < base + extent)
{
return true;
} |
5fc10872 |
} |
81d882d5 |
else |
5fc10872 |
{ |
81d882d5 |
if ((test+0x80000000u) < (base+0x80000000u) + extent)
{
return true;
} |
a1849f41 |
}
|
81d882d5 |
return false; |
a1849f41 |
}
/*
* verify that p1 < p2 while allowing for p1 or p2 wraparound
*/
static inline bool |
81d882d5 |
reliable_pid_min(const packet_id_type p1,
const packet_id_type p2) |
a1849f41 |
{ |
81d882d5 |
return !reliable_pid_in_range1(p1, p2, 0x80000000u); |
a1849f41 |
}
|
6fbf66fa |
/* check if a particular packet_id is present in ack */
static inline bool |
81d882d5 |
reliable_ack_packet_id_present(struct reliable_ack *ack, packet_id_type pid) |
6fbf66fa |
{ |
81d882d5 |
int i;
for (i = 0; i < ack->len; ++i) |
4cd4899e |
{ |
81d882d5 |
if (ack->packet_id[i] == pid)
{
return true;
} |
4cd4899e |
} |
81d882d5 |
return false; |
6fbf66fa |
}
/* get a packet_id from buf */
bool |
81d882d5 |
reliable_ack_read_packet_id(struct buffer *buf, packet_id_type *pid) |
6fbf66fa |
{ |
81d882d5 |
packet_id_type net_pid; |
6fbf66fa |
|
81d882d5 |
if (buf_read(buf, &net_pid, sizeof(net_pid))) |
6fbf66fa |
{ |
81d882d5 |
*pid = ntohpid(net_pid);
dmsg(D_REL_DEBUG, "ACK read ID " packet_id_format " (buf->len=%d)",
(packet_id_print_type)*pid, buf->len);
return true; |
6fbf66fa |
}
|
81d882d5 |
dmsg(D_REL_LOW, "ACK read ID FAILED (buf->len=%d)", buf->len);
return false; |
6fbf66fa |
}
/* acknowledge a packet_id by adding it to a struct reliable_ack */
bool |
81d882d5 |
reliable_ack_acknowledge_packet_id(struct reliable_ack *ack, packet_id_type pid) |
6fbf66fa |
{ |
81d882d5 |
if (!reliable_ack_packet_id_present(ack, pid) && ack->len < RELIABLE_ACK_SIZE) |
6fbf66fa |
{ |
81d882d5 |
ack->packet_id[ack->len++] = pid;
dmsg(D_REL_DEBUG, "ACK acknowledge ID " packet_id_format " (ack->len=%d)",
(packet_id_print_type)pid, ack->len);
return true; |
6fbf66fa |
}
|
81d882d5 |
dmsg(D_REL_LOW, "ACK acknowledge ID " packet_id_format " FAILED (ack->len=%d)",
(packet_id_print_type)pid, ack->len);
return false; |
6fbf66fa |
}
/* read a packet ID acknowledgement record from buf into ack */
bool |
81d882d5 |
reliable_ack_read(struct reliable_ack *ack,
struct buffer *buf, const struct session_id *sid) |
6fbf66fa |
{ |
81d882d5 |
struct gc_arena gc = gc_new();
int i;
uint8_t count;
packet_id_type net_pid;
packet_id_type pid;
struct session_id session_id_remote;
if (!buf_read(buf, &count, sizeof(count)))
{
goto error;
}
for (i = 0; i < count; ++i)
{
if (!buf_read(buf, &net_pid, sizeof(net_pid)))
{
goto error;
}
if (ack->len >= RELIABLE_ACK_SIZE)
{
goto error;
}
pid = ntohpid(net_pid);
ack->packet_id[ack->len++] = pid;
}
if (count)
{
if (!session_id_read(&session_id_remote, buf))
{
goto error;
}
if (!session_id_defined(&session_id_remote)
|| !session_id_equal(&session_id_remote, sid))
{
dmsg(D_REL_LOW,
"ACK read BAD SESSION-ID FROM REMOTE, local=%s, remote=%s",
session_id_print(sid, &gc), session_id_print(&session_id_remote, &gc));
goto error;
}
}
gc_free(&gc);
return true; |
6fbf66fa |
error: |
81d882d5 |
gc_free(&gc);
return false; |
6fbf66fa |
}
|
81d882d5 |
#define ACK_SIZE(n) (sizeof(uint8_t) + ((n) ? SID_SIZE : 0) + sizeof(packet_id_type) * (n)) |
6fbf66fa |
/* write a packet ID acknowledgement record to buf, */
/* removing all acknowledged entries from ack */
bool |
81d882d5 |
reliable_ack_write(struct reliable_ack *ack,
struct buffer *buf,
const struct session_id *sid, int max, bool prepend) |
6fbf66fa |
{ |
81d882d5 |
int i, j;
uint8_t n;
struct buffer sub; |
6fbf66fa |
|
81d882d5 |
n = ack->len;
if (n > max) |
6fbf66fa |
{ |
81d882d5 |
n = max; |
6fbf66fa |
} |
81d882d5 |
sub = buf_sub(buf, ACK_SIZE(n), prepend);
if (!BDEF(&sub)) |
6fbf66fa |
{ |
81d882d5 |
goto error;
}
ASSERT(buf_write(&sub, &n, sizeof(n)));
for (i = 0; i < n; ++i)
{
packet_id_type pid = ack->packet_id[i];
packet_id_type net_pid = htonpid(pid);
ASSERT(buf_write(&sub, &net_pid, sizeof(net_pid)));
dmsg(D_REL_DEBUG, "ACK write ID " packet_id_format " (ack->len=%d, n=%d)", (packet_id_print_type)pid, ack->len, n);
}
if (n)
{
ASSERT(session_id_defined(sid));
ASSERT(session_id_write(sid, &sub));
for (i = 0, j = n; j < ack->len; ) |
4cd4899e |
{ |
81d882d5 |
ack->packet_id[i++] = ack->packet_id[j++]; |
4cd4899e |
} |
81d882d5 |
ack->len = i; |
6fbf66fa |
}
|
81d882d5 |
return true; |
6fbf66fa |
error: |
81d882d5 |
return false; |
6fbf66fa |
}
/* add to extra_frame the maximum number of bytes we will need for reliable_ack_write */
void |
81d882d5 |
reliable_ack_adjust_frame_parameters(struct frame *frame, int max) |
6fbf66fa |
{ |
81d882d5 |
frame_add_to_extra_frame(frame, ACK_SIZE(max)); |
6fbf66fa |
}
/* print a reliable ACK record coming off the wire */
const char * |
81d882d5 |
reliable_ack_print(struct buffer *buf, bool verbose, struct gc_arena *gc) |
6fbf66fa |
{ |
81d882d5 |
int i;
uint8_t n_ack;
struct session_id sid_ack;
packet_id_type pid;
struct buffer out = alloc_buf_gc(256, gc); |
6fbf66fa |
|
81d882d5 |
buf_printf(&out, "[");
if (!buf_read(buf, &n_ack, sizeof(n_ack))) |
6fbf66fa |
{ |
81d882d5 |
goto done; |
6fbf66fa |
} |
81d882d5 |
for (i = 0; i < n_ack; ++i) |
6fbf66fa |
{ |
81d882d5 |
if (!buf_read(buf, &pid, sizeof(pid)))
{
goto done;
}
pid = ntohpid(pid);
buf_printf(&out, " " packet_id_format, (packet_id_print_type)pid);
}
if (n_ack)
{
if (!session_id_read(&sid_ack, buf))
{
goto done;
}
if (verbose)
{
buf_printf(&out, " sid=%s", session_id_print(&sid_ack, gc));
} |
6fbf66fa |
}
|
81d882d5 |
done:
buf_printf(&out, " ]");
return BSTR(&out); |
6fbf66fa |
}
/*
* struct reliable member functions.
*/
void |
81d882d5 |
reliable_init(struct reliable *rel, int buf_size, int offset, int array_size, bool hold) |
6fbf66fa |
{ |
81d882d5 |
int i; |
6fbf66fa |
|
81d882d5 |
CLEAR(*rel);
ASSERT(array_size > 0 && array_size <= RELIABLE_CAPACITY);
rel->hold = hold;
rel->size = array_size;
rel->offset = offset;
for (i = 0; i < rel->size; ++i) |
6fbf66fa |
{ |
81d882d5 |
struct reliable_entry *e = &rel->array[i];
e->buf = alloc_buf(buf_size);
ASSERT(buf_init(&e->buf, offset)); |
6fbf66fa |
}
}
void |
81d882d5 |
reliable_free(struct reliable *rel) |
6fbf66fa |
{ |
81d882d5 |
int i;
for (i = 0; i < rel->size; ++i) |
6fbf66fa |
{ |
81d882d5 |
struct reliable_entry *e = &rel->array[i];
free_buf(&e->buf); |
6fbf66fa |
}
}
/* no active buffers? */
bool |
81d882d5 |
reliable_empty(const struct reliable *rel) |
6fbf66fa |
{ |
81d882d5 |
int i;
for (i = 0; i < rel->size; ++i) |
6fbf66fa |
{ |
81d882d5 |
const struct reliable_entry *e = &rel->array[i];
if (e->active)
{
return false;
} |
6fbf66fa |
} |
81d882d5 |
return true; |
6fbf66fa |
}
/* del acknowledged items from send buf */
void |
81d882d5 |
reliable_send_purge(struct reliable *rel, struct reliable_ack *ack) |
6fbf66fa |
{ |
81d882d5 |
int i, j;
for (i = 0; i < ack->len; ++i)
{
packet_id_type pid = ack->packet_id[i];
for (j = 0; j < rel->size; ++j)
{
struct reliable_entry *e = &rel->array[j];
if (e->active && e->packet_id == pid)
{
dmsg(D_REL_DEBUG,
"ACK received for pid " packet_id_format ", deleting from send buffer",
(packet_id_print_type)pid); |
6fbf66fa |
#if 0 |
81d882d5 |
/* DEBUGGING -- how close were we timing out on ACK failure and resending? */
{
if (e->next_try)
{
const interval_t wake = e->next_try - now;
msg(M_INFO, "ACK " packet_id_format ", wake=%d", pid, wake);
}
} |
6fbf66fa |
#endif |
81d882d5 |
e->active = false;
break;
}
} |
6fbf66fa |
}
}
/* print the current sequence of active packet IDs */
static const char * |
81d882d5 |
reliable_print_ids(const struct reliable *rel, struct gc_arena *gc) |
6fbf66fa |
{ |
81d882d5 |
struct buffer out = alloc_buf_gc(256, gc);
int i; |
6fbf66fa |
|
81d882d5 |
buf_printf(&out, "[" packet_id_format "]", (packet_id_print_type)rel->packet_id);
for (i = 0; i < rel->size; ++i) |
6fbf66fa |
{ |
81d882d5 |
const struct reliable_entry *e = &rel->array[i];
if (e->active)
{
buf_printf(&out, " " packet_id_format, (packet_id_print_type)e->packet_id);
} |
6fbf66fa |
} |
81d882d5 |
return BSTR(&out); |
6fbf66fa |
}
/* true if at least one free buffer available */
bool |
81d882d5 |
reliable_can_get(const struct reliable *rel) |
6fbf66fa |
{ |
81d882d5 |
struct gc_arena gc = gc_new();
int i;
for (i = 0; i < rel->size; ++i)
{
const struct reliable_entry *e = &rel->array[i];
if (!e->active)
{
return true;
}
}
dmsg(D_REL_LOW, "ACK no free receive buffer available: %s", reliable_print_ids(rel, &gc));
gc_free(&gc);
return false; |
6fbf66fa |
}
/* make sure that incoming packet ID isn't a replay */
bool |
81d882d5 |
reliable_not_replay(const struct reliable *rel, packet_id_type id) |
6fbf66fa |
{ |
81d882d5 |
struct gc_arena gc = gc_new();
int i;
if (reliable_pid_min(id, rel->packet_id)) |
6fbf66fa |
{ |
81d882d5 |
goto bad; |
6fbf66fa |
} |
81d882d5 |
for (i = 0; i < rel->size; ++i)
{
const struct reliable_entry *e = &rel->array[i];
if (e->active && e->packet_id == id)
{
goto bad;
}
}
gc_free(&gc);
return true; |
6fbf66fa |
|
81d882d5 |
bad:
dmsg(D_REL_DEBUG, "ACK " packet_id_format " is a replay: %s", (packet_id_print_type)id, reliable_print_ids(rel, &gc));
gc_free(&gc);
return false; |
6fbf66fa |
}
/* make sure that incoming packet ID won't deadlock the receive buffer */
bool |
81d882d5 |
reliable_wont_break_sequentiality(const struct reliable *rel, packet_id_type id) |
6fbf66fa |
{ |
81d882d5 |
struct gc_arena gc = gc_new(); |
6fbf66fa |
|
81d882d5 |
const int ret = reliable_pid_in_range2(id, rel->packet_id, rel->size); |
a1849f41 |
|
81d882d5 |
if (!ret) |
6fbf66fa |
{ |
81d882d5 |
dmsg(D_REL_LOW, "ACK " packet_id_format " breaks sequentiality: %s",
(packet_id_print_type)id, reliable_print_ids(rel, &gc)); |
6fbf66fa |
} |
a1849f41 |
|
81d882d5 |
dmsg(D_REL_DEBUG, "ACK RWBS rel->size=%d rel->packet_id=%08x id=%08x ret=%d\n", rel->size, rel->packet_id, id, ret); |
a1849f41 |
|
81d882d5 |
gc_free(&gc);
return ret; |
6fbf66fa |
}
/* grab a free buffer */
struct buffer * |
81d882d5 |
reliable_get_buf(struct reliable *rel) |
6fbf66fa |
{ |
81d882d5 |
int i;
for (i = 0; i < rel->size; ++i) |
6fbf66fa |
{ |
81d882d5 |
struct reliable_entry *e = &rel->array[i];
if (!e->active)
{
ASSERT(buf_init(&e->buf, rel->offset));
return &e->buf;
} |
6fbf66fa |
} |
81d882d5 |
return NULL; |
6fbf66fa |
}
/* grab a free buffer, fail if buffer clogged by unacknowledged low packet IDs */
struct buffer * |
81d882d5 |
reliable_get_buf_output_sequenced(struct reliable *rel) |
6fbf66fa |
{ |
81d882d5 |
struct gc_arena gc = gc_new();
int i;
packet_id_type min_id = 0;
bool min_id_defined = false;
struct buffer *ret = NULL; |
6fbf66fa |
|
81d882d5 |
/* find minimum active packet_id */
for (i = 0; i < rel->size; ++i) |
6fbf66fa |
{ |
81d882d5 |
const struct reliable_entry *e = &rel->array[i];
if (e->active)
{
if (!min_id_defined || reliable_pid_min(e->packet_id, min_id))
{
min_id_defined = true;
min_id = e->packet_id;
}
} |
6fbf66fa |
}
|
81d882d5 |
if (!min_id_defined || reliable_pid_in_range1(rel->packet_id, min_id, rel->size)) |
6fbf66fa |
{ |
81d882d5 |
ret = reliable_get_buf(rel); |
6fbf66fa |
} |
81d882d5 |
else |
6fbf66fa |
{ |
81d882d5 |
dmsg(D_REL_LOW, "ACK output sequence broken: %s", reliable_print_ids(rel, &gc)); |
6fbf66fa |
} |
81d882d5 |
gc_free(&gc);
return ret; |
6fbf66fa |
}
/* get active buffer for next sequentially increasing key ID */
struct buffer * |
81d882d5 |
reliable_get_buf_sequenced(struct reliable *rel) |
6fbf66fa |
{ |
81d882d5 |
int i;
for (i = 0; i < rel->size; ++i) |
6fbf66fa |
{ |
81d882d5 |
struct reliable_entry *e = &rel->array[i];
if (e->active && e->packet_id == rel->packet_id)
{
return &e->buf;
} |
6fbf66fa |
} |
81d882d5 |
return NULL; |
6fbf66fa |
}
/* return true if reliable_send would return a non-NULL result */
bool |
81d882d5 |
reliable_can_send(const struct reliable *rel) |
6fbf66fa |
{ |
81d882d5 |
struct gc_arena gc = gc_new();
int i;
int n_active = 0, n_current = 0;
for (i = 0; i < rel->size; ++i)
{
const struct reliable_entry *e = &rel->array[i];
if (e->active)
{
++n_active;
if (now >= e->next_try)
{
++n_current;
}
}
}
dmsg(D_REL_DEBUG, "ACK reliable_can_send active=%d current=%d : %s",
n_active,
n_current,
reliable_print_ids(rel, &gc));
gc_free(&gc);
return n_current > 0 && !rel->hold; |
6fbf66fa |
}
/* return next buffer to send to remote */
struct buffer * |
81d882d5 |
reliable_send(struct reliable *rel, int *opcode) |
6fbf66fa |
{ |
81d882d5 |
int i;
struct reliable_entry *best = NULL;
const time_t local_now = now; |
6fbf66fa |
|
81d882d5 |
for (i = 0; i < rel->size; ++i) |
6fbf66fa |
{ |
81d882d5 |
struct reliable_entry *e = &rel->array[i];
if (e->active && local_now >= e->next_try)
{
if (!best || reliable_pid_min(e->packet_id, best->packet_id))
{
best = e;
}
} |
6fbf66fa |
} |
81d882d5 |
if (best) |
6fbf66fa |
{
#ifdef EXPONENTIAL_BACKOFF |
81d882d5 |
/* exponential backoff */ |
2af8853e |
best->next_try = local_now + best->timeout; |
81d882d5 |
best->timeout *= 2; |
6fbf66fa |
#else |
81d882d5 |
/* constant timeout, no backoff */
best->next_try = local_now + best->timeout; |
6fbf66fa |
#endif |
81d882d5 |
*opcode = best->opcode;
dmsg(D_REL_DEBUG, "ACK reliable_send ID " packet_id_format " (size=%d to=%d)",
(packet_id_print_type)best->packet_id, best->buf.len,
(int)(best->next_try - local_now));
return &best->buf; |
6fbf66fa |
} |
81d882d5 |
return NULL; |
6fbf66fa |
}
/* schedule all pending packets for immediate retransmit */
void |
81d882d5 |
reliable_schedule_now(struct reliable *rel) |
6fbf66fa |
{ |
81d882d5 |
int i;
dmsg(D_REL_DEBUG, "ACK reliable_schedule_now");
rel->hold = false;
for (i = 0; i < rel->size; ++i)
{
struct reliable_entry *e = &rel->array[i];
if (e->active)
{
e->next_try = now;
e->timeout = rel->initial_timeout;
} |
6fbf66fa |
}
}
/* in how many seconds should we wake up to check for timeout */
/* if we return BIG_TIMEOUT, nothing to wait for */
interval_t |
81d882d5 |
reliable_send_timeout(const struct reliable *rel) |
6fbf66fa |
{ |
81d882d5 |
struct gc_arena gc = gc_new();
interval_t ret = BIG_TIMEOUT;
int i;
const time_t local_now = now;
for (i = 0; i < rel->size; ++i)
{
const struct reliable_entry *e = &rel->array[i];
if (e->active)
{
if (e->next_try <= local_now)
{
ret = 0;
break;
}
else
{
ret = min_int(ret, e->next_try - local_now);
}
}
}
dmsg(D_REL_DEBUG, "ACK reliable_send_timeout %d %s",
(int) ret,
reliable_print_ids(rel, &gc));
gc_free(&gc);
return ret; |
6fbf66fa |
}
/*
* Enable an incoming buffer previously returned by a get function as active.
*/
void |
81d882d5 |
reliable_mark_active_incoming(struct reliable *rel, struct buffer *buf,
packet_id_type pid, int opcode) |
6fbf66fa |
{ |
81d882d5 |
int i;
for (i = 0; i < rel->size; ++i) |
6fbf66fa |
{ |
81d882d5 |
struct reliable_entry *e = &rel->array[i];
if (buf == &e->buf)
{
e->active = true; |
6fbf66fa |
|
81d882d5 |
/* packets may not arrive in sequential order */
e->packet_id = pid; |
6fbf66fa |
|
81d882d5 |
/* check for replay */
ASSERT(!reliable_pid_min(pid, rel->packet_id)); |
6fbf66fa |
|
81d882d5 |
e->opcode = opcode;
e->next_try = 0;
e->timeout = 0;
dmsg(D_REL_DEBUG, "ACK mark active incoming ID " packet_id_format, (packet_id_print_type)e->packet_id);
return;
} |
6fbf66fa |
} |
81d882d5 |
ASSERT(0); /* buf not found in rel */ |
6fbf66fa |
}
/*
* Enable an outgoing buffer previously returned by a get function as active.
*/
void |
81d882d5 |
reliable_mark_active_outgoing(struct reliable *rel, struct buffer *buf, int opcode) |
6fbf66fa |
{ |
81d882d5 |
int i;
for (i = 0; i < rel->size; ++i)
{
struct reliable_entry *e = &rel->array[i];
if (buf == &e->buf)
{
/* Write mode, increment packet_id (i.e. sequence number)
* linearly and prepend id to packet */
packet_id_type net_pid;
e->packet_id = rel->packet_id++;
net_pid = htonpid(e->packet_id);
ASSERT(buf_write_prepend(buf, &net_pid, sizeof(net_pid)));
e->active = true;
e->opcode = opcode;
e->next_try = 0;
e->timeout = rel->initial_timeout;
dmsg(D_REL_DEBUG, "ACK mark active outgoing ID " packet_id_format, (packet_id_print_type)e->packet_id);
return;
}
}
ASSERT(0); /* buf not found in rel */ |
6fbf66fa |
}
/* delete a buffer previously activated by reliable_mark_active() */
void |
81d882d5 |
reliable_mark_deleted(struct reliable *rel, struct buffer *buf, bool inc_pid) |
6fbf66fa |
{ |
81d882d5 |
int i;
for (i = 0; i < rel->size; ++i)
{
struct reliable_entry *e = &rel->array[i];
if (buf == &e->buf)
{
e->active = false;
if (inc_pid)
{
rel->packet_id = e->packet_id + 1;
}
return;
}
}
ASSERT(0); |
6fbf66fa |
}
#if 0
void |
81d882d5 |
reliable_ack_debug_print(const struct reliable_ack *ack, char *desc) |
6fbf66fa |
{ |
81d882d5 |
int i; |
6fbf66fa |
|
81d882d5 |
printf("********* struct reliable_ack %s\n", desc);
for (i = 0; i < ack->len; ++i) |
6fbf66fa |
{ |
81d882d5 |
printf(" %d: " packet_id_format "\n", i, (packet_id_print_type) ack->packet_id[i]); |
6fbf66fa |
}
}
void |
81d882d5 |
reliable_debug_print(const struct reliable *rel, char *desc) |
6fbf66fa |
{ |
81d882d5 |
int i;
update_time();
printf("********* struct reliable %s\n", desc);
printf(" initial_timeout=%d\n", (int)rel->initial_timeout);
printf(" packet_id=" packet_id_format "\n", rel->packet_id); |
06ad53e0 |
printf(" now=%"PRIi64"\n", (int64_t)now); |
81d882d5 |
for (i = 0; i < rel->size; ++i)
{
const struct reliable_entry *e = &rel->array[i];
if (e->active)
{
printf(" %d: packet_id=" packet_id_format " len=%d", i, e->packet_id, e->buf.len); |
06ad53e0 |
printf(" next_try=%"PRIi64, (int64_t)e->next_try); |
81d882d5 |
printf("\n");
} |
6fbf66fa |
}
}
|
81d882d5 |
#endif /* if 0 */ |