aboutsummaryrefslogtreecommitdiff
path: root/src/fragmentation
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2009-05-29 00:46:26 +0000
committerChristian Grothoff <christian@grothoff.org>2009-05-29 00:46:26 +0000
commit0a217a8df1657b4334b55b0e4a6c7837a8dbcfd9 (patch)
tree6b552f40eb089db96409a312a98d9b12bd669102 /src/fragmentation
downloadgnunet-0a217a8df1657b4334b55b0e4a6c7837a8dbcfd9.tar.gz
gnunet-0a217a8df1657b4334b55b0e4a6c7837a8dbcfd9.zip
ng
Diffstat (limited to 'src/fragmentation')
-rw-r--r--src/fragmentation/Makefile.am28
-rw-r--r--src/fragmentation/fragmentation.c702
-rw-r--r--src/fragmentation/test_fragmentation.c439
3 files changed, 1169 insertions, 0 deletions
diff --git a/src/fragmentation/Makefile.am b/src/fragmentation/Makefile.am
new file mode 100644
index 000000000..d3a47d920
--- /dev/null
+++ b/src/fragmentation/Makefile.am
@@ -0,0 +1,28 @@
1INCLUDES = -I$(top_srcdir)/src/include
2
3if MINGW
4 WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols
5endif
6
7if USE_COVERAGE
8 AM_CFLAGS = --coverage
9endif
10
11lib_LTLIBRARIES = libgnunetfragmentation.la
12
13libgnunetfragmentation_la_SOURCES = \
14 fragmentation.c
15libgnunetfragmentation_la_LIBADD = \
16 $(top_builddir)/src/util/libgnunetutil.la
17
18check_PROGRAMS = \
19 test_fragmentation
20
21TESTS = $(check_PROGRAMS)
22
23test_fragmentation_SOURCES = \
24 test_fragmentation.c
25test_fragmentation_LDADD = \
26 $(top_builddir)/src/fragmentation/libgnunetfragmentation.la \
27 $(top_builddir)/src/util/libgnunetutil.la
28
diff --git a/src/fragmentation/fragmentation.c b/src/fragmentation/fragmentation.c
new file mode 100644
index 000000000..9550663c7
--- /dev/null
+++ b/src/fragmentation/fragmentation.c
@@ -0,0 +1,702 @@
1/*
2 This file is part of GNUnet
3 (C) 2004, 2006, 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20/**
21 * @file fragmentation/fragmentation.c
22 * @brief fragmentation and defragmentation, this code allows
23 * sending and receiving messages that are larger than
24 * the MTU of the transport. Messages are still limited
25 * to a maximum size of 65535 bytes, which is a good
26 * idea because otherwise we may need ungainly fragmentation
27 * buffers. Each connected peer can have at most one
28 * fragmented packet at any given point in time (prevents
29 * DoS attacks). Fragmented messages that have not been
30 * completed after a certain amount of time are discarded.
31 * @author Christian Grothoff
32 */
33
34#include "platform.h"
35#include "gnunet_fragmentation_lib.h"
36
37/**
38 * Message fragment. This header is followed
39 * by the actual data of the fragment.
40 */
41struct Fragment
42{
43
44 struct GNUNET_MessageHeader header;
45
46 /**
47 * Fragment offset.
48 */
49 uint32_t off GNUNET_PACKED;
50
51 /**
52 * "unique" id for the fragment
53 */
54 uint64_t id GNUNET_PACKED;
55
56};
57
58
59/**
60 * Defragmentation context.
61 */
62struct GNUNET_FRAGMENT_Context
63{
64};
65
66
67/**
68 * Fragment an over-sized message.
69 *
70 * @param msg the message to fragment
71 * @param mtu the maximum message size
72 * @param proc function to call for each fragment
73 * @param proc_cls closure for proc
74 */
75void
76GNUNET_FRAGMENT_fragment (const struct GNUNET_MessageHeader *msg,
77 uint16_t mtu,
78 GNUNET_FRAGMENT_MessageProcessor proc,
79 void *proc_cls)
80{
81 GNUNET_assert (0);
82}
83
84
85/**
86 * Create a defragmentation context.
87 *
88 * @param stats statistics context
89 * @param proc function to call with defragmented messages
90 * @param proc_cls closure for proc
91 * @return the defragmentation context
92 */
93struct GNUNET_FRAGMENT_Context *
94GNUNET_FRAGMENT_context_create (struct GNUNET_STATISTICS_Handle *stats,
95 GNUNET_FRAGMENT_MessageProcessor proc,
96 void *proc_cls)
97{
98 return NULL;
99}
100
101
102/**
103 * Destroy the given defragmentation context.
104 */
105void
106GNUNET_FRAGMENT_context_destroy (struct GNUNET_FRAGMENT_Context *ctx)
107{
108 GNUNET_assert (0);
109}
110
111
112/**
113 * We have received a fragment. Process it.
114 *
115 * @param ctx the context
116 * @param sender who transmitted the fragment
117 * @param msg the message that was received
118 */
119void
120GNUNET_FRAGMENT_process (struct GNUNET_FRAGMENT_Context *ctx,
121 const struct GNUNET_PeerIdentity *sender,
122 const struct GNUNET_MessageHeader *msg)
123{
124 GNUNET_assert (0);
125}
126
127
128
129#if 0
130
131/**
132 * How many buckets does the fragment hash table
133 * have?
134 */
135#define DEFRAG_BUCKET_COUNT 16
136
137/**
138 * After how long do fragments time out?
139 */
140#ifndef DEFRAGMENTATION_TIMEOUT
141#define DEFRAGMENTATION_TIMEOUT (3 * GNUNET_CRON_MINUTES)
142#endif
143
144/**
145 * Entry in the linked list of fragments.
146 */
147typedef struct FL
148{
149 struct FL *link;
150 P2P_fragmentation_MESSAGE *frag;
151} FL;
152
153/**
154 * Entry in the GNUNET_hash table of fragments.
155 */
156typedef struct FC
157{
158 struct FC *next;
159 FL *head;
160 GNUNET_PeerIdentity sender;
161 int id;
162 GNUNET_CronTime ttl;
163} FC;
164
165#define FRAGSIZE(fl) ((ntohs(fl->frag->header.size)-sizeof(P2P_fragmentation_MESSAGE)))
166
167static GNUNET_CoreAPIForPlugins *coreAPI;
168
169static GNUNET_Stats_ServiceAPI *stats;
170
171static int stat_defragmented;
172
173static int stat_fragmented;
174
175static int stat_discarded;
176
177/**
178 * Hashtable *with* collision management!
179 */
180static FC *defragmentationCache[DEFRAG_BUCKET_COUNT];
181
182/**
183 * Lock for the defragmentation cache.
184 */
185static struct GNUNET_Mutex *defragCacheLock;
186
187static void
188freeFL (FL * fl, int c)
189{
190 while (fl != NULL)
191 {
192 FL *link = fl->link;
193 if (stats != NULL)
194 stats->change (stat_discarded, c);
195 GNUNET_free (fl->frag);
196 GNUNET_free (fl);
197 fl = link;
198 }
199}
200
201/**
202 * This cron job ensures that we purge buffers of fragments
203 * that have timed out. It can run in much longer intervals
204 * than the defragmentationCron, e.g. every 60s.
205 * <p>
206 * This method goes through the hashtable, finds entries that
207 * have timed out and removes them (and all the fragments that
208 * belong to the entry). It's a bit more complicated as the
209 * collision list is also collapsed.
210 */
211static void
212defragmentationPurgeCron (void *unused)
213{
214 int i;
215 FC *smf;
216 FC *next;
217 FC *last;
218
219 GNUNET_mutex_lock (defragCacheLock);
220 for (i = 0; i < DEFRAG_BUCKET_COUNT; i++)
221 {
222 last = NULL;
223 smf = defragmentationCache[i];
224 while (smf != NULL)
225 {
226 if (smf->ttl < GNUNET_get_time ())
227 {
228 /* free linked list of fragments */
229 freeFL (smf->head, 1);
230 next = smf->next;
231 GNUNET_free (smf);
232 if (last == NULL)
233 defragmentationCache[i] = next;
234 else
235 last->next = next;
236 smf = next;
237 }
238 else
239 {
240 last = smf;
241 smf = smf->next;
242 }
243 } /* while smf != NULL */
244 } /* for all buckets */
245 GNUNET_mutex_unlock (defragCacheLock);
246}
247
248/**
249 * Check if this fragment-list is complete. If yes, put it together,
250 * process and free all buffers. Does not free the pep
251 * itself (but sets the TTL to 0 to have the cron free it
252 * in the next iteration).
253 *
254 * @param pep the entry in the GNUNET_hash table
255 */
256static void
257checkComplete (FC * pep)
258{
259 FL *pos;
260 unsigned short off;
261 unsigned short len;
262 char *msg;
263
264 GNUNET_GE_ASSERT (NULL, pep != NULL);
265 pos = pep->head;
266 if (pos == NULL)
267 return;
268 len = ntohs (pos->frag->len);
269 if (len == 0)
270 goto CLEANUP; /* really bad error! */
271 off = 0;
272 while ((pos != NULL) && (ntohs (pos->frag->off) <= off))
273 {
274 if (off >= off + FRAGSIZE (pos))
275 goto CLEANUP; /* error! */
276 if (ntohs (pos->frag->off) + FRAGSIZE (pos) > off)
277 off = ntohs (pos->frag->off) + FRAGSIZE (pos);
278 else
279 goto CLEANUP; /* error! */
280 pos = pos->link;
281 }
282 if (off < len)
283 return; /* some fragment is still missing */
284
285 msg = GNUNET_malloc (len);
286 pos = pep->head;
287 while (pos != NULL)
288 {
289 memcpy (&msg[ntohs (pos->frag->off)], &pos->frag[1], FRAGSIZE (pos));
290 pos = pos->link;
291 }
292 if (stats != NULL)
293 stats->change (stat_defragmented, 1);
294#if 0
295 printf ("Finished defragmentation!\n");
296#endif
297 /* handle message! */
298 coreAPI->loopback_send (&pep->sender, msg, len, GNUNET_YES, NULL);
299 GNUNET_free (msg);
300CLEANUP:
301 /* free fragment buffers */
302 freeFL (pep->head, 0);
303 pep->head = NULL;
304 pep->ttl = 0;
305}
306
307/**
308 * See if the new fragment is a part of this entry and join them if
309 * yes. Return GNUNET_SYSERR if the fragments do not match. Return GNUNET_OK if
310 * the fragments do match and the fragment has been processed. The
311 * defragCacheLock is already acquired by the caller whenever this
312 * method is called.<p>
313 *
314 * @param entry the entry in the cache
315 * @param pep the new entry
316 * @param packet the ip part in the new entry
317 */
318static int
319tryJoin (FC * entry,
320 const GNUNET_PeerIdentity * sender,
321 const P2P_fragmentation_MESSAGE * packet)
322{
323 /* frame before ours; may end in the middle of
324 our frame or before it starts; NULL if we are
325 the earliest position we have received so far */
326 FL *before;
327 /* frame after ours; may start in the middle of
328 our frame or after it; NULL if we are the last
329 fragment we have received so far */
330 FL *after;
331 /* current position in the frame-list */
332 FL *pos;
333 /* the new entry that we're inserting */
334 FL *pep;
335 FL *tmp;
336 unsigned short end;
337
338 GNUNET_GE_ASSERT (NULL, entry != NULL);
339 if (0 != memcmp (sender, &entry->sender, sizeof (GNUNET_PeerIdentity)))
340 return GNUNET_SYSERR; /* wrong fragment list, try another! */
341 if (ntohl (packet->id) != entry->id)
342 return GNUNET_SYSERR; /* wrong fragment list, try another! */
343#if 0
344 printf ("Received fragment %u from %u to %u\n",
345 ntohl (packet->id),
346 ntohs (packet->off),
347 ntohs (packet->off) + ntohs (packet->header.size) -
348 sizeof (P2P_fragmentation_MESSAGE));
349#endif
350 pos = entry->head;
351 if ((pos != NULL) && (packet->len != pos->frag->len))
352 return GNUNET_SYSERR; /* wrong fragment size */
353
354 before = NULL;
355 /* find the before-frame */
356 while ((pos != NULL) && (ntohs (pos->frag->off) < ntohs (packet->off)))
357 {
358 before = pos;
359 pos = pos->link;
360 }
361
362 /* find the after-frame */
363 end =
364 ntohs (packet->off) + ntohs (packet->header.size) -
365 sizeof (P2P_fragmentation_MESSAGE);
366 if (end <= ntohs (packet->off))
367 {
368 GNUNET_GE_LOG (NULL,
369 GNUNET_GE_DEVELOPER | GNUNET_GE_DEBUG | GNUNET_GE_BULK,
370 "Received invalid fragment at %s:%d\n", __FILE__,
371 __LINE__);
372 return GNUNET_SYSERR; /* yuck! integer overflow! */
373 }
374
375 if (before != NULL)
376 after = before;
377 else
378 after = entry->head;
379 while ((after != NULL) && (ntohs (after->frag->off) < end))
380 after = after->link;
381
382 if ((before != NULL) && (before == after))
383 {
384 /* this implies after or before != NULL and thereby the new
385 fragment is redundant as it is fully enclosed in an earlier
386 fragment */
387 if (stats != NULL)
388 stats->change (stat_defragmented, 1);
389 return GNUNET_OK; /* drop, there is a packet that spans our range! */
390 }
391
392 if ((before != NULL) &&
393 (after != NULL) &&
394 ((htons (before->frag->off) +
395 FRAGSIZE (before)) >= htons (after->frag->off)))
396 {
397 /* this implies that the fragment that starts before us and the
398 fragment that comes after this one leave no space in the middle
399 or even overlap; thus we can drop this redundant piece */
400 if (stats != NULL)
401 stats->change (stat_defragmented, 1);
402 return GNUNET_OK;
403 }
404
405 /* allocate pep */
406 pep = GNUNET_malloc (sizeof (FC));
407 pep->frag = GNUNET_malloc (ntohs (packet->header.size));
408 memcpy (pep->frag, packet, ntohs (packet->header.size));
409 pep->link = NULL;
410
411 if (before == NULL)
412 {
413 pep->link = after;
414 pos = entry->head;
415 while (pos != after)
416 {
417 tmp = pos->link;
418 GNUNET_free (pos->frag);
419 GNUNET_free (pos);
420 pos = tmp;
421 }
422 entry->head = pep;
423 goto FINISH;
424 /* end of insert first */
425 }
426
427 if (after == NULL)
428 {
429 /* insert last: find the end, free everything after it */
430 freeFL (before->link, 1);
431 before->link = pep;
432 goto FINISH;
433 }
434
435 /* ok, we are filling the middle between two fragments; insert. If
436 there is anything else in the middle, it can be dropped as we're
437 bigger & cover that area as well */
438 /* free everything between before and after */
439 pos = before->link;
440 while (pos != after)
441 {
442 tmp = pos->link;
443 GNUNET_free (pos->frag);
444 GNUNET_free (pos);
445 pos = tmp;
446 }
447 before->link = pep;
448 pep->link = after;
449
450FINISH:
451 entry->ttl = GNUNET_get_time () + DEFRAGMENTATION_TIMEOUT;
452 checkComplete (entry);
453 return GNUNET_OK;
454}
455
456/**
457 * Defragment the given fragment and pass to handler once
458 * defragmentation is complete.
459 *
460 * @param frag the packet to defragment
461 * @return GNUNET_SYSERR if the fragment is invalid
462 */
463static int
464processFragment (const GNUNET_PeerIdentity * sender,
465 const GNUNET_MessageHeader * frag)
466{
467 unsigned int hash;
468 FC *smf;
469
470 if (ntohs (frag->size) < sizeof (P2P_fragmentation_MESSAGE))
471 return GNUNET_SYSERR;
472
473 GNUNET_mutex_lock (defragCacheLock);
474 hash = sender->hashPubKey.bits[0] % DEFRAG_BUCKET_COUNT;
475 smf = defragmentationCache[hash];
476 while (smf != NULL)
477 {
478 if (GNUNET_OK ==
479 tryJoin (smf, sender, (P2P_fragmentation_MESSAGE *) frag))
480 {
481 GNUNET_mutex_unlock (defragCacheLock);
482 return GNUNET_OK;
483 }
484 if (0 == memcmp (sender, &smf->sender, sizeof (GNUNET_PeerIdentity)))
485 {
486 freeFL (smf->head, 1);
487 break;
488 }
489 smf = smf->next;
490 }
491 if (smf == NULL)
492 {
493 smf = GNUNET_malloc (sizeof (FC));
494 smf->next = defragmentationCache[hash];
495 defragmentationCache[hash] = smf;
496 smf->ttl = GNUNET_get_time () + DEFRAGMENTATION_TIMEOUT;
497 smf->sender = *sender;
498 }
499 smf->id = ntohl (((P2P_fragmentation_MESSAGE *) frag)->id);
500 smf->head = GNUNET_malloc (sizeof (FL));
501 smf->head->link = NULL;
502 smf->head->frag = GNUNET_malloc (ntohs (frag->size));
503 memcpy (smf->head->frag, frag, ntohs (frag->size));
504
505 GNUNET_mutex_unlock (defragCacheLock);
506 return GNUNET_OK;
507}
508
509typedef struct
510{
511 GNUNET_PeerIdentity sender;
512 /* maximums size of each fragment */
513 unsigned short mtu;
514 /** how long is this message part expected to be? */
515 unsigned short len;
516 /** when did we intend to transmit? */
517 GNUNET_CronTime transmissionTime;
518} FragmentBMC;
519
520/**
521 * Send a message that had to be fragmented (right now!). First grabs
522 * the first part of the message (obtained from ctx->se) and stores
523 * that in a P2P_fragmentation_MESSAGE envelope. The remaining fragments are
524 * added to the send queue with GNUNET_EXTREME_PRIORITY (to ensure that they
525 * will be transmitted next). The logic here is that if the priority
526 * for the first fragment was sufficiently high, the priority should
527 * also have been sufficiently high for all of the other fragments (at
528 * this time) since they have the same priority. And we want to make
529 * sure that we send all of them since just sending the first fragment
530 * and then going to other messages of equal priority would not be
531 * such a great idea (i.e. would just waste bandwidth).
532 */
533static int
534fragmentBMC (void *buf, void *cls, unsigned short len)
535{
536 FragmentBMC *ctx = cls;
537 static int idGen = 0;
538 P2P_fragmentation_MESSAGE *frag;
539 unsigned int pos;
540 int id;
541 unsigned short mlen;
542
543 if ((len < ctx->mtu) || (buf == NULL))
544 {
545 GNUNET_free (ctx);
546 return GNUNET_SYSERR;
547 }
548 if (stats != NULL)
549 stats->change (stat_fragmented, 1);
550 id = (idGen++) + GNUNET_random_u32 (GNUNET_RANDOM_QUALITY_WEAK, 512);
551 /* write first fragment to buf */
552 frag = (P2P_fragmentation_MESSAGE *) buf;
553 frag->header.size = htons (len);
554 frag->header.type = htons (GNUNET_P2P_PROTO_MESSAGE_FRAGMENT);
555 frag->id = id;
556 frag->off = htons (0);
557 frag->len = htons (ctx->len);
558 memcpy (&frag[1], &ctx[1], len - sizeof (P2P_fragmentation_MESSAGE));
559
560 /* create remaining fragments, add to queue! */
561 pos = len - sizeof (P2P_fragmentation_MESSAGE);
562 frag = GNUNET_malloc (ctx->mtu);
563 while (pos < ctx->len)
564 {
565 mlen = sizeof (P2P_fragmentation_MESSAGE) + ctx->len - pos;
566 if (mlen > ctx->mtu)
567 mlen = ctx->mtu;
568 GNUNET_GE_ASSERT (NULL, mlen > sizeof (P2P_fragmentation_MESSAGE));
569 frag->header.size = htons (mlen);
570 frag->header.type = htons (GNUNET_P2P_PROTO_MESSAGE_FRAGMENT);
571 frag->id = id;
572 frag->off = htons (pos);
573 frag->len = htons (ctx->len);
574 memcpy (&frag[1],
575 &((char *) (&ctx[1]))[pos],
576 mlen - sizeof (P2P_fragmentation_MESSAGE));
577 coreAPI->ciphertext_send (&ctx->sender,
578 &frag->header,
579 GNUNET_EXTREME_PRIORITY,
580 ctx->transmissionTime - GNUNET_get_time ());
581 pos += mlen - sizeof (P2P_fragmentation_MESSAGE);
582 }
583 GNUNET_GE_ASSERT (NULL, pos == ctx->len);
584 GNUNET_free (frag);
585 GNUNET_free (ctx);
586 return GNUNET_OK;
587}
588
589/**
590 * The given message must be fragmented. Produce a placeholder that
591 * corresponds to the first fragment. Once that fragment is scheduled
592 * for transmission, the placeholder should automatically add all of
593 * the other fragments (with very high priority).
594 */
595void
596fragment (const GNUNET_PeerIdentity * peer,
597 unsigned int mtu,
598 unsigned int prio,
599 unsigned int targetTime,
600 unsigned int len, GNUNET_BuildMessageCallback bmc, void *bmcClosure)
601{
602 FragmentBMC *fbmc;
603 int xlen;
604
605 GNUNET_GE_ASSERT (NULL, len > mtu);
606 GNUNET_GE_ASSERT (NULL, mtu > sizeof (P2P_fragmentation_MESSAGE));
607 fbmc = GNUNET_malloc (sizeof (FragmentBMC) + len);
608 fbmc->mtu = mtu;
609 fbmc->sender = *peer;
610 fbmc->transmissionTime = targetTime;
611 fbmc->len = len;
612 if (bmc == NULL)
613 {
614 memcpy (&fbmc[1], bmcClosure, len);
615 GNUNET_free (bmcClosure);
616 }
617 else
618 {
619 if (GNUNET_SYSERR == bmc (&fbmc[1], bmcClosure, len))
620 {
621 GNUNET_free (fbmc);
622 return;
623 }
624 }
625 xlen = mtu - sizeof (P2P_fragmentation_MESSAGE);
626 coreAPI->ciphertext_send_with_callback (peer, &fragmentBMC, fbmc, mtu, prio * xlen / len, /* compute new priority */
627 targetTime);
628}
629
630/**
631 * Initialize Fragmentation module.
632 */
633GNUNET_Fragmentation_ServiceAPI *
634provide_module_fragmentation (GNUNET_CoreAPIForPlugins * capi)
635{
636 static GNUNET_Fragmentation_ServiceAPI ret;
637 int i;
638
639 coreAPI = capi;
640 stats = coreAPI->service_request ("stats");
641 if (stats != NULL)
642 {
643 stat_defragmented =
644 stats->create (gettext_noop ("# messages defragmented"));
645 stat_fragmented =
646 stats->create (gettext_noop ("# messages fragmented"));
647 stat_discarded = stats->create (gettext_noop ("# fragments discarded"));
648 }
649 for (i = 0; i < DEFRAG_BUCKET_COUNT; i++)
650 defragmentationCache[i] = NULL;
651 defragCacheLock = GNUNET_mutex_create (GNUNET_NO);
652 GNUNET_cron_add_job (coreAPI->cron,
653 &defragmentationPurgeCron,
654 60 * GNUNET_CRON_SECONDS, 60 * GNUNET_CRON_SECONDS,
655 NULL);
656 GNUNET_GE_LOG (capi->ectx,
657 GNUNET_GE_INFO | GNUNET_GE_USER | GNUNET_GE_REQUEST,
658 _("`%s' registering handler %d\n"), "fragmentation",
659 GNUNET_P2P_PROTO_MESSAGE_FRAGMENT);
660 capi->p2p_ciphertext_handler_register (GNUNET_P2P_PROTO_MESSAGE_FRAGMENT,
661 &processFragment);
662
663 ret.fragment = &fragment;
664 return &ret;
665}
666
667/**
668 * Shutdown fragmentation.
669 */
670void
671release_module_fragmentation ()
672{
673 int i;
674
675 coreAPI->p2p_ciphertext_handler_unregister
676 (GNUNET_P2P_PROTO_MESSAGE_FRAGMENT, &processFragment);
677 GNUNET_cron_del_job (coreAPI->cron, &defragmentationPurgeCron,
678 60 * GNUNET_CRON_SECONDS, NULL);
679 for (i = 0; i < DEFRAG_BUCKET_COUNT; i++)
680 {
681 FC *pos = defragmentationCache[i];
682 while (pos != NULL)
683 {
684 FC *next = pos->next;
685 freeFL (pos->head, 1);
686 GNUNET_free (pos);
687 pos = next;
688 }
689 }
690 if (stats != NULL)
691 {
692 coreAPI->service_release (stats);
693 stats = NULL;
694 }
695 GNUNET_mutex_destroy (defragCacheLock);
696 defragCacheLock = NULL;
697 coreAPI = NULL;
698}
699
700#endif
701
702/* end of fragmentation.c */
diff --git a/src/fragmentation/test_fragmentation.c b/src/fragmentation/test_fragmentation.c
new file mode 100644
index 000000000..e6e3e5d22
--- /dev/null
+++ b/src/fragmentation/test_fragmentation.c
@@ -0,0 +1,439 @@
1/*
2 This file is part of GNUnet
3 (C) 2004, 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20/**
21 * @file fragmentation/test_fragmentation.c
22 * @brief test for fragmentation.c
23 * @author Christian Grothoff
24 */
25
26/**
27 * Testcase for defragmentation code.
28 * We have testcases for:
29 * - 2 fragments, aligned, [0,16),[16,32)
30 * - n (50) fragments, [i*16,(i+1)*16)
31 * - n (50) fragments, [0,i*16) + [50*16,51*16)
32 * - n (100) fragments, inserted in interleaved order (holes in sequence)
33 * - holes in sequence
34 * - other overlaps
35 * - timeouts
36 * - multiple entries in GNUNET_hash-list
37 * - id collisions in GNUNET_hash-list
38 */
39
40#include "platform.h"
41#include "gnunet_fragmentation_lib.h"
42
43#if 0
44
45/* -- to speed up the testcases -- */
46#define DEFRAGMENTATION_TIMEOUT (1 * GNUNET_CRON_SECONDS)
47
48
49static GNUNET_PeerIdentity mySender;
50static char *myMsg;
51static unsigned short myMsgLen;
52
53/* static buffers to avoid lots of malloc/free */
54static char masterBuffer[65536];
55static char resultBuffer[65536];
56
57static void
58handleHelper (const GNUNET_PeerIdentity * sender,
59 const char *msg,
60 const unsigned int len, int wasEncrypted, GNUNET_TSession * ts)
61{
62 GNUNET_GE_ASSERT (NULL,
63 0 == memcmp (sender, &mySender,
64 sizeof (GNUNET_PeerIdentity)));
65 myMsg = resultBuffer;
66 memcpy (resultBuffer, msg, len);
67 myMsgLen = len;
68}
69
70/**
71 * Wait long enough to force all fragments to timeout.
72 */
73static void
74makeTimeout ()
75{
76 GNUNET_thread_sleep (DEFRAGMENTATION_TIMEOUT * 2);
77 defragmentationPurgeCron (NULL);
78}
79
80/**
81 * Create a fragment. The data-portion will be filled
82 * with a sequence of numbers from start+id to start+len-1+id.
83 *
84 * @param pep pointer to the ethernet frame/buffer
85 * @param ip pointer to the ip-header
86 * @param start starting-offset
87 * @param length of the data portion
88 * @param id the identity of the fragment
89 */
90static GNUNET_MessageHeader *
91makeFragment (unsigned short start,
92 unsigned short size, unsigned short tot, int id)
93{
94 P2P_fragmentation_MESSAGE *frag;
95 int i;
96
97 frag = (P2P_fragmentation_MESSAGE *) masterBuffer;
98 frag->id = htonl (id);
99 frag->off = htons (start);
100 frag->len = htons (tot);
101 frag->header.size = htons (sizeof (P2P_fragmentation_MESSAGE) + size);
102
103 for (i = 0; i < size; i++)
104 ((char *) &frag[1])[i] = (char) i + id + start;
105 return &frag->header;
106}
107
108/**
109 * Check that the packet received is what we expected to
110 * get.
111 * @param id the expected id
112 * @param len the expected length
113 */
114static void
115checkPacket (int id, unsigned int len)
116{
117 int i;
118
119 GNUNET_GE_ASSERT (NULL, myMsg != NULL);
120 GNUNET_GE_ASSERT (NULL, myMsgLen == len);
121 for (i = 0; i < len; i++)
122 GNUNET_GE_ASSERT (NULL, myMsg[i] == (char) (i + id));
123 myMsgLen = 0;
124 myMsg = NULL;
125}
126
127
128/* **************** actual testcases ***************** */
129
130static void
131testSimpleFragment ()
132{
133 GNUNET_MessageHeader *pep;
134
135 pep = makeFragment (0, 16, 32, 42);
136 processFragment (&mySender, pep);
137 GNUNET_GE_ASSERT (NULL, myMsg == NULL);
138 pep = makeFragment (16, 16, 32, 42);
139 processFragment (&mySender, pep);
140 checkPacket (42, 32);
141}
142
143static void
144testSimpleFragmentTimeout ()
145{
146 GNUNET_MessageHeader *pep;
147
148 pep = makeFragment (0, 16, 32, 42);
149 processFragment (&mySender, pep);
150 GNUNET_GE_ASSERT (NULL, myMsg == NULL);
151 makeTimeout ();
152 pep = makeFragment (16, 16, 32, 42);
153 processFragment (&mySender, pep);
154 GNUNET_GE_ASSERT (NULL, myMsg == NULL);
155 pep = makeFragment (0, 16, 32, 42);
156 processFragment (&mySender, pep);
157 checkPacket (42, 32);
158}
159
160static void
161testSimpleFragmentReverse ()
162{
163 GNUNET_MessageHeader *pep;
164
165 pep = makeFragment (16, 16, 32, 42);
166 processFragment (&mySender, pep);
167 GNUNET_GE_ASSERT (NULL, myMsg == NULL);
168 pep = makeFragment (0, 16, 32, 42);
169 processFragment (&mySender, pep);
170 checkPacket (42, 32);
171}
172
173static void
174testManyFragments ()
175{
176 GNUNET_MessageHeader *pep;
177 int i;
178
179 for (i = 0; i < 50; i++)
180 {
181 pep = makeFragment (i * 16, 16, 51 * 16, 42);
182 processFragment (&mySender, pep);
183 GNUNET_GE_ASSERT (NULL, myMsg == NULL);
184 }
185 pep = makeFragment (50 * 16, 16, 51 * 16, 42);
186 processFragment (&mySender, pep);
187 checkPacket (42, 51 * 16);
188}
189
190static void
191testManyFragmentsMegaLarge ()
192{
193 GNUNET_MessageHeader *pep;
194 int i;
195
196 for (i = 0; i < 4000; i++)
197 {
198 pep = makeFragment (i * 16, 16, 4001 * 16, 42);
199 processFragment (&mySender, pep);
200 GNUNET_GE_ASSERT (NULL, myMsg == NULL);
201 }
202 pep = makeFragment (4000 * 16, 16, 4001 * 16, 42);
203 processFragment (&mySender, pep);
204 checkPacket (42, 4001 * 16);
205}
206
207static void
208testLastFragmentEarly ()
209{
210 GNUNET_MessageHeader *pep;
211 int i;
212
213 for (i = 0; i < 5; i++)
214 {
215 pep = makeFragment (i * 16, 8, 6 * 16 + 8, 42);
216 processFragment (&mySender, pep);
217 GNUNET_GE_ASSERT (NULL, myMsg == NULL);
218 }
219 pep = makeFragment (5 * 16, 24, 6 * 16 + 8, 42);
220 processFragment (&mySender, pep);
221 for (i = 0; i < 5; i++)
222 {
223 pep = makeFragment (i * 16 + 8, 8, 6 * 16 + 8, 42);
224 processFragment (&mySender, pep);
225 }
226 checkPacket (42, 6 * 16 + 8);
227}
228
229static void
230testManyInterleavedFragments ()
231{
232 GNUNET_MessageHeader *pep;
233 int i;
234
235 for (i = 0; i < 50; i++)
236 {
237 pep = makeFragment (i * 16, 8, 51 * 16 + 8, 42);
238 processFragment (&mySender, pep);
239 GNUNET_GE_ASSERT (NULL, myMsg == NULL);
240 }
241 for (i = 0; i < 50; i++)
242 {
243 pep = makeFragment (i * 16 + 8, 8, 51 * 16 + 8, 42);
244 processFragment (&mySender, pep);
245 GNUNET_GE_ASSERT (NULL, myMsg == NULL);
246 }
247 pep = makeFragment (50 * 16, 24, 51 * 16 + 8, 42);
248 processFragment (&mySender, pep);
249 checkPacket (42, 51 * 16 + 8);
250}
251
252static void
253testManyInterleavedOverlappingFragments ()
254{
255 GNUNET_MessageHeader *pep;
256 int i;
257
258 for (i = 0; i < 50; i++)
259 {
260 pep = makeFragment (i * 32, 16, 51 * 32, 42);
261 processFragment (&mySender, pep);
262 GNUNET_GE_ASSERT (NULL, myMsg == NULL);
263 }
264 for (i = 0; i < 50; i++)
265 {
266 pep = makeFragment (i * 32 + 8, 24, 51 * 32, 42);
267 processFragment (&mySender, pep);
268 GNUNET_GE_ASSERT (NULL, myMsg == NULL);
269 }
270 pep = makeFragment (50 * 32, 32, 51 * 32, 42);
271 processFragment (&mySender, pep);
272 checkPacket (42, 51 * 32);
273}
274
275static void
276testManyOverlappingFragments ()
277{
278 GNUNET_MessageHeader *pep;
279 int i;
280
281 for (i = 0; i < 50; i++)
282 {
283 pep = makeFragment (0, i * 16 + 16, 51 * 16, 42);
284 processFragment (&mySender, pep);
285 GNUNET_GE_ASSERT (NULL, myMsg == NULL);
286 }
287 pep = makeFragment (50 * 16, 16, 51 * 16, 42);
288 processFragment (&mySender, pep);
289 checkPacket (42, 51 * 16);
290}
291
292static void
293testManyOverlappingFragmentsTimeout ()
294{
295 GNUNET_MessageHeader *pep;
296 int i;
297
298 for (i = 0; i < 50; i++)
299 {
300 pep = makeFragment (0, i * 16 + 16, 51 * 16 + 8, 42);
301 processFragment (&mySender, pep);
302 GNUNET_GE_ASSERT (NULL, myMsg == NULL);
303 }
304 makeTimeout ();
305 pep = makeFragment (50 * 16, 24, 51 * 16 + 8, 42);
306 processFragment (&mySender, pep);
307 GNUNET_GE_ASSERT (NULL, myMsg == NULL);
308 for (i = 0; i < 50; i++)
309 {
310 pep = makeFragment (0, i * 16 + 16, 51 * 16 + 8, 42);
311 processFragment (&mySender, pep);
312 }
313 checkPacket (42, 51 * 16 + 8);
314}
315
316static void
317testManyFragmentsMultiId ()
318{
319 GNUNET_MessageHeader *pep;
320 int i;
321 int id;
322
323 for (i = 0; i < 50; i++)
324 {
325 for (id = 0; id < DEFRAG_BUCKET_COUNT; id++)
326 {
327 pep = makeFragment (i * 16, 16, 51 * 16, id + 5);
328 mySender.hashPubKey.bits[0] = id;
329 processFragment (&mySender, pep);
330 GNUNET_GE_ASSERT (NULL, myMsg == NULL);
331 }
332 }
333 for (id = 0; id < DEFRAG_BUCKET_COUNT; id++)
334 {
335 pep = makeFragment (50 * 16, 16, 51 * 16, id + 5);
336 mySender.hashPubKey.bits[0] = id;
337 processFragment (&mySender, pep);
338 checkPacket (id + 5, 51 * 16);
339 }
340}
341
342static void
343testManyFragmentsMultiIdCollisions ()
344{
345 GNUNET_MessageHeader *pep;
346 int i;
347 int id;
348
349 for (i = 0; i < 5; i++)
350 {
351 for (id = 0; id < DEFRAG_BUCKET_COUNT * 4; id++)
352 {
353 pep = makeFragment (i * 16, 16, 6 * 16, id + 5);
354 mySender.hashPubKey.bits[0] = id;
355 processFragment (&mySender, pep);
356 GNUNET_GE_ASSERT (NULL, myMsg == NULL);
357 }
358 }
359 for (id = 0; id < DEFRAG_BUCKET_COUNT * 4; id++)
360 {
361 pep = makeFragment (5 * 16, 16, 6 * 16, id + 5);
362 mySender.hashPubKey.bits[0] = id;
363 processFragment (&mySender, pep);
364 checkPacket (id + 5, 6 * 16);
365 }
366}
367
368/* ************* driver ****************** */
369
370static int
371p2p_register_handler (const unsigned short type,
372 GNUNET_P2PRequestHandler callback)
373{
374 return GNUNET_OK;
375}
376
377static int
378p2p_unregister_handler (const unsigned short type,
379 GNUNET_P2PRequestHandler callback)
380{
381 return GNUNET_OK;
382}
383
384
385static void *
386request_service (const char *name)
387{
388 return NULL;
389}
390
391#endif
392
393int
394main (int argc, char *argv[])
395{
396 fprintf (stderr, "WARNING: testcase not yet ported to new API.\n");
397#if 0
398 GNUNET_CoreAPIForPlugins capi;
399
400 memset (&capi, 0, sizeof (GNUNET_CoreAPIForPlugins));
401 capi.cron = GNUNET_cron_create (NULL);
402 capi.loopback_send = &handleHelper;
403 capi.service_request = &request_service;
404 capi.p2p_ciphertext_handler_register = &p2p_register_handler;
405 capi.p2p_ciphertext_handler_unregister = &p2p_unregister_handler;
406 provide_module_fragmentation (&capi);
407
408 fprintf (stderr, ".");
409 testSimpleFragment ();
410 fprintf (stderr, ".");
411 testSimpleFragmentTimeout ();
412 fprintf (stderr, ".");
413 testSimpleFragmentReverse ();
414 fprintf (stderr, ".");
415 testManyFragments ();
416 fprintf (stderr, ".");
417 testManyFragmentsMegaLarge ();
418 fprintf (stderr, ".");
419 testManyFragmentsMultiId ();
420 fprintf (stderr, ".");
421
422 testManyInterleavedFragments ();
423 fprintf (stderr, ".");
424 testManyInterleavedOverlappingFragments ();
425 fprintf (stderr, ".");
426 testManyOverlappingFragments ();
427 fprintf (stderr, ".");
428 testManyOverlappingFragmentsTimeout ();
429 fprintf (stderr, ".");
430 testLastFragmentEarly ();
431 fprintf (stderr, ".");
432 testManyFragmentsMultiIdCollisions ();
433 fprintf (stderr, ".");
434 release_module_fragmentation ();
435 fprintf (stderr, "\n");
436 GNUNET_cron_destroy (capi.cron);
437#endif
438 return 0; /* testcase passed */
439}