📄 reliable.c
字号:
/* * OpenVPN -- An application to securely tunnel IP networks * over a single UDP port, with support for TLS-based * session authentication and key exchange, * packet encryption, packet authentication, and * packet compression. * * Copyright (C) 2002 James Yonan <jim@yonan.net> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program (see the file COPYING included with this * distribution); if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *//* * These routines implement a reliability layer on top of UDP, * so that SSL/TLS can be run over UDP. */#include "config.h"#if defined(USE_CRYPTO) && defined(USE_SSL)#include "syshead.h"#include "buffer.h"#include "error.h"#include "common.h"#include "reliable.h"#include "memdbg.h"/* * Add the packet ID of buf to ack, advance buf ptr, place packet ID in *pid. * Return true if we were successful, false if ack is full. */boolreliable_ack_read_packet_id (struct reliable_ack *ack, struct buffer *buf, packet_id_type *pid){ packet_id_type net_pid; if (buf_read (buf, &net_pid, sizeof (net_pid))) { if (ack->len < RELIABLE_ACK_SIZE) { *pid = ack->packet_id[ack->len++] = ntohpid (net_pid); msg (D_REL_DEBUG, "ACK ID " packet_id_format " (buf->len=%d, ack->len=%d)", *pid, buf->len, ack->len); return true; } } msg (D_REL_LOW, "ACK ID FAILED (buf->len=%d, ack->len=%d)", buf->len, ack->len); return false;}/* read a packet ID acknowledgement record from buf into ack */boolreliable_ack_read (struct reliable_ack * ack, struct buffer * buf, const struct session_id * sid){ 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)) { msg (D_REL_LOW, "ACK read BAD SESSION-ID FROM REMOTE, local=%s, remote=%s", session_id_print (sid), session_id_print (&session_id_remote)); goto error; } } return true;error: return false;}#define ACK_SIZE(n) (sizeof (uint8_t) + ((n) ? SID_SIZE : 0) + sizeof (packet_id_type) * (n))/* write a packet ID acknowledgement record to buf, *//* removing all acknowledged entries from ack */boolreliable_ack_write (struct reliable_ack * ack, struct buffer * buf, const struct session_id * sid, int max, bool prepend){ int i, j; uint8_t n; struct buffer sub; n = ack->len; if (n > max) n = max; sub = buf_sub (buf, ACK_SIZE(n), prepend); if (!BDEF (&sub)) 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))); msg (D_REL_DEBUG, "ACK write " packet_id_format " (ack->len=%d, n=%d)", 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;) ack->packet_id[i++] = ack->packet_id[j++]; ack->len = i; } return true;error: return false;}/* add to extra_frame the maximum number of bytes we will need for reliable_ack_write */voidreliable_ack_adjust_frame_parameters (struct frame* frame, int max){ frame->extra_frame += ACK_SIZE (max);}/* print a reliable ACK record coming off the wire */const char*reliable_ack_print(struct buffer* buf){ int i; uint8_t n_ack; struct session_id sid_ack; packet_id_type pid; struct buffer out = alloc_buf_gc (256); buf_printf (&out, "["); if (!buf_read (buf, &n_ack, sizeof (n_ack))) goto done; for (i = 0; i < n_ack; ++i) { if (!buf_read (buf, &pid, sizeof (pid))) goto done; pid = ntohpid (pid); buf_printf (&out, " " packet_id_format, pid); } if (n_ack) { if (!session_id_read (&sid_ack, buf)) goto done; buf_printf (&out, " sid=%s", session_id_print (&sid_ack)); } done: buf_printf (&out, " ]"); return out.data;}/* * struct reliable member functions. */voidreliable_init (struct reliable *rel, int size, int offset){ int i; CLEAR (*rel); rel->offset = offset; for (i = 0; i < RELIABLE_SIZE; ++i) { struct reliable_entry *e = &rel->array[i]; e->buf = alloc_buf (size); ASSERT (buf_init (&e->buf, offset)); }}voidreliable_free (struct reliable *rel){ int i; for (i = 0; i < RELIABLE_SIZE; ++i) { struct reliable_entry *e = &rel->array[i]; free_buf (&e->buf); }}/* no active buffers? */boolreliable_empty (const struct reliable *rel){ int i; for (i = 0; i < RELIABLE_SIZE; ++i) { const struct reliable_entry *e = &rel->array[i]; if (e->active) return false; } return true;}/* in how many seconds should we wake up to check for timeout *//* if we return 0, nothing to wait for */intreliable_send_timeout (const struct reliable *rel, time_t current){ int ret = 0; int i; for (i = 0; i < RELIABLE_SIZE; ++i) { const struct reliable_entry *e = &rel->array[i]; if (e->active && e->next_try) { int wake = e->next_try - current; if (wake < 1) wake = 1; if (!ret || wake < ret) ret = wake; } } return ret;}/* del acknowledged items from send buf */voidreliable_send_purge (struct reliable *rel, struct reliable_ack *ack){ int i, j; for (i = 0; i < ack->len; ++i) { packet_id_type pid = ack->packet_id[i]; for (j = 0; j < RELIABLE_SIZE; ++j) { struct reliable_entry *e = &rel->array[j]; if (e->active && e->packet_id == pid) { msg (D_REL_DEBUG, "ACK received for pid " packet_id_format ", deleting from send buffer", pid);#if 0 /* DEBUGGING -- how close were we timing out on ACK failure and resending? */ { const int wake = e->next_try - time(NULL); msg (M_INFO, "ACK " packet_id_format ", wake=%d", pid, wake); }#endif e->active = false; break; } } }}/* true if at least one free buffer available */boolreliable_can_get (const struct reliable *rel){ int i; for (i = 0; i < RELIABLE_SIZE; ++i) { const struct reliable_entry *e = &rel->array[i]; if (!e->active) return true; } return false;}/* grab a free buffer */struct buffer *reliable_get_buf (struct reliable *rel){ int i; for (i = 0; i < RELIABLE_SIZE; ++i) { struct reliable_entry *e = &rel->array[i]; if (!e->active) { ASSERT (buf_init (&e->buf, rel->offset)); return &e->buf; } } return NULL;}/* get active buffer for next sequentially increasing key ID */struct buffer *reliable_get_buf_sequenced (struct reliable *rel){ int i; for (i = 0; i < RELIABLE_SIZE; ++i) { struct reliable_entry *e = &rel->array[i]; if (e->active && e->packet_id == rel->packet_id) { return &e->buf; } } return NULL;}/* return true if reliable_send would return a non-NULL result */boolreliable_can_send (const struct reliable *rel, time_t current){ int i; for (i = 0; i < RELIABLE_SIZE; ++i) { const struct reliable_entry *e = &rel->array[i]; if (e->active && current >= e->next_try) return true; } return false;}/* return next buffer to send to remote */struct buffer *reliable_send (struct reliable *rel, int *opcode, time_t current){ int i; struct reliable_entry *best = NULL; for (i = 0; i < RELIABLE_SIZE; ++i) { struct reliable_entry *e = &rel->array[i]; if (e->active && current >= e->next_try) { if (!best || e->packet_id < best->packet_id) best = e; } } if (best) { best->next_try = current + rel->timeout; *opcode = best->opcode; return &best->buf; } return NULL;}/* schedule all pending packets for immediate retransmit */voidreliable_schedule_now (struct reliable *rel, time_t current){ int i; msg (D_REL_DEBUG, "reliable_schedule_now"); for (i = 0; i < RELIABLE_SIZE; ++i) { struct reliable_entry *e = &rel->array[i]; if (e->active) e->next_try = current; }}/* * Enable an incoming buffer previously returned by a get function as active. */voidreliable_mark_active_incoming (struct reliable *rel, struct buffer *buf, packet_id_type pid, int opcode){ int i; for (i = 0; i < RELIABLE_SIZE; ++i) { struct reliable_entry *e = &rel->array[i]; if (buf == &e->buf) { /* packets may not arrive in sequential order */ e->packet_id = pid; /* throw away old, previously received packets */ e->active = (pid >= rel->packet_id); e->opcode = opcode; e->next_try = 0; msg (D_REL_DEBUG, "ACK Mark Active Incoming ID " packet_id_format, e->packet_id); return; } } ASSERT (0); /* buf not found in rel */}/* * Enable an outgoing buffer previously returned by a get function as active. */voidreliable_mark_active_outgoing (struct reliable *rel, struct buffer *buf, int opcode){ int i; for (i = 0; i < RELIABLE_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; msg (D_REL_DEBUG, "ACK Mark Active Outgoing ID " packet_id_format, e->packet_id); return; } } ASSERT (0); /* buf not found in rel */}/* delete a buffer previously activated by reliable_mark_active() */voidreliable_mark_deleted (struct reliable *rel, struct buffer *buf, bool inc_pid){ int i; for (i = 0; i < RELIABLE_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);}#if 0voidreliable_ack_debug_print (const struct reliable_ack *ack, char *desc){ int i; time_t current = time (NULL); printf ("********* struct reliable_ack %s\n", desc); for (i = 0; i < ack->len; ++i) { printf (" %d: " packet_id_format "\n", i, ack->packet_id[i]); }}voidreliable_debug_print (const struct reliable *rel, char *desc){ int i; time_t current = time (NULL); printf ("********* struct reliable %s\n", desc); printf (" timeout=%d\n", rel->timeout); printf (" packet_id=" packet_id_format "\n", rel->packet_id); printf (" current=" time_format "\n", current); for (i = 0; i < RELIABLE_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); printf (" next_try=" time_format, e->next_try); printf ("\n"); } }}#endif#endif /* USE_CRYPTO && USE_SSL*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -