OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [rtos/] [freertos-6.1.1/] [Demo/] [Common/] [ethernet/] [lwIP_130/] [src/] [core/] [ipv4/] [ip_frag.c] - Blame information for rev 606

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 606 jeremybenn
/**
2
 * @file
3
 * This is the IPv4 packet segmentation and reassembly implementation.
4
 *
5
 */
6
 
7
/*
8
 * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
9
 * All rights reserved.
10
 *
11
 * Redistribution and use in source and binary forms, with or without modification,
12
 * are permitted provided that the following conditions are met:
13
 *
14
 * 1. Redistributions of source code must retain the above copyright notice,
15
 *    this list of conditions and the following disclaimer.
16
 * 2. Redistributions in binary form must reproduce the above copyright notice,
17
 *    this list of conditions and the following disclaimer in the documentation
18
 *    and/or other materials provided with the distribution.
19
 * 3. The name of the author may not be used to endorse or promote products
20
 *    derived from this software without specific prior written permission.
21
 *
22
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
23
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
24
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
25
 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
26
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
27
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
30
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
31
 * OF SUCH DAMAGE.
32
 *
33
 * This file is part of the lwIP TCP/IP stack.
34
 *
35
 * Author: Jani Monoses <jani@iv.ro>
36
 *         Simon Goldschmidt
37
 * original reassembly code by Adam Dunkels <adam@sics.se>
38
 *
39
 */
40
 
41
#include "lwip/opt.h"
42
#include "lwip/ip_frag.h"
43
#include "lwip/ip.h"
44
#include "lwip/inet.h"
45
#include "lwip/inet_chksum.h"
46
#include "lwip/netif.h"
47
#include "lwip/snmp.h"
48
#include "lwip/stats.h"
49
#include "lwip/icmp.h"
50
 
51
#include <string.h>
52
 
53
#if IP_REASSEMBLY
54
/**
55
 * The IP reassembly code currently has the following limitations:
56
 * - IP header options are not supported
57
 * - fragments must not overlap (e.g. due to different routes),
58
 *   currently, overlapping or duplicate fragments are thrown away
59
 *   if IP_REASS_CHECK_OVERLAP=1 (the default)!
60
 *
61
 * @todo: work with IP header options
62
 */
63
 
64
/** Setting this to 0, you can turn off checking the fragments for overlapping
65
 * regions. The code gets a little smaller. Only use this if you know that
66
 * overlapping won't occur on your network! */
67
#ifndef IP_REASS_CHECK_OVERLAP
68
#define IP_REASS_CHECK_OVERLAP 1
69
#endif /* IP_REASS_CHECK_OVERLAP */
70
 
71
/** Set to 0 to prevent freeing the oldest datagram when the reassembly buffer is
72
 * full (IP_REASS_MAX_PBUFS pbufs are enqueued). The code gets a little smaller.
73
 * Datagrams will be freed by timeout only. Especially useful when MEMP_NUM_REASSDATA
74
 * is set to 1, so one datagram can be reassembled at a time, only. */
75
#ifndef IP_REASS_FREE_OLDEST
76
#define IP_REASS_FREE_OLDEST 1
77
#endif /* IP_REASS_FREE_OLDEST */
78
 
79
#define IP_REASS_FLAG_LASTFRAG 0x01
80
 
81
/** This is a helper struct which holds the starting
82
 * offset and the ending offset of this fragment to
83
 * easily chain the fragments.
84
 */
85
struct ip_reass_helper {
86
  struct pbuf *next_pbuf;
87
  u16_t start;
88
  u16_t end;
89
};
90
 
91
#define IP_ADDRESSES_AND_ID_MATCH(iphdrA, iphdrB)  \
92
  (ip_addr_cmp(&(iphdrA)->src, &(iphdrB)->src) && \
93
   ip_addr_cmp(&(iphdrA)->dest, &(iphdrB)->dest) && \
94
   IPH_ID(iphdrA) == IPH_ID(iphdrB)) ? 1 : 0
95
 
96
/* global variables */
97
static struct ip_reassdata *reassdatagrams;
98
static u16_t ip_reass_pbufcount;
99
 
100
/* function prototypes */
101
static void ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev);
102
static int ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev);
103
 
104
/**
105
 * Reassembly timer base function
106
 * for both NO_SYS == 0 and 1 (!).
107
 *
108
 * Should be called every 1000 msec (defined by IP_TMR_INTERVAL).
109
 */
110
void
111
ip_reass_tmr(void)
112
{
113
  struct ip_reassdata *r, *prev = NULL;
114
 
115
  r = reassdatagrams;
116
  while (r != NULL) {
117
    /* Decrement the timer. Once it reaches 0,
118
     * clean up the incomplete fragment assembly */
119
    if (r->timer > 0) {
120
      r->timer--;
121
      LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_tmr: timer dec %"U16_F"\n",(u16_t)r->timer));
122
      prev = r;
123
      r = r->next;
124
    } else {
125
      /* reassembly timed out */
126
      struct ip_reassdata *tmp;
127
      LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_tmr: timer timed out\n"));
128
      tmp = r;
129
      /* get the next pointer before freeing */
130
      r = r->next;
131
      /* free the helper struct and all enqueued pbufs */
132
      ip_reass_free_complete_datagram(tmp, prev);
133
     }
134
   }
135
}
136
 
137
/**
138
 * Free a datagram (struct ip_reassdata) and all its pbufs.
139
 * Updates the total count of enqueued pbufs (ip_reass_pbufcount),
140
 * SNMP counters and sends an ICMP time exceeded packet.
141
 *
142
 * @param ipr datagram to free
143
 * @param prev the previous datagram in the linked list
144
 * @return the number of pbufs freed
145
 */
146
static int
147
ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev)
148
{
149
  int pbufs_freed = 0;
150
  struct pbuf *p;
151
  struct ip_reass_helper *iprh;
152
 
153
  LWIP_ASSERT("prev != ipr", prev != ipr);
154
  if (prev != NULL) {
155
    LWIP_ASSERT("prev->next == ipr", prev->next == ipr);
156
  }
157
 
158
  snmp_inc_ipreasmfails();
159
#if LWIP_ICMP
160
  iprh = (struct ip_reass_helper *)ipr->p->payload;
161
  if (iprh->start == 0) {
162
    /* The first fragment was received, send ICMP time exceeded. */
163
    /* First, de-queue the first pbuf from r->p. */
164
    p = ipr->p;
165
    ipr->p = iprh->next_pbuf;
166
    /* Then, copy the original header into it. */
167
    SMEMCPY(p->payload, &ipr->iphdr, IP_HLEN);
168
    icmp_time_exceeded(p, ICMP_TE_FRAG);
169
    pbufs_freed += pbuf_clen(p);
170
    pbuf_free(p);
171
  }
172
#endif /* LWIP_ICMP */
173
 
174
  /* First, free all received pbufs.  The individual pbufs need to be released
175
     separately as they have not yet been chained */
176
  p = ipr->p;
177
  while (p != NULL) {
178
    struct pbuf *pcur;
179
    iprh = (struct ip_reass_helper *)p->payload;
180
    pcur = p;
181
    /* get the next pointer before freeing */
182
    p = iprh->next_pbuf;
183
    pbufs_freed += pbuf_clen(pcur);
184
    pbuf_free(pcur);
185
  }
186
  /* Then, unchain the struct ip_reassdata from the list and free it. */
187
  ip_reass_dequeue_datagram(ipr, prev);
188
  LWIP_ASSERT("ip_reass_pbufcount >= clen", ip_reass_pbufcount >= pbufs_freed);
189
  ip_reass_pbufcount -= pbufs_freed;
190
 
191
  return pbufs_freed;
192
}
193
 
194
#if IP_REASS_FREE_OLDEST
195
/**
196
 * Free the oldest datagram to make room for enqueueing new fragments.
197
 * The datagram 'fraghdr' belongs to is not freed!
198
 *
199
 * @param fraghdr IP header of the current fragment
200
 * @param pbufs_needed number of pbufs needed to enqueue
201
 *        (used for freeing other datagrams if not enough space)
202
 * @return the number of pbufs freed
203
 */
204
static int
205
ip_reass_remove_oldest_datagram(struct ip_hdr *fraghdr, int pbufs_needed)
206
{
207
  /* @todo Can't we simply remove the last datagram in the
208
   *       linked list behind reassdatagrams?
209
   */
210
  struct ip_reassdata *r, *oldest, *prev;
211
  int pbufs_freed = 0, pbufs_freed_current;
212
  int other_datagrams;
213
 
214
  /* Free datagrams until being allowed to enqueue 'pbufs_needed' pbufs,
215
   * but don't free the datagram that 'fraghdr' belongs to! */
216
  do {
217
    oldest = NULL;
218
    prev = NULL;
219
    other_datagrams = 0;
220
    r = reassdatagrams;
221
    while (r != NULL) {
222
      if (!IP_ADDRESSES_AND_ID_MATCH(&r->iphdr, fraghdr)) {
223
        /* Not the same datagram as fraghdr */
224
        other_datagrams++;
225
        if (oldest == NULL) {
226
          oldest = r;
227
        } else if (r->timer <= oldest->timer) {
228
          /* older than the previous oldest */
229
          oldest = r;
230
        }
231
      }
232
      if (r->next != NULL) {
233
        prev = r;
234
      }
235
      r = r->next;
236
    }
237
    if (oldest != NULL) {
238
      pbufs_freed_current = ip_reass_free_complete_datagram(oldest, prev);
239
      pbufs_freed += pbufs_freed_current;
240
    }
241
  } while ((pbufs_freed < pbufs_needed) && (other_datagrams > 1));
242
  return pbufs_freed;
243
}
244
#endif /* IP_REASS_FREE_OLDEST */
245
 
246
/**
247
 * Enqueues a new fragment into the fragment queue
248
 * @param fraghdr points to the new fragments IP hdr
249
 * @param clen number of pbufs needed to enqueue (used for freeing other datagrams if not enough space)
250
 * @return A pointer to the queue location into which the fragment was enqueued
251
 */
252
static struct ip_reassdata*
253
ip_reass_enqueue_new_datagram(struct ip_hdr *fraghdr, int clen)
254
{
255
  struct ip_reassdata* ipr;
256
  /* No matching previous fragment found, allocate a new reassdata struct */
257
  ipr = memp_malloc(MEMP_REASSDATA);
258
  if (ipr == NULL) {
259
#if IP_REASS_FREE_OLDEST
260
    if (ip_reass_remove_oldest_datagram(fraghdr, clen) >= clen) {
261
      ipr = memp_malloc(MEMP_REASSDATA);
262
    }
263
    if (ipr == NULL)
264
#endif /* IP_REASS_FREE_OLDEST */
265
    {
266
      IPFRAG_STATS_INC(ip_frag.memerr);
267
      LWIP_DEBUGF(IP_REASS_DEBUG,("Failed to alloc reassdata struct\n"));
268
      return NULL;
269
    }
270
  }
271
  memset(ipr, 0, sizeof(struct ip_reassdata));
272
  ipr->timer = IP_REASS_MAXAGE;
273
 
274
  /* enqueue the new structure to the front of the list */
275
  ipr->next = reassdatagrams;
276
  reassdatagrams = ipr;
277
  /* copy the ip header for later tests and input */
278
  /* @todo: no ip options supported? */
279
  SMEMCPY(&(ipr->iphdr), fraghdr, IP_HLEN);
280
  return ipr;
281
}
282
 
283
/**
284
 * Dequeues a datagram from the datagram queue. Doesn't deallocate the pbufs.
285
 * @param ipr points to the queue entry to dequeue
286
 */
287
static void
288
ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev)
289
{
290
 
291
  /* dequeue the reass struct  */
292
  if (reassdatagrams == ipr) {
293
    /* it was the first in the list */
294
    reassdatagrams = ipr->next;
295
  } else {
296
    /* it wasn't the first, so it must have a valid 'prev' */
297
    LWIP_ASSERT("sanity check linked list", prev != NULL);
298
    prev->next = ipr->next;
299
  }
300
 
301
  /* now we can free the ip_reass struct */
302
  memp_free(MEMP_REASSDATA, ipr);
303
}
304
 
305
/**
306
 * Chain a new pbuf into the pbuf list that composes the datagram.  The pbuf list
307
 * will grow over time as  new pbufs are rx.
308
 * Also checks that the datagram passes basic continuity checks (if the last
309
 * fragment was received at least once).
310
 * @param root_p points to the 'root' pbuf for the current datagram being assembled.
311
 * @param new_p points to the pbuf for the current fragment
312
 * @return 0 if invalid, >0 otherwise
313
 */
314
static int
315
ip_reass_chain_frag_into_datagram_and_validate(struct ip_reassdata *ipr, struct pbuf *new_p)
316
{
317
  struct ip_reass_helper *iprh, *iprh_tmp, *iprh_prev=NULL;
318
  struct pbuf *q;
319
  u16_t offset,len;
320
  struct ip_hdr *fraghdr;
321
  int valid = 1;
322
 
323
  /* Extract length and fragment offset from current fragment */
324
  fraghdr = (struct ip_hdr*)new_p->payload;
325
  len = ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4;
326
  offset = (ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8;
327
 
328
  /* overwrite the fragment's ip header from the pbuf with our helper struct,
329
   * and setup the embedded helper structure. */
330
  /* make sure the struct ip_reass_helper fits into the IP header */
331
  LWIP_ASSERT("sizeof(struct ip_reass_helper) <= IP_HLEN",
332
              sizeof(struct ip_reass_helper) <= IP_HLEN);
333
  iprh = (struct ip_reass_helper*)new_p->payload;
334
  iprh->next_pbuf = NULL;
335
  iprh->start = offset;
336
  iprh->end = offset + len;
337
 
338
  /* Iterate through until we either get to the end of the list (append),
339
   * or we find on with a larger offset (insert). */
340
  for (q = ipr->p; q != NULL;) {
341
    iprh_tmp = (struct ip_reass_helper*)q->payload;
342
    if (iprh->start < iprh_tmp->start) {
343
      /* the new pbuf should be inserted before this */
344
      iprh->next_pbuf = q;
345
      if (iprh_prev != NULL) {
346
        /* not the fragment with the lowest offset */
347
#if IP_REASS_CHECK_OVERLAP
348
        if ((iprh->start < iprh_prev->end) || (iprh->end > iprh_tmp->start)) {
349
          /* fragment overlaps with previous or following, throw away */
350
          goto freepbuf;
351
        }
352
#endif /* IP_REASS_CHECK_OVERLAP */
353
        iprh_prev->next_pbuf = new_p;
354
      } else {
355
        /* fragment with the lowest offset */
356
        ipr->p = new_p;
357
      }
358
      break;
359
    } else if(iprh->start == iprh_tmp->start) {
360
      /* received the same datagram twice: no need to keep the datagram */
361
      goto freepbuf;
362
#if IP_REASS_CHECK_OVERLAP
363
    } else if(iprh->start < iprh_tmp->end) {
364
      /* overlap: no need to keep the new datagram */
365
      goto freepbuf;
366
#endif /* IP_REASS_CHECK_OVERLAP */
367
    } else {
368
      /* Check if the fragments received so far have no wholes. */
369
      if (iprh_prev != NULL) {
370
        if (iprh_prev->end != iprh_tmp->start) {
371
          /* There is a fragment missing between the current
372
           * and the previous fragment */
373
          valid = 0;
374
        }
375
      }
376
    }
377
    q = iprh_tmp->next_pbuf;
378
    iprh_prev = iprh_tmp;
379
  }
380
 
381
  /* If q is NULL, then we made it to the end of the list. Determine what to do now */
382
  if (q == NULL) {
383
    if (iprh_prev != NULL) {
384
      /* this is (for now), the fragment with the highest offset:
385
       * chain it to the last fragment */
386
#if IP_REASS_CHECK_OVERLAP
387
      LWIP_ASSERT("check fragments don't overlap", iprh_prev->end <= iprh->start);
388
#endif /* IP_REASS_CHECK_OVERLAP */
389
      iprh_prev->next_pbuf = new_p;
390
      if (iprh_prev->end != iprh->start) {
391
        valid = 0;
392
      }
393
    } else {
394
#if IP_REASS_CHECK_OVERLAP
395
      LWIP_ASSERT("no previous fragment, this must be the first fragment!",
396
        ipr->p == NULL);
397
#endif /* IP_REASS_CHECK_OVERLAP */
398
      /* this is the first fragment we ever received for this ip datagram */
399
      ipr->p = new_p;
400
    }
401
  }
402
 
403
  /* At this point, the validation part begins: */
404
  /* If we already received the last fragment */
405
  if ((ipr->flags & IP_REASS_FLAG_LASTFRAG) != 0) {
406
    /* and had no wholes so far */
407
    if (valid) {
408
      /* then check if the rest of the fragments is here */
409
      /* Check if the queue starts with the first datagram */
410
      if (((struct ip_reass_helper*)ipr->p->payload)->start != 0) {
411
        valid = 0;
412
      } else {
413
        /* and check that there are no wholes after this datagram */
414
        iprh_prev = iprh;
415
        q = iprh->next_pbuf;
416
        while (q != NULL) {
417
          iprh = (struct ip_reass_helper*)q->payload;
418
          if (iprh_prev->end != iprh->start) {
419
            valid = 0;
420
            break;
421
          }
422
          iprh_prev = iprh;
423
          q = iprh->next_pbuf;
424
        }
425
        /* if still valid, all fragments are received
426
         * (because to the MF==0 already arrived */
427
        if (valid) {
428
          LWIP_ASSERT("sanity check", ipr->p != NULL);
429
          LWIP_ASSERT("sanity check",
430
            ((struct ip_reass_helper*)ipr->p->payload) != iprh);
431
          LWIP_ASSERT("validate_datagram:next_pbuf!=NULL",
432
            iprh->next_pbuf == NULL);
433
          LWIP_ASSERT("validate_datagram:datagram end!=datagram len",
434
            iprh->end == ipr->datagram_len);
435
        }
436
      }
437
    }
438
    /* If valid is 0 here, there are some fragments missing in the middle
439
     * (since MF == 0 has already arrived). Such datagrams simply time out if
440
     * no more fragments are received... */
441
    return valid;
442
  }
443
  /* If we come here, not all fragments were received, yet! */
444
  return 0; /* not yet valid! */
445
#if IP_REASS_CHECK_OVERLAP
446
freepbuf:
447
  ip_reass_pbufcount -= pbuf_clen(new_p);
448
  pbuf_free(new_p);
449
  return 0;
450
#endif /* IP_REASS_CHECK_OVERLAP */
451
}
452
 
453
/**
454
 * Reassembles incoming IP fragments into an IP datagram.
455
 *
456
 * @param p points to a pbuf chain of the fragment
457
 * @return NULL if reassembly is incomplete, ? otherwise
458
 */
459
struct pbuf *
460
ip_reass(struct pbuf *p)
461
{
462
  struct pbuf *r;
463
  struct ip_hdr *fraghdr;
464
  struct ip_reassdata *ipr;
465
  struct ip_reass_helper *iprh;
466
  u16_t offset, len;
467
  u8_t clen;
468
  struct ip_reassdata *ipr_prev = NULL;
469
 
470
  IPFRAG_STATS_INC(ip_frag.recv);
471
  snmp_inc_ipreasmreqds();
472
 
473
  fraghdr = (struct ip_hdr*)p->payload;
474
 
475
  if ((IPH_HL(fraghdr) * 4) != IP_HLEN) {
476
    LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: IP options currently not supported!\n"));
477
    IPFRAG_STATS_INC(ip_frag.err);
478
    goto nullreturn;
479
  }
480
 
481
  offset = (ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8;
482
  len = ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4;
483
 
484
  /* Check if we are allowed to enqueue more datagrams. */
485
  clen = pbuf_clen(p);
486
  if ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS) {
487
#if IP_REASS_FREE_OLDEST
488
    if (!ip_reass_remove_oldest_datagram(fraghdr, clen) ||
489
        ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS))
490
#endif /* IP_REASS_FREE_OLDEST */
491
    {
492
      /* No datagram could be freed and still too many pbufs enqueued */
493
      LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: Overflow condition: pbufct=%d, clen=%d, MAX=%d\n",
494
        ip_reass_pbufcount, clen, IP_REASS_MAX_PBUFS));
495
      IPFRAG_STATS_INC(ip_frag.memerr);
496
      /* @todo: send ICMP time exceeded here? */
497
      /* drop this pbuf */
498
      goto nullreturn;
499
    }
500
  }
501
 
502
  /* Look for the datagram the fragment belongs to in the current datagram queue,
503
   * remembering the previous in the queue for later dequeueing. */
504
  for (ipr = reassdatagrams; ipr != NULL; ipr = ipr->next) {
505
    /* Check if the incoming fragment matches the one currently present
506
       in the reassembly buffer. If so, we proceed with copying the
507
       fragment into the buffer. */
508
    if (IP_ADDRESSES_AND_ID_MATCH(&ipr->iphdr, fraghdr)) {
509
      LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass: matching previous fragment ID=%"X16_F"\n",
510
        ntohs(IPH_ID(fraghdr))));
511
      IPFRAG_STATS_INC(ip_frag.cachehit);
512
      break;
513
    }
514
    ipr_prev = ipr;
515
  }
516
 
517
  if (ipr == NULL) {
518
  /* Enqueue a new datagram into the datagram queue */
519
    ipr = ip_reass_enqueue_new_datagram(fraghdr, clen);
520
    /* Bail if unable to enqueue */
521
    if(ipr == NULL) {
522
      goto nullreturn;
523
    }
524
  } else {
525
    if (((ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) == 0) &&
526
      ((ntohs(IPH_OFFSET(&ipr->iphdr)) & IP_OFFMASK) != 0)) {
527
      /* ipr->iphdr is not the header from the first fragment, but fraghdr is
528
       * -> copy fraghdr into ipr->iphdr since we want to have the header
529
       * of the first fragment (for ICMP time exceeded and later, for copying
530
       * all options, if supported)*/
531
      SMEMCPY(&ipr->iphdr, fraghdr, IP_HLEN);
532
    }
533
  }
534
  /* Track the current number of pbufs current 'in-flight', in order to limit
535
  the number of fragments that may be enqueued at any one time */
536
  ip_reass_pbufcount += clen;
537
 
538
  /* At this point, we have either created a new entry or pointing
539
   * to an existing one */
540
 
541
  /* check for 'no more fragments', and update queue entry*/
542
  if ((ntohs(IPH_OFFSET(fraghdr)) & IP_MF) == 0) {
543
    ipr->flags |= IP_REASS_FLAG_LASTFRAG;
544
    ipr->datagram_len = offset + len;
545
    LWIP_DEBUGF(IP_REASS_DEBUG,
546
     ("ip_reass: last fragment seen, total len %"S16_F"\n",
547
      ipr->datagram_len));
548
  }
549
  /* find the right place to insert this pbuf */
550
  /* @todo: trim pbufs if fragments are overlapping */
551
  if (ip_reass_chain_frag_into_datagram_and_validate(ipr, p)) {
552
    /* the totally last fragment (flag more fragments = 0) was received at least
553
     * once AND all fragments are received */
554
    ipr->datagram_len += IP_HLEN;
555
 
556
    /* save the second pbuf before copying the header over the pointer */
557
    r = ((struct ip_reass_helper*)ipr->p->payload)->next_pbuf;
558
 
559
    /* copy the original ip header back to the first pbuf */
560
    fraghdr = (struct ip_hdr*)(ipr->p->payload);
561
    SMEMCPY(fraghdr, &ipr->iphdr, IP_HLEN);
562
    IPH_LEN_SET(fraghdr, htons(ipr->datagram_len));
563
    IPH_OFFSET_SET(fraghdr, 0);
564
    IPH_CHKSUM_SET(fraghdr, 0);
565
    /* @todo: do we need to set calculate the correct checksum? */
566
    IPH_CHKSUM_SET(fraghdr, inet_chksum(fraghdr, IP_HLEN));
567
 
568
    p = ipr->p;
569
 
570
    /* chain together the pbufs contained within the reass_data list. */
571
    while(r != NULL) {
572
      iprh = (struct ip_reass_helper*)r->payload;
573
 
574
      /* hide the ip header for every succeding fragment */
575
      pbuf_header(r, -IP_HLEN);
576
      pbuf_cat(p, r);
577
      r = iprh->next_pbuf;
578
    }
579
    /* release the sources allocate for the fragment queue entry */
580
    ip_reass_dequeue_datagram(ipr, ipr_prev);
581
 
582
    /* and adjust the number of pbufs currently queued for reassembly. */
583
    ip_reass_pbufcount -= pbuf_clen(p);
584
 
585
    /* Return the pbuf chain */
586
    return p;
587
  }
588
  /* the datagram is not (yet?) reassembled completely */
