📄 reliable.c
字号:
/* * 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. * * Copyright (C) 2002-2004 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. */#ifdef WIN32#include "config-win32.h"#else#include "config.h"#endif#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"/* check if a particular packet_id is present in ack */static inline boolreliable_ack_packet_id_present (struct reliable_ack *ack, packet_id_type pid){ int i; for (i = 0; i < ack->len; ++i) if (ack->packet_id[i] == pid) return true; return false;}/* get a packet_id from buf */boolreliable_ack_read_packet_id (struct buffer *buf, packet_id_type *pid){ packet_id_type net_pid; if (buf_read (buf, &net_pid, sizeof (net_pid))) { *pid = ntohpid (net_pid); msg (D_REL_DEBUG, "ACK read ID " packet_id_format " (buf->len=%d)", (packet_id_print_type)*pid, buf->len); return true; } msg (D_REL_LOW, "ACK read ID FAILED (buf->len=%d)", buf->len); return false;}/* acknowledge a packet_id by adding it to a struct reliable_ack */boolreliable_ack_acknowledge_packet_id (struct reliable_ack *ack, packet_id_type pid){ if (!reliable_ack_packet_id_present (ack, pid) && ack->len < RELIABLE_ACK_SIZE) { ack->packet_id[ack->len++] = pid; msg (D_REL_DEBUG, "ACK acknowledge ID " packet_id_format " (ack->len=%d)", (packet_id_print_type)pid, ack->len); return true; } msg (D_REL_LOW, "ACK acknowledge ID " packet_id_format " FAILED (ack->len=%d)", (packet_id_print_type)pid, 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){ 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)) { msg (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;error: gc_free (&gc); 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 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;) 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_add_to_extra_frame (frame, ACK_SIZE (max));}/* print a reliable ACK record coming off the wire */const char *reliable_ack_print (struct buffer *buf, bool verbose, struct gc_arena *gc){ int i; uint8_t n_ack; struct session_id sid_ack; packet_id_type pid; struct buffer out = alloc_buf_gc (256, gc); 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, (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)); } done: buf_printf (&out, " ]"); return BSTR (&out);}/* * struct reliable member functions. */voidreliable_init (struct reliable *rel, int buf_size, int offset, int array_size){ int i; CLEAR (*rel); ASSERT (array_size > 0 && array_size <= RELIABLE_CAPACITY); rel->size = array_size; rel->offset = offset; for (i = 0; i < rel->size; ++i) { struct reliable_entry *e = &rel->array[i]; e->buf = alloc_buf (buf_size); ASSERT (buf_init (&e->buf, offset)); }}voidreliable_free (struct reliable *rel){ int i; for (i = 0; i < rel->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 < rel->size; ++i) { const struct reliable_entry *e = &rel->array[i]; if (e->active) return false; } return true;}/* 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 < rel->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", (packet_id_print_type)pid);#if 0 /* 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); } }#endif e->active = false; break; } } }}/* print the current sequence of active packet IDs */static const char *reliable_print_ids (const struct reliable *rel, struct gc_arena *gc){ struct buffer out = alloc_buf_gc (512, gc); int i; buf_printf (&out, "[" packet_id_format "]", (packet_id_print_type)rel->packet_id); for (i = 0; i < rel->size; ++i) { const struct reliable_entry *e = &rel->array[i]; if (e->active) buf_printf (&out, " " packet_id_format, (packet_id_print_type)e->packet_id); } return BSTR (&out);}/* true if at least one free buffer available */boolreliable_can_get (const struct reliable *rel){ 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; } msg (D_REL_LOW, "ACK no free receive buffer available: %s", reliable_print_ids (rel, &gc)); gc_free (&gc); return false;}/* make sure that incoming packet ID isn't a replay */boolreliable_not_replay (const struct reliable *rel, packet_id_type id){ struct gc_arena gc = gc_new (); int i; if (id < rel->packet_id) goto bad; 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; bad: msg (D_REL_DEBUG, "ACK " packet_id_format " is a replay: %s", (packet_id_print_type)id, reliable_print_ids (rel, &gc));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -