/* * 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-2005 OpenVPN Solutions LLC * * 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. * * 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 */ #ifdef WIN32 #include "config-win32.h" #else #include "config.h" #endif #include "syshead.h" #ifdef ENABLE_FRAGMENT #include "misc.h" #include "fragment.h" #include "integer.h" #include "memdbg.h" #define FRAG_ERR(s) { errmsg = s; goto error; } static void fragment_list_buf_init (struct fragment_list *list, const struct frame *frame) { int i; for (i = 0; i < N_FRAG_BUF; ++i) list->fragments[i].buf = alloc_buf (BUF_SIZE (frame)); } static void fragment_list_buf_free (struct fragment_list *list) { int i; for (i = 0; i < N_FRAG_BUF; ++i) free_buf (&list->fragments[i].buf); } /* * Given a sequence ID number, get a fragment buffer. Use a sliding window, * similar to packet_id code. */ static struct fragment * fragment_list_get_buf (struct fragment_list *list, int seq_id) { int diff; if (abs (diff = modulo_subtract (seq_id, list->seq_id, N_SEQ_ID)) >= N_FRAG_BUF) { int i; for (i = 0; i < N_FRAG_BUF; ++i) list->fragments[i].defined = false; list->index = 0; list->seq_id = seq_id; diff = 0; } while (diff > 0) { list->fragments[list->index = modulo_add (list->index, 1, N_FRAG_BUF)].defined = false; list->seq_id = modulo_add (list->seq_id, 1, N_SEQ_ID); --diff; } return &list->fragments[modulo_add (list->index, diff, N_FRAG_BUF)]; } struct fragment_master * fragment_init (struct frame *frame) { struct fragment_master *ret; /* code that initializes other parts of fragment_master assume an initial CLEAR */ ALLOC_OBJ_CLEAR (ret, struct fragment_master); /* add in the size of our contribution to the expanded frame size */ frame_add_to_extra_frame (frame, sizeof(fragment_header_type)); /* * Outgoing sequence ID is randomized to reduce * the probability of sequence number collisions * when openvpn sessions are restarted. This is * not done out of any need for security, as all * fragmentation control information resides * inside of the encrypted/authenticated envelope. */ ret->outgoing_seq_id = (int)get_random() & (N_SEQ_ID - 1); event_timeout_init (&ret->wakeup, FRAG_WAKEUP_INTERVAL, now); return ret; } void fragment_free (struct fragment_master *f) { fragment_list_buf_free (&f->incoming); free_buf (&f->outgoing); free_buf (&f->outgoing_return); free (f); } void fragment_frame_init (struct fragment_master *f, const struct frame *frame) { fragment_list_buf_init (&f->incoming, frame); f->outgoing = alloc_buf (BUF_SIZE (frame)); f->outgoing_return = alloc_buf (BUF_SIZE (frame)); } /* * Accept an incoming datagram (which may be a fragment) from remote. * If the datagram is whole (i.e not a fragment), pass through. * If the datagram is a fragment, join with other fragments received so far. * If a fragment fully completes the datagram, return the datagram. */ void fragment_incoming (struct fragment_master *f, struct buffer *buf, const struct frame* frame) { const char *errmsg = NULL; fragment_header_type flags = 0; int frag_type = 0; if (buf->len > 0) { /* get flags from packet head */ if (!buf_read (buf, &flags, sizeof (flags))) FRAG_ERR ("flags not found in packet"); flags = ntoh_fragment_header_type (flags); /* get fragment type from flags */ frag_type = ((flags >> FRAG_TYPE_SHIFT) & FRAG_TYPE_MASK); #if 0 /* * If you want to extract FRAG_EXTRA_MASK/FRAG_EXTRA_SHIFT bits, * do it here. */ if (frag_type == FRAG_WHOLE || frag_type == FRAG_YES_NOTLAST) { } #endif /* handle the fragment type */ if (frag_type == FRAG_WHOLE) { dmsg (D_FRAG_DEBUG, "FRAG_IN buf->len=%d type=FRAG_WHOLE flags=" fragment_header_format, buf->len, flags); if (flags & (FRAG_SEQ_ID_MASK | FRAG_ID_MASK)) FRAG_ERR ("spurrious FRAG_WHOLE flags"); } else if (frag_type == FRAG_YES_NOTLAST || frag_type == FRAG_YES_LAST) { const int seq_id = ((flags >> FRAG_SEQ_ID_SHIFT) & FRAG_SEQ_ID_MASK); const int n = ((flags >> FRAG_ID_SHIFT) & FRAG_ID_MASK); const int size = ((frag_type == FRAG_YES_LAST) ? (int)(((flags >> FRAG_SIZE_SHIFT) & FRAG_SIZE_MASK) << FRAG_SIZE_ROUND_SHIFT) : buf->len); /* get the appropriate fragment buffer based on received seq_id */ struct fragment *frag = fragment_list_get_buf (&f->incoming, seq_id); dmsg (D_FRAG_DEBUG, "FRAG_IN len=%d type=%d seq_id=%d frag_id=%d size=%d flags=" fragment_header_format, buf->len, frag_type, seq_id, n, size, flags); /* make sure that size is an even multiple of 1<defined || (frag->defined && frag->max_frag_size != size)) { frag->defined = true; frag->max_frag_size = size; frag->map = 0; ASSERT (buf_init (&frag->buf, FRAME_HEADROOM_ADJ (frame, FRAME_HEADROOM_MARKER_FRAGMENT))); } /* copy the data to fragment buffer */ if (!buf_copy_range (&frag->buf, n * size, buf, 0, buf->len)) FRAG_ERR ("fragment buffer overflow"); /* set elements in bit array to reflect which fragments have been received */ frag->map |= (((frag_type == FRAG_YES_LAST) ? FRAG_MAP_MASK : 1) << n); /* update timestamp on partially built datagram */ frag->timestamp = now; /* received full datagram? */ if ((frag->map & FRAG_MAP_MASK) == FRAG_MAP_MASK) { frag->defined = false; *buf = frag->buf; } else { buf->len = 0; } } else if (frag_type == FRAG_TEST) { FRAG_ERR ("FRAG_TEST not implemented"); } else { FRAG_ERR ("unknown fragment type"); } } return; error: if (errmsg) msg (D_FRAG_ERRORS, "FRAG_IN error flags=" fragment_header_format ": %s", flags, errmsg); buf->len = 0; return; } /* pack fragment parms into a uint32_t and prepend to buffer */ static void fragment_prepend_flags (struct buffer *buf, int type, int seq_id, int frag_id, int frag_size) { fragment_header_type flags = ((type & FRAG_TYPE_MASK) << FRAG_TYPE_SHIFT) | ((seq_id & FRAG_SEQ_ID_MASK) << FRAG_SEQ_ID_SHIFT) | ((frag_id & FRAG_ID_MASK) << FRAG_ID_SHIFT); if (type == FRAG_WHOLE || type == FRAG_YES_NOTLAST) { /* * If you want to set FRAG_EXTRA_MASK/FRAG_EXTRA_SHIFT bits, * do it here. */ dmsg (D_FRAG_DEBUG, "FRAG_OUT len=%d type=%d seq_id=%d frag_id=%d frag_size=%d flags=" fragment_header_format, buf->len, type, seq_id, frag_id, frag_size, flags); } else { flags |= (((frag_size >> FRAG_SIZE_ROUND_SHIFT) & FRAG_SIZE_MASK) << FRAG_SIZE_SHIFT); dmsg (D_FRAG_DEBUG, "FRAG_OUT len=%d type=%d seq_id=%d frag_id=%d frag_size=%d flags=" fragment_header_format, buf->len, type, seq_id, frag_id, frag_size, flags); } flags = hton_fragment_header_type (flags); ASSERT (buf_write_prepend (buf, &flags, sizeof (flags))); } /* * Without changing the number of fragments, return a possibly smaller * max fragment size that will allow for the last fragment to be of * similar size as previous fragments. */ static inline int optimal_fragment_size (int len, int max_frag_size) { const int mfs_aligned = (max_frag_size & ~FRAG_SIZE_ROUND_MASK); const int div = len / mfs_aligned; const int mod = len % mfs_aligned; if (div > 0 && mod > 0 && mod < mfs_aligned * 3 / 4) return min_int (mfs_aligned, (max_frag_size - ((max_frag_size - mod) / (div + 1)) + FRAG_SIZE_ROUND_MASK) & ~FRAG_SIZE_ROUND_MASK); else return mfs_aligned; } /* process an outgoing datagram, possibly breaking it up into fragments */ void fragment_outgoing (struct fragment_master *f, struct buffer *buf, const struct frame* frame) { const char *errmsg = NULL; if (buf->len > 0) { /* The outgoing buffer should be empty so we can put new data in it */ if (f->outgoing.len) msg (D_FRAG_ERRORS, "FRAG: outgoing buffer is not empty, len=[%d,%d]", buf->len, f->outgoing.len); if (buf->len > PAYLOAD_SIZE_DYNAMIC(frame)) /* should we fragment? */ { /* * Send the datagram as a series of 2 or more fragments. */ f->outgoing_frag_size = optimal_fragment_size (buf->len, PAYLOAD_SIZE_DYNAMIC(frame)); if (buf->len > f->outgoing_frag_size * MAX_FRAGS) FRAG_ERR ("too many fragments would be required to send datagram"); ASSERT (buf_init (&f->outgoing, FRAME_HEADROOM (frame))); ASSERT (buf_copy (&f->outgoing, buf)); f->outgoing_seq_id = modulo_add (f->outgoing_seq_id, 1, N_SEQ_ID); f->outgoing_frag_id = 0; buf->len = 0; ASSERT (fragment_ready_to_send (f, buf, frame)); } else { /* * Send the datagram whole. */ fragment_prepend_flags (buf, FRAG_WHOLE, 0, 0, 0); } } return; error: if (errmsg) msg (D_FRAG_ERRORS, "FRAG_OUT error, len=%d frag_size=%d MAX_FRAGS=%d: %s", buf->len, f->outgoing_frag_size, MAX_FRAGS, errmsg); buf->len = 0; return; } /* return true (and set buf) if we have an outgoing fragment which is ready to send */ bool fragment_ready_to_send (struct fragment_master *f, struct buffer *buf, const struct frame* frame) { if (fragment_outgoing_defined (f)) { /* get fragment size, and determine if it is the last fragment */ int size = f->outgoing_frag_size; int last = false; if (f->outgoing.len <= size) { size = f->outgoing.len; last = true; } /* initialize return buffer */ *buf = f->outgoing_return; ASSERT (buf_init (buf, FRAME_HEADROOM (frame))); ASSERT (buf_copy_n (buf, &f->outgoing, size)); /* fragment flags differ based on whether or not we are sending the last fragment */ fragment_prepend_flags (buf, last ? FRAG_YES_LAST : FRAG_YES_NOTLAST, f->outgoing_seq_id, f->outgoing_frag_id++, f->outgoing_frag_size); ASSERT (!last || !f->outgoing.len); /* outgoing buffer length should be zero after last fragment sent */ return true; } else return false; } static void fragment_ttl_reap (struct fragment_master *f) { int i; for (i = 0; i < N_FRAG_BUF; ++i) { struct fragment *frag = &f->incoming.fragments[i]; if (frag->defined && frag->timestamp + FRAG_TTL_SEC <= now) { msg (D_FRAG_ERRORS, "FRAG TTL expired i=%d", i); frag->defined = false; } } } /* called every FRAG_WAKEUP_INTERVAL seconds */ void fragment_wakeup (struct fragment_master *f, struct frame *frame) { /* delete fragments with expired TTLs */ fragment_ttl_reap (f); } #else static void dummy(void) {} #endif