589
  LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass_pbufcount: %d out\n", ip_reass_pbufcount));
590
  return NULL;
591
 
592
nullreturn:
593
  LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: nullreturn\n"));
594
  IPFRAG_STATS_INC(ip_frag.drop);
595
  pbuf_free(p);
596
  return NULL;
597
}
598
#endif /* IP_REASSEMBLY */
599
 
600
#if IP_FRAG
601
#if IP_FRAG_USES_STATIC_BUF
602
static u8_t buf[LWIP_MEM_ALIGN_SIZE(IP_FRAG_MAX_MTU)];
603
#endif /* IP_FRAG_USES_STATIC_BUF */
604
 
605
/**
606
 * Fragment an IP datagram if too large for the netif.
607
 *
608
 * Chop the datagram in MTU sized chunks and send them in order
609
 * by using a fixed size static memory buffer (PBUF_REF) or
610
 * point PBUF_REFs into p (depending on IP_FRAG_USES_STATIC_BUF).
611
 *
612
 * @param p ip packet to send
613
 * @param netif the netif on which to send
614
 * @param dest destination ip address to which to send
615
 *
616
 * @return ERR_OK if sent successfully, err_t otherwise
617
 */
618
err_t
619
ip_frag(struct pbuf *p, struct netif *netif, struct ip_addr *dest)
620
{
621
  struct pbuf *rambuf;
622
#if IP_FRAG_USES_STATIC_BUF
623
  struct pbuf *header;
624
#else
625
  struct pbuf *newpbuf;
626
  struct ip_hdr *original_iphdr;
627
#endif
628
  struct ip_hdr *iphdr;
629
  u16_t nfb;
630
  u16_t left, cop;
631
  u16_t mtu = netif->mtu;
632
  u16_t ofo, omf;
633
  u16_t last;
634
  u16_t poff = IP_HLEN;
635
  u16_t tmp;
636
#if !IP_FRAG_USES_STATIC_BUF
637
  u16_t newpbuflen = 0;
638
  u16_t left_to_copy;
639
#endif
640
 
641
  /* Get a RAM based MTU sized pbuf */
642
#if IP_FRAG_USES_STATIC_BUF
643
  /* When using a static buffer, we use a PBUF_REF, which we will
644
   * use to reference the packet (without link header).
645
   * Layer and length is irrelevant.
646
   */
647
  rambuf = pbuf_alloc(PBUF_LINK, 0, PBUF_REF);
648
  if (rambuf == NULL) {
649
    LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_frag: pbuf_alloc(PBUF_LINK, 0, PBUF_REF) failed\n"));
650
    return ERR_MEM;
651
  }
652
  rambuf->tot_len = rambuf->len = mtu;
653
  rambuf->payload = LWIP_MEM_ALIGN((void *)buf);
654
 
655
  /* Copy the IP header in it */
656
  iphdr = rambuf->payload;
657
  SMEMCPY(iphdr, p->payload, IP_HLEN);
658
#else /* IP_FRAG_USES_STATIC_BUF */
659
  original_iphdr = p->payload;
660
  iphdr = original_iphdr;
661
#endif /* IP_FRAG_USES_STATIC_BUF */
662
 
663
  /* Save original offset */
664
  tmp = ntohs(IPH_OFFSET(iphdr));
665
  ofo = tmp & IP_OFFMASK;
666
  omf = tmp & IP_MF;
667
 
668
  left = p->tot_len - IP_HLEN;
669
 
670
  nfb = (mtu - IP_HLEN) / 8;
671
 
672
  while (left) {
673
    last = (left <= mtu - IP_HLEN);
674
 
675
    /* Set new offset and MF flag */
676
    tmp = omf | (IP_OFFMASK & (ofo));
677
    if (!last)
678
      tmp = tmp | IP_MF;
679
 
680
    /* Fill this fragment */
681
    cop = last ? left : nfb * 8;
682
 
683
#if IP_FRAG_USES_STATIC_BUF
684
    poff += pbuf_copy_partial(p, (u8_t*)iphdr + IP_HLEN, cop, poff);
685
#else /* IP_FRAG_USES_STATIC_BUF */
686
    /* When not using a static buffer, create a chain of pbufs.
687
     * The first will be a PBUF_RAM holding the link and IP header.
688
     * The rest will be PBUF_REFs mirroring the pbuf chain to be fragged,
689
     * but limited to the size of an mtu.
690
     */
691
    rambuf = pbuf_alloc(PBUF_LINK, IP_HLEN, PBUF_RAM);
692
    if (rambuf == NULL) {
693
      return ERR_MEM;
694
    }
695
    LWIP_ASSERT("this needs a pbuf in one piece!",
696
                (p->len >= (IP_HLEN)));
697
    SMEMCPY(rambuf->payload, original_iphdr, IP_HLEN);
698
    iphdr = rambuf->payload;
699
 
700
    /* Can just adjust p directly for needed offset. */
701
    p->payload = (u8_t *)p->payload + poff;
702
    p->len -= poff;
703
 
704
    left_to_copy = cop;
705
    while (left_to_copy) {
706
      newpbuflen = (left_to_copy < p->len) ? left_to_copy : p->len;
707
      /* Is this pbuf already empty? */
708
      if (!newpbuflen) {
709
        p = p->next;
710
        continue;
711
      }
712
      newpbuf = pbuf_alloc(PBUF_RAW, 0, PBUF_REF);
713
      if (newpbuf == NULL) {
714
        pbuf_free(rambuf);
715
        return ERR_MEM;
716
      }
717
      /* Mirror this pbuf, although we might not need all of it. */
718
      newpbuf->payload = p->payload;
719
      newpbuf->len = newpbuf->tot_len = newpbuflen;
720
      /* Add it to end of rambuf's chain, but using pbuf_cat, not pbuf_chain
721
       * so that it is removed when pbuf_dechain is later called on rambuf.
722
       */
723
      pbuf_cat(rambuf, newpbuf);
724
      left_to_copy -= newpbuflen;
725
      if (left_to_copy)
726
        p = p->next;
727
    }
728
    poff = newpbuflen;
729
#endif /* IP_FRAG_USES_STATIC_BUF */
730
 
731
    /* Correct header */
732
    IPH_OFFSET_SET(iphdr, htons(tmp));
733
    IPH_LEN_SET(iphdr, htons(cop + IP_HLEN));
734
    IPH_CHKSUM_SET(iphdr, 0);
735
    IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN));
