~ubuntu-branches/ubuntu/intrepid/gnunet/intrepid

« back to all changes in this revision

Viewing changes to src/applications/fragmentation/fragmentation.c

  • Committer: Bazaar Package Importer
  • Author(s): Arnaud Kyheng
  • Date: 2005-11-20 03:09:15 UTC
  • mfrom: (1.2.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20051120030915-4b3lwgjqtugsjhcy
Tags: 0.7.0a-1
* New upstream release.
* debian/po/fr.po: Updated french translation. Thanks to Eric (Closes: #336769).
* debian/control: Updated dependencies to follow the libstdc++ allocator change.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
     This file is part of GNUnet
 
3
     (C) 2004 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_protocols.h"
 
36
#include "gnunet_util.h"
 
37
#include "gnunet_core.h"
 
38
#include "gnunet_stats_service.h"
 
39
#include "gnunet_fragmentation_service.h"
 
40
 
 
41
/**
 
42
 * Message fragment.
 
43
 */
 
44
typedef struct {
 
45
  P2P_MESSAGE_HEADER header;
 
46
 
 
47
  /**
 
48
   * Fragment identity.
 
49
   */
 
50
  int id;
 
51
 
 
52
  /**
 
53
   * Fragment offset.
 
54
   */
 
55
  unsigned short off;
 
56
 
 
57
  /**
 
58
   * Total fragment size
 
59
   */
 
60
  unsigned short len;
 
61
 
 
62
} P2P_fragmentation_MESSAGE;
 
63
 
 
64
/**
 
65
 * How many buckets does the fragment hash table
 
66
 * have?
 
67
 */
 
68
#define DEFRAG_BUCKET_COUNT 16
 
69
 
 
70
/**
 
71
 * After how long do fragments time out?
 
72
 */
 
73
#ifndef DEFRAGMENTATION_TIMEOUT
 
74
#define DEFRAGMENTATION_TIMEOUT (3 * cronMINUTES)
 
75
#endif
 
76
 
 
77
/**
 
78
 * Entry in the linked list of fragments.
 
79
 */
 
80
typedef struct FL {
 
81
  struct FL * link;
 
82
  P2P_fragmentation_MESSAGE * frag;
 
83
} FL;
 
84
 
 
85
/**
 
86
 * Entry in the hash table of fragments.
 
87
 */
 
88
typedef struct FC {
 
89
  struct FC * next;
 
90
  FL * head;
 
91
  PeerIdentity sender;
 
92
  int id;
 
93
  cron_t ttl;
 
94
} FC;
 
95
 
 
96
#define FRAGSIZE(fl) ((ntohs(fl->frag->header.size)-sizeof(P2P_fragmentation_MESSAGE)))
 
97
 
 
98
static CoreAPIForApplication * coreAPI;
 
99
 
 
100
static Stats_ServiceAPI * stats;
 
101
 
 
102
static int stat_defragmented;
 
103
 
 
104
static int stat_fragmented;
 
105
 
 
106
static int stat_discarded;
 
107
 
 
108
/**
 
109
 * Hashtable *with* collision management!
 
110
 */
 
111
static FC * defragmentationCache[DEFRAG_BUCKET_COUNT];
 
112
 
 
113
/**
 
114
 * Lock for the defragmentation cache.
 
115
 */
 
116
static Mutex defragCacheLock;
 
117
 
 
118
static void freeFL(FL * fl,
 
119
                   int c) {
 
120
  while (fl != NULL) {
 
121
    FL * link = fl->link;
 
122
    if (stats != NULL)
 
123
      stats->change(stat_discarded, c);
 
124
    FREE(fl->frag);
 
125
    FREE(fl);
 
126
    fl = link;
 
127
  }
 
128
}
 
129
 
 
130
/**
 
131
 * This cron job ensures that we purge buffers of fragments
 
132
 * that have timed out.  It can run in much longer intervals
 
133
 * than the defragmentationCron, e.g. every 60s.
 
134
 * <p>
 
135
 * This method goes through the hashtable, finds entries that
 
136
 * have timed out and removes them (and all the fragments that
 
137
 * belong to the entry).  It's a bit more complicated as the
 
138
 * collision list is also collapsed.
 
139
 */
 
140
static void defragmentationPurgeCron() {
 
141
  int i;
 
142
  FC * smf;
 
143
  FC * next;
 
144
  FC * last;
 
145
 
 
146
  MUTEX_LOCK(&defragCacheLock);
 
147
  for (i=0;i<DEFRAG_BUCKET_COUNT;i++) {
 
148
    last = NULL;
 
149
    smf = defragmentationCache[i];
 
150
    while (smf != NULL) {
 
151
      if (smf->ttl < cronTime(NULL)) {
 
152
        /* free linked list of fragments */
 
153
        freeFL(smf->head, 1);
 
154
        next = smf->next;
 
155
        FREE(smf);      
 
156
        if (last == NULL)
 
157
          defragmentationCache[i] = next;
 
158
        else
 
159
          last->next = next;    
 
160
        smf = next;
 
161
      } else {
 
162
        last = smf;
 
163
        smf = smf->next;
 
164
      }
 
165
    } /* while smf != NULL */
 
166
  } /* for all buckets */
 
167
  MUTEX_UNLOCK(&defragCacheLock);
 
168
}
 
169
 
 
170
/**
 
171
 * Check if this fragment-list is complete.  If yes, put it together,
 
172
 * process and free all buffers.  Does not free the pep
 
173
 * itself (but sets the TTL to 0 to have the cron free it
 
174
 * in the next iteration).
 
175
 *
 
176
 * @param pep the entry in the hash table
 
177
 */
 
178
static void checkComplete(FC * pep) {
 
179
  FL * pos;
 
180
  unsigned short off;
 
181
  unsigned short len;
 
182
  char * msg;
 
183
 
 
184
  GNUNET_ASSERT(pep != NULL);
 
185
 
 
186
  pos = pep->head;
 
187
  if (pos == NULL)
 
188
    return;
 
189
  len = ntohs(pos->frag->len);
 
190
  if (len == 0)
 
191
    goto CLEANUP; /* really bad error! */
 
192
  off = 0;
 
193
  while ( (pos != NULL) &&
 
194
          (ntohs(pos->frag->off) <= off) ) {
 
195
    if (off >= off + FRAGSIZE(pos))
 
196
      goto CLEANUP; /* error! */
 
197
    if (ntohs(pos->frag->off) + FRAGSIZE(pos) > off)
 
198
      off = ntohs(pos->frag->off) + FRAGSIZE(pos);
 
199
    else
 
200
      goto CLEANUP; /* error! */
 
201
    pos = pos->link;
 
202
  }
 
203
  if (off < len)
 
204
    return; /* some fragment is still missing */
 
205
 
 
206
  msg = MALLOC(len);
 
207
  pos = pep->head;
 
208
  while (pos != NULL) {
 
209
    memcpy(&msg[ntohs(pos->frag->off)],
 
210
           &pos->frag[1],
 
211
           FRAGSIZE(pos));
 
212
    pos = pos->link;
 
213
  }
 
214
 
 
215
  if (stats != NULL)
 
216
    stats->change(stat_defragmented, 1);
 
217
  /* handle message! */
 
218
  coreAPI->injectMessage(&pep->sender,
 
219
                         msg,                   
 
220
                         len,
 
221
                         YES,
 
222
                         NULL);
 
223
  FREE(msg);
 
224
 CLEANUP:
 
225
  /* free fragment buffers */
 
226
  freeFL(pep->head, 0);
 
227
  pep->head = NULL;
 
228
  pep->ttl = 0;
 
229
}
 
230
 
 
231
/**
 
232
 * See if the new fragment is a part of this entry and join them if
 
233
 * yes.  Return SYSERR if the fragments do not match.  Return OK if
 
234
 * the fragments do match and the fragment has been processed.  The
 
235
 * defragCacheLock is already aquired by the caller whenever this
 
236
 * method is called.<p>
 
237
 *
 
238
 * @param entry the entry in the cache
 
239
 * @param pep the new entry
 
240
 * @param packet the ip part in the new entry
 
241
 */
 
242
static int tryJoin(FC * entry,
 
243
                   const PeerIdentity * sender,
 
244
                   const P2P_fragmentation_MESSAGE * packet) {
 
245
  /* frame before ours; may end in the middle of
 
246
     our frame or before it starts; NULL if we are
 
247
     the earliest position we have received so far */
 
248
  FL * before;
 
249
  /* frame after ours; may start in the middle of
 
250
     our frame or after it; NULL if we are the last
 
251
     fragment we have received so far */
 
252
  FL * after;
 
253
  /* current position in the frame-list */
 
254
  FL * pos;
 
255
  /* the new entry that we're inserting */
 
256
  FL * pep;
 
257
  FL * tmp;
 
258
  unsigned short end;
 
259
 
 
260
  GNUNET_ASSERT(entry != NULL);
 
261
  if (! hostIdentityEquals(sender,
 
262
                           &entry->sender))
 
263
    return SYSERR; /* wrong fragment list, try another! */
 
264
  if (ntohl(packet->id) != entry->id)
 
265
    return SYSERR; /* wrong fragment list, try another! */
 
266
 
 
267
  pos = entry->head;
 
268
  if ( (pos != NULL) &&
 
269
       (packet->len != pos->frag->len) )
 
270
    return SYSERR; /* wrong fragment size */
 
271
 
 
272
  before = NULL;
 
273
  /* find the before-frame */
 
274
  while ( (pos != NULL) &&
 
275
          (ntohs(pos->frag->off) <
 
276
           ntohs(packet->off)) ) {
 
277
    before = pos;
 
278
    pos = pos->link;
 
279
  }
 
280
 
 
281
  /* find the after-frame */
 
282
  end = ntohs(packet->off) + ntohs(packet->header.size) - sizeof(P2P_fragmentation_MESSAGE);
 
283
  if (end <= ntohs(packet->off)) {
 
284
    LOG(LOG_DEBUG,
 
285
        "Received invalid fragment at %s:%d\n",
 
286
        __FILE__, __LINE__);
 
287
    return SYSERR; /* yuck! integer overflow! */
 
288
  }
 
289
 
 
290
  if (before != NULL)
 
291
    after = before;
 
292
  else
 
293
    after = entry->head;
 
294
  while ( (after != NULL) &&
 
295
          (ntohs(after->frag->off)<end) )
 
296
    after = after->link;
 
297
 
 
298
  if ( (before != NULL) &&
 
299
       (before == after) ) {
 
300
    /* this implies after or before != NULL and thereby the new
 
301
       fragment is redundant as it is fully enclosed in an earlier
 
302
       fragment */
 
303
    if (stats != NULL)
 
304
      stats->change(stat_defragmented, 1);
 
305
    return OK; /* drop, there is a packet that spans our range! */
 
306
  }
 
307
 
 
308
  if ( (before != NULL) &&
 
309
       (after != NULL) &&
 
310
       ( (htons(before->frag->off) +
 
311
          FRAGSIZE(before))
 
312
         >= htons(after->frag->off)) ) {
 
313
    /* this implies that the fragment that starts before us and the
 
314
       fragment that comes after this one leave no space in the middle
 
315
       or even overlap; thus we can drop this redundant piece */
 
316
    if (stats != NULL)
 
317
      stats->change(stat_defragmented, 1);
 
318
    return OK;
 
319
  }
 
320
 
 
321
  /* allocate pep */
 
322
  pep = MALLOC(sizeof(FC));
 
323
  pep->frag = MALLOC(ntohs(packet->header.size));
 
324
  memcpy(pep->frag,
 
325
         packet,
 
326
         ntohs(packet->header.size));
 
327
  pep->link = NULL;
 
328
 
 
329
  if (before == NULL) {
 
330
    pep->link = after;
 
331
    pos = entry->head;
 
332
    while (pos != after) {
 
333
      tmp = pos->link;
 
334
      FREE(pos->frag);
 
335
      FREE(pos);
 
336
      pos = tmp;
 
337
    }
 
338
    entry->head = pep;
 
339
    goto FINISH;
 
340
    /* end of insert first */
 
341
  }
 
342
 
 
343
  if (after == NULL) {
 
344
    /* insert last: find the end, free everything after it */
 
345
    freeFL(before->link, 1);
 
346
    before->link = pep;
 
347
    goto FINISH;
 
348
  }
 
349
 
 
350
  /* ok, we are filling the middle between two fragments; insert.  If
 
351
     there is anything else in the middle, it can be dropped as we're
 
352
     bigger & cover that area as well */
 
353
  /* free everything between before and after */
 
354
  pos = before->link;
 
355
  while (pos != after) {
 
356
    tmp = pos->link;
 
357
    FREE(pos->frag);
 
358
    FREE(pos);
 
359
    pos = tmp;
 
360
  }
 
361
  before->link = pep;
 
362
  pep->link = after;
 
363
 
 
364
 FINISH:
 
365
  entry->ttl = cronTime(NULL) + DEFRAGMENTATION_TIMEOUT;
 
366
  checkComplete(entry);
 
367
  return OK;
 
368
}
 
369
 
 
370
/**
 
371
 * Defragment the given fragment and pass to handler once
 
372
 * defragmentation is complete.
 
373
 *
 
374
 * @param frag the packet to defragment
 
375
 * @return SYSERR if the fragment is invalid
 
376
 */
 
377
static int processFragment(const PeerIdentity * sender,
 
378
                           const P2P_MESSAGE_HEADER * frag) {
 
379
  unsigned int hash;
 
380
  FC * smf;
 
381
 
 
382
  if (ntohs(frag->size) < sizeof(P2P_fragmentation_MESSAGE))
 
383
    return SYSERR;
 
384
 
 
385
  MUTEX_LOCK(&defragCacheLock);
 
386
  hash = sender->hashPubKey.bits[0] % DEFRAG_BUCKET_COUNT;
 
387
  smf = defragmentationCache[hash];
 
388
  while (smf != NULL) {
 
389
    if (OK == tryJoin(smf,
 
390
                      sender,
 
391
                      (P2P_fragmentation_MESSAGE*) frag)) {
 
392
      MUTEX_UNLOCK(&defragCacheLock);
 
393
      return OK;
 
394
    }
 
395
    if (hostIdentityEquals(sender,
 
396
                           &smf->sender)) {
 
397
      freeFL(smf->head, 1);
 
398
      break;
 
399
    }
 
400
    smf = smf->next;
 
401
  }
 
402
  if (smf == NULL) {
 
403
    smf = MALLOC(sizeof(FC));
 
404
    smf->next = defragmentationCache[hash];
 
405
    defragmentationCache[hash] = smf;
 
406
    smf->ttl = cronTime(NULL) + DEFRAGMENTATION_TIMEOUT;
 
407
    smf->sender = *sender;
 
408
  }
 
409
  smf->id = ntohl(((P2P_fragmentation_MESSAGE*)frag)->id);
 
410
  smf->head = MALLOC(sizeof(FL));
 
411
  smf->head->link = NULL;
 
412
  smf->head->frag = MALLOC(ntohs(frag->size));
 
413
  memcpy(smf->head->frag,
 
414
         frag,
 
415
         ntohs(frag->size));
 
416
 
 
417
  MUTEX_UNLOCK(&defragCacheLock);
 
418
  return OK;
 
419
}
 
420
 
 
421
typedef struct {
 
422
  PeerIdentity sender;
 
423
  /* maximums size of each fragment */
 
424
  unsigned short mtu;
 
425
  /** how long is this message part expected to be? */
 
426
  unsigned short len;
 
427
  /** when did we intend to transmit? */
 
428
  cron_t transmissionTime;
 
429
} FragmentBMC;
 
430
 
 
431
/**
 
432
 * Send a message that had to be fragmented (right now!).  First grabs
 
433
 * the first part of the message (obtained from ctx->se) and stores
 
434
 * that in a P2P_fragmentation_MESSAGE envelope.  The remaining fragments are
 
435
 * added to the send queue with EXTREME_PRIORITY (to ensure that they
 
436
 * will be transmitted next).  The logic here is that if the priority
 
437
 * for the first fragment was sufficiently high, the priority should
 
438
 * also have been sufficiently high for all of the other fragments (at
 
439
 * this time) since they have the same priority.  And we want to make
 
440
 * sure that we send all of them since just sending the first fragment
 
441
 * and then going to other messages of equal priority would not be
 
442
 * such a great idea (i.e. would just waste bandwidth).
 
443
 */
 
444
static int fragmentBMC(void * buf,
 
445
                       FragmentBMC * ctx,
 
446
                       unsigned short len) {
 
447
  static int idGen = 0;
 
448
  P2P_fragmentation_MESSAGE * frag;
 
449
  unsigned int pos;
 
450
  int id;
 
451
  unsigned short mlen;
 
452
 
 
453
  if ( (len < ctx->mtu) ||
 
454
       (buf == NULL) ) {
 
455
    FREE(ctx);
 
456
    return SYSERR;
 
457
  }
 
458
  if (stats != NULL)
 
459
    stats->change(stat_fragmented, 1);
 
460
  id = (idGen++) + randomi(512);
 
461
  /* write first fragment to buf */
 
462
  frag = (P2P_fragmentation_MESSAGE*) buf;
 
463
  frag->header.size = htons(len);
 
464
  frag->header.type = htons(P2P_PROTO_fragment);
 
465
  frag->id = id;
 
466
  frag->off = htons(0);
 
467
  frag->len = htons(ctx->len);
 
468
  memcpy(&frag[1],
 
469
         &ctx[1],
 
470
         len - sizeof(P2P_fragmentation_MESSAGE));
 
471
 
 
472
  /* create remaining fragments, add to queue! */
 
473
  pos = len - sizeof(P2P_fragmentation_MESSAGE);
 
474
  frag = MALLOC(ctx->mtu);
 
475
  while (pos < ctx->len) {
 
476
    mlen = sizeof(P2P_fragmentation_MESSAGE) + ctx->len - pos;
 
477
    if (mlen > ctx->mtu)
 
478
      mlen = ctx->mtu;
 
479
    GNUNET_ASSERT(mlen > sizeof(P2P_fragmentation_MESSAGE));
 
480
    frag->header.size = htons(mlen);
 
481
    frag->header.type = htons(P2P_PROTO_fragment);
 
482
    frag->id = id;
 
483
    frag->off = htons(pos);
 
484
    frag->len = htons(ctx->len);
 
485
    memcpy(&frag[1],
 
486
           &((char*)(&ctx[1]))[pos],
 
487
           mlen - sizeof(P2P_fragmentation_MESSAGE));
 
488
    coreAPI->unicast(&ctx->sender,
 
489
                     &frag->header,
 
490
                     EXTREME_PRIORITY,
 
491
                     ctx->transmissionTime - cronTime(NULL));
 
492
    pos += mlen - sizeof(P2P_fragmentation_MESSAGE);
 
493
  }
 
494
  GNUNET_ASSERT(pos == ctx->len);
 
495
  FREE(frag);
 
496
  FREE(ctx);
 
497
  return OK;
 
498
}
 
499
 
 
500
/**
 
501
 * The given message must be fragmented.  Produce a placeholder that
 
502
 * corresponds to the first fragment.  Once that fragment is scheduled
 
503
 * for transmission, the placeholder should automatically add all of
 
504
 * the other fragments (with very high priority).
 
505
 */
 
506
void fragment(const PeerIdentity * peer,
 
507
              unsigned int mtu,
 
508
              unsigned int prio,
 
509
              unsigned int targetTime,
 
510
              unsigned int len,
 
511
              BuildMessageCallback bmc,
 
512
              void * bmcClosure) {
 
513
  FragmentBMC * fbmc;
 
514
  int xlen;
 
515
 
 
516
  GNUNET_ASSERT(len > mtu);
 
517
  GNUNET_ASSERT(mtu > sizeof(P2P_fragmentation_MESSAGE));
 
518
  fbmc = MALLOC(sizeof(FragmentBMC) + len);
 
519
  fbmc->mtu = mtu;
 
520
  fbmc->sender = *peer;
 
521
  fbmc->transmissionTime = targetTime;
 
522
  fbmc->len = len;
 
523
  if (bmc == NULL) {
 
524
    memcpy(&fbmc[1],
 
525
           bmcClosure,
 
526
           len);
 
527
    FREE(bmcClosure);
 
528
  } else {
 
529
    if (SYSERR == bmc(&fbmc[1],
 
530
                      bmcClosure,
 
531
                      len)) {
 
532
      FREE(fbmc);
 
533
      return;
 
534
    }
 
535
  }
 
536
  xlen = mtu - sizeof(P2P_fragmentation_MESSAGE);
 
537
  coreAPI->unicastCallback(peer,
 
538
                           (BuildMessageCallback) &fragmentBMC,
 
539
                           fbmc,
 
540
                           mtu,
 
541
                           prio * xlen / len, /* compute new prio */
 
542
                           targetTime);
 
543
}
 
544
 
 
545
/**
 
546
 * Initialize Fragmentation module.
 
547
 */
 
548
Fragmentation_ServiceAPI *
 
549
provide_module_fragmentation(CoreAPIForApplication * capi) {
 
550
  static Fragmentation_ServiceAPI ret;
 
551
  int i;
 
552
 
 
553
  coreAPI = capi;
 
554
  stats = coreAPI->requestService("stats");
 
555
  if (stats != NULL) {
 
556
    stat_defragmented = stats->create(gettext_noop("# messages defragmented"));
 
557
    stat_fragmented = stats->create(gettext_noop("# messages fragmented"));
 
558
    stat_discarded = stats->create(gettext_noop("# fragments discarded"));
 
559
  }
 
560
  for (i=0;i<DEFRAG_BUCKET_COUNT;i++)
 
561
    defragmentationCache[i] = NULL;
 
562
  MUTEX_CREATE(&defragCacheLock);
 
563
  addCronJob((CronJob) &defragmentationPurgeCron,
 
564
             60 * cronSECONDS,
 
565
             60 * cronSECONDS,
 
566
             NULL);
 
567
  LOG(LOG_DEBUG,
 
568
      _("`%s' registering handler %d\n"),
 
569
      "fragmentation",
 
570
      P2P_PROTO_fragment);
 
571
  capi->registerHandler(P2P_PROTO_fragment,
 
572
                        &processFragment);
 
573
 
 
574
  ret.fragment = &fragment;
 
575
  return &ret;
 
576
}
 
577
 
 
578
/**
 
579
 * Shutdown fragmentation.
 
580
 */
 
581
void release_module_fragmentation() {
 
582
  int i;
 
583
 
 
584
  coreAPI->unregisterHandler(P2P_PROTO_fragment,
 
585
                             &processFragment);
 
586
  delCronJob((CronJob) &defragmentationPurgeCron,
 
587
             60 * cronSECONDS,
 
588
             NULL);
 
589
  for (i=0;i<DEFRAG_BUCKET_COUNT;i++) {
 
590
    FC * pos = defragmentationCache[i];
 
591
    while (pos != NULL) {
 
592
      FC * next = pos->next;
 
593
      freeFL(pos->head, 1);
 
594
      FREE(pos);
 
595
      pos = next;
 
596
    }
 
597
  }
 
598
  if (stats != NULL) {
 
599
    coreAPI->releaseService(stats);
 
600
    stats = NULL;
 
601
  }
 
602
  MUTEX_DESTROY(&defragCacheLock);
 
603
  coreAPI = NULL;
 
604
}
 
605
 
 
606
/* end of fragmentation.c */