source: arduino-1-6-7/trunk/fuentes/arduino-ide-amd64/hardware/arduino/avr/firmwares/wifishield/wifiHD/src/SOFTWARE_FRAMEWORK/SERVICES/LWIP/lwip-1.3.2/src/core/ipv4/ip_frag.c @ 4837

Last change on this file since 4837 was 4837, checked in by daduve, 2 years ago

Adding new version

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