736
 
737
#if IP_FRAG_USES_STATIC_BUF
738
    if (last)
739
      pbuf_realloc(rambuf, left + IP_HLEN);
740
 
741
    /* This part is ugly: we alloc a RAM based pbuf for
742
     * the link level header for each chunk and then
743
     * free it.A PBUF_ROM style pbuf for which pbuf_header
744
     * worked would make things simpler.
745
     */
746
    header = pbuf_alloc(PBUF_LINK, 0, PBUF_RAM);
747
    if (header != NULL) {
748
      pbuf_chain(header, rambuf);
749
      netif->output(netif, header, dest);
750
      IPFRAG_STATS_INC(ip_frag.xmit);
751
      snmp_inc_ipfragcreates();
752
      pbuf_free(header);
753
    } else {
754
      LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_frag: pbuf_alloc() for header failed\n"));
755
      pbuf_free(rambuf);
756
      return ERR_MEM;
757
    }
758
#else /* IP_FRAG_USES_STATIC_BUF */
759
    /* No need for separate header pbuf - we allowed room for it in rambuf
760
     * when allocated.
761
     */
762
    netif->output(netif, rambuf, dest);
763
    IPFRAG_STATS_INC(ip_frag.xmit);
764
 
765
    /* Unfortunately we can't reuse rambuf - the hardware may still be
766
     * using the buffer. Instead we free it (and the ensuing chain) and
767
     * recreate it next time round the loop. If we're lucky the hardware
768
     * will have already sent the packet, the free will really free, and
769
     * there will be zero memory penalty.
770
     */
771
 
772
    pbuf_free(rambuf);
773
#endif /* IP_FRAG_USES_STATIC_BUF */
774
    left -= cop;
775
    ofo += nfb;
776
  }
777
#if IP_FRAG_USES_STATIC_BUF
778
  pbuf_free(rambuf);
779
#endif /* IP_FRAG_USES_STATIC_BUF */
780
  snmp_inc_ipfragoks();
781
  return ERR_OK;
782
}
783
#endif /* IP_FRAG */

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.