/* This file is part of GNUnet (C) 2004 Christian Grothoff (and other contributing authors) GNUnet 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, or (at your option) any later version. GNUnet 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 GNUnet; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /** * @file server/fragmentation.c * @brief fragmentation and defragmentation, this code allows * sending and receiving messages that are larger than * the MTU of the transport. Messages are still limited * to a maximum size of 65535 bytes, which is a good * idea because otherwise we may need ungainly fragmentation * buffers. Each connected peer can have at most one * fragmented packet at any given point in time (prevents * DoS attacks). Fragmented messages that have not been * completed after a certain amount of time are discarded. * @author Christian Grothoff * * todo: * - test * - integrate into knapsack, but in such a way that * if we send fragments, we'll send them _all_. * (otherwise we'll waste too much bandwidth...) */ #include "gnunet_core.h" #include "platform.h" #include "handler.h" #include "fragmentation.h" /** * How many buckets does the fragment hash table * have? */ #define DEFRAG_BUCKET_COUNT 16 /** * After how long do fragments time out? */ #ifndef DEFRAGMENTATION_TIMEOUT #define DEFRAGMENTATION_TIMEOUT (3 * cronMINUTES) #endif /** * Entry in the linked list of fragments. */ typedef struct FL { struct FL * link; FRAGMENT_Message * frag; } FL; /** * Entry in the hash table of fragments. */ typedef struct FC { struct FC * next; FL * head; HostIdentity sender; int id; cron_t ttl; } FC; #define FRAGSIZE(fl) ((ntohs(fl->frag->header.size)-sizeof(FRAGMENT_Message))) /** * Hashtable *with* collision management! */ static FC * defragmentationCache[DEFRAG_BUCKET_COUNT]; /** * Lock for the defragmentation cache. */ static Mutex defragCacheLock; static void freeFL(FL * fl) { while (fl != NULL) { FL * link = fl->link; FREE(fl->frag); FREE(fl); fl = link; } } /** * This cron job ensures that we purge buffers of fragments * that have timed out. It can run in much longer intervals * than the defragmentationCron, e.g. every 60s. *
* This method goes through the hashtable, finds entries that
* have timed out and removes them (and all the fragments that
* belong to the entry). It's a bit more complicated as the
* collision list is also collapsed.
*/
static void defragmentationPurgeCron() {
int i;
FC * smf;
FC * next;
FC * last;
MUTEX_LOCK(&defragCacheLock);
for (i=0;i
*
* @param entry the entry in the cache
* @param pep the new entry
* @param packet the ip part in the new entry
*/
static int tryJoin(FC * entry,
const HostIdentity * sender,
const FRAGMENT_Message * packet) {
/* frame before ours; may end in the middle of
our frame or before it starts; NULL if we are
the earliest position we have received so far */
FL * before;
/* frame after ours; may start in the middle of
our frame or after it; NULL if we are the last
fragment we have received so far */
FL * after;
/* current position in the frame-list */
FL * pos;
/* the new entry that we're inserting */
FL * pep;
FL * tmp;
unsigned short end;
GNUNET_ASSERT(entry != NULL);
if (! hostIdentityEquals(sender,
&entry->sender))
return SYSERR; /* wrong fragment list, try another! */
if (ntohl(packet->id) != entry->id)
return SYSERR; /* wrong fragment list, try another! */
pos = entry->head;
if ( (pos != NULL) &&
(packet->len != pos->frag->len) )
return SYSERR; /* wrong fragment size */
before = NULL;
/* find the before-frame */
while ( (pos != NULL) &&
(ntohs(pos->frag->off) <
ntohs(packet->off)) ) {
before = pos;
pos = pos->link;
}
/* find the after-frame */
end = ntohs(packet->off) + ntohs(packet->header.size) - sizeof(FRAGMENT_Message);
if (end <= ntohs(packet->off)) {
LOG(LOG_DEBUG,
"Received invalid fragment at %s:%d\n",
__FILE__, __LINE__);
return SYSERR; /* yuck! integer overflow! */
}
if (before != NULL)
after = before;
else
after = entry->head;
while ( (after != NULL) &&
(ntohs(after->frag->off)