Bonjour/Zerconf library

Dependencies:   mbed

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers ip_frag.c Source File

ip_frag.c

Go to the documentation of this file.
00001 /**
00002  * @file
00003  * This is the IPv4 packet segmentation and reassembly implementation.
00004  *
00005  */
00006 
00007 /*
00008  * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
00009  * All rights reserved. 
00010  * 
00011  * Redistribution and use in source and binary forms, with or without modification, 
00012  * are permitted provided that the following conditions are met:
00013  *
00014  * 1. Redistributions of source code must retain the above copyright notice,
00015  *    this list of conditions and the following disclaimer.
00016  * 2. Redistributions in binary form must reproduce the above copyright notice,
00017  *    this list of conditions and the following disclaimer in the documentation
00018  *    and/or other materials provided with the distribution.
00019  * 3. The name of the author may not be used to endorse or promote products
00020  *    derived from this software without specific prior written permission. 
00021  *
00022  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 
00023  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
00024  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
00025  * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
00026  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
00027  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
00028  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
00029  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
00030  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
00031  * OF SUCH DAMAGE.
00032  *
00033  * This file is part of the lwIP TCP/IP stack.
00034  * 
00035  * Author: Jani Monoses <jani@iv.ro> 
00036  *         Simon Goldschmidt
00037  * original reassembly code by Adam Dunkels <adam@sics.se>
00038  * 
00039  */
00040 
00041 #include "lwip/opt.h"
00042 #include "lwip/ip_frag.h"
00043 #include "lwip/def.h"
00044 #include "lwip/inet_chksum.h"
00045 #include "lwip/netif.h"
00046 #include "lwip/snmp.h"
00047 #include "lwip/stats.h"
00048 #include "lwip/icmp.h"
00049 
00050 #include <string.h>
00051 
00052 #if IP_REASSEMBLY
00053 /**
00054  * The IP reassembly code currently has the following limitations:
00055  * - IP header options are not supported
00056  * - fragments must not overlap (e.g. due to different routes),
00057  *   currently, overlapping or duplicate fragments are thrown away
00058  *   if IP_REASS_CHECK_OVERLAP=1 (the default)!
00059  *
00060  * @todo: work with IP header options
00061  */
00062 
00063 /** Setting this to 0, you can turn off checking the fragments for overlapping
00064  * regions. The code gets a little smaller. Only use this if you know that
00065  * overlapping won't occur on your network! */
00066 #ifndef IP_REASS_CHECK_OVERLAP
00067 #define IP_REASS_CHECK_OVERLAP 1
00068 #endif /* IP_REASS_CHECK_OVERLAP */
00069 
00070 /** Set to 0 to prevent freeing the oldest datagram when the reassembly buffer is
00071  * full (IP_REASS_MAX_PBUFS pbufs are enqueued). The code gets a little smaller.
00072  * Datagrams will be freed by timeout only. Especially useful when MEMP_NUM_REASSDATA
00073  * is set to 1, so one datagram can be reassembled at a time, only. */
00074 #ifndef IP_REASS_FREE_OLDEST
00075 #define IP_REASS_FREE_OLDEST 1
00076 #endif /* IP_REASS_FREE_OLDEST */
00077 
00078 #define IP_REASS_FLAG_LASTFRAG 0x01
00079 
00080 /** This is a helper struct which holds the starting
00081  * offset and the ending offset of this fragment to
00082  * easily chain the fragments.
00083  * It has to be packed since it has to fit inside the IP header.
00084  */
00085 #ifdef PACK_STRUCT_USE_INCLUDES
00086 #  include "arch/bpstruct.h"
00087 #endif
00088 PACK_STRUCT_BEGIN
00089 struct ip_reass_helper {
00090   PACK_STRUCT_FIELD(struct pbuf *next_pbuf);
00091   PACK_STRUCT_FIELD(u16_t start);
00092   PACK_STRUCT_FIELD(u16_t end);
00093 } PACK_STRUCT_STRUCT;
00094 PACK_STRUCT_END
00095 #ifdef PACK_STRUCT_USE_INCLUDES
00096 #  include "arch/epstruct.h"
00097 #endif
00098 
00099 #define IP_ADDRESSES_AND_ID_MATCH(iphdrA, iphdrB)  \
00100   (ip_addr_cmp(&(iphdrA)->src, &(iphdrB)->src) && \
00101    ip_addr_cmp(&(iphdrA)->dest, &(iphdrB)->dest) && \
00102    IPH_ID(iphdrA) == IPH_ID(iphdrB)) ? 1 : 0
00103 
00104 /* global variables */
00105 static struct ip_reassdata *reassdatagrams;
00106 static u16_t ip_reass_pbufcount;
00107 
00108 /* function prototypes */
00109 static void ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev);
00110 static int ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev);
00111 
00112 /**
00113  * Reassembly timer base function
00114  * for both NO_SYS == 0 and 1 (!).
00115  *
00116  * Should be called every 1000 msec (defined by IP_TMR_INTERVAL).
00117  */
00118 void
00119 ip_reass_tmr(void)
00120 {
00121   struct ip_reassdata *r, *prev = NULL;
00122 
00123   r = reassdatagrams;
00124   while (r != NULL) {
00125     /* Decrement the timer. Once it reaches 0,
00126      * clean up the incomplete fragment assembly */
00127     if (r->timer > 0) {
00128       r->timer--;
00129       LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_tmr: timer dec %"U16_F"\n",(u16_t)r->timer));
00130       prev = r;
00131       r = r->next;
00132     } else {
00133       /* reassembly timed out */
00134       struct ip_reassdata *tmp;
00135       LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_tmr: timer timed out\n"));
00136       tmp = r;
00137       /* get the next pointer before freeing */
00138       r = r->next;
00139       /* free the helper struct and all enqueued pbufs */
00140       ip_reass_free_complete_datagram(tmp, prev);
00141      }
00142    }
00143 }
00144 
00145 /**
00146  * Free a datagram (struct ip_reassdata) and all its pbufs.
00147  * Updates the total count of enqueued pbufs (ip_reass_pbufcount),
00148  * SNMP counters and sends an ICMP time exceeded packet.
00149  *
00150  * @param ipr datagram to free
00151  * @param prev the previous datagram in the linked list
00152  * @return the number of pbufs freed
00153  */
00154 static int
00155 ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev)
00156 {
00157   u16_t pbufs_freed = 0;
00158   u8_t clen;
00159   struct pbuf *p;
00160   struct ip_reass_helper *iprh;
00161 
00162   LWIP_ASSERT("prev != ipr", prev != ipr);
00163   if (prev != NULL) {
00164     LWIP_ASSERT("prev->next == ipr", prev->next == ipr);
00165   }
00166 
00167   snmp_inc_ipreasmfails();
00168 #if LWIP_ICMP
00169   iprh = (struct ip_reass_helper *)ipr->p->payload;
00170   if (iprh->start == 0) {
00171     /* The first fragment was received, send ICMP time exceeded. */
00172     /* First, de-queue the first pbuf from r->p. */
00173     p = ipr->p;
00174     ipr->p = iprh->next_pbuf;
00175     /* Then, copy the original header into it. */
00176     SMEMCPY(p->payload, &ipr->iphdr, IP_HLEN);
00177     icmp_time_exceeded(p, ICMP_TE_FRAG);
00178     clen = pbuf_clen(p);
00179     LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff);
00180     pbufs_freed += clen;
00181     pbuf_free(p);
00182   }
00183 #endif /* LWIP_ICMP */
00184 
00185   /* First, free all received pbufs.  The individual pbufs need to be released 
00186      separately as they have not yet been chained */
00187   p = ipr->p;
00188   while (p != NULL) {
00189     struct pbuf *pcur;
00190     iprh = (struct ip_reass_helper *)p->payload;
00191     pcur = p;
00192     /* get the next pointer before freeing */
00193     p = iprh->next_pbuf;
00194     clen = pbuf_clen(pcur);
00195     LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff);
00196     pbufs_freed += clen;
00197     pbuf_free(pcur);
00198   }
00199   /* Then, unchain the struct ip_reassdata from the list and free it. */
00200   ip_reass_dequeue_datagram(ipr, prev);
00201   LWIP_ASSERT("ip_reass_pbufcount >= clen", ip_reass_pbufcount >= pbufs_freed);
00202   ip_reass_pbufcount -= pbufs_freed;
00203 
00204   return pbufs_freed;
00205 }
00206 
00207 #if IP_REASS_FREE_OLDEST
00208 /**
00209  * Free the oldest datagram to make room for enqueueing new fragments.
00210  * The datagram 'fraghdr' belongs to is not freed!
00211  *
00212  * @param fraghdr IP header of the current fragment
00213  * @param pbufs_needed number of pbufs needed to enqueue
00214  *        (used for freeing other datagrams if not enough space)
00215  * @return the number of pbufs freed
00216  */
00217 static int
00218 ip_reass_remove_oldest_datagram(struct ip_hdr *fraghdr, int pbufs_needed)
00219 {
00220   /* @todo Can't we simply remove the last datagram in the
00221    *       linked list behind reassdatagrams?
00222    */
00223   struct ip_reassdata *r, *oldest, *prev;
00224   int pbufs_freed = 0, pbufs_freed_current;
00225   int other_datagrams;
00226 
00227   /* Free datagrams until being allowed to enqueue 'pbufs_needed' pbufs,
00228    * but don't free the datagram that 'fraghdr' belongs to! */
00229   do {
00230     oldest = NULL;
00231     prev = NULL;
00232     other_datagrams = 0;
00233     r = reassdatagrams;
00234     while (r != NULL) {
00235       if (!IP_ADDRESSES_AND_ID_MATCH(&r->iphdr, fraghdr)) {
00236         /* Not the same datagram as fraghdr */
00237         other_datagrams++;
00238         if (oldest == NULL) {
00239           oldest = r;
00240         } else if (r->timer <= oldest->timer) {
00241           /* older than the previous oldest */
00242           oldest = r;
00243         }
00244       }
00245       if (r->next != NULL) {
00246         prev = r;
00247       }
00248       r = r->next;
00249     }
00250     if (oldest != NULL) {
00251       pbufs_freed_current = ip_reass_free_complete_datagram(oldest, prev);
00252       pbufs_freed += pbufs_freed_current;
00253     }
00254   } while ((pbufs_freed < pbufs_needed) && (other_datagrams > 1));
00255   return pbufs_freed;
00256 }
00257 #endif /* IP_REASS_FREE_OLDEST */
00258 
00259 /**
00260  * Enqueues a new fragment into the fragment queue
00261  * @param fraghdr points to the new fragments IP hdr
00262  * @param clen number of pbufs needed to enqueue (used for freeing other datagrams if not enough space)
00263  * @return A pointer to the queue location into which the fragment was enqueued
00264  */
00265 static struct ip_reassdata*
00266 ip_reass_enqueue_new_datagram(struct ip_hdr *fraghdr, int clen)
00267 {
00268   struct ip_reassdata* ipr;
00269   /* No matching previous fragment found, allocate a new reassdata struct */
00270   ipr = (struct ip_reassdata*) memp_malloc(MEMP_REASSDATA);
00271   if (ipr == NULL) {
00272 #if IP_REASS_FREE_OLDEST
00273     if (ip_reass_remove_oldest_datagram(fraghdr, clen) >= clen) {
00274       ipr =  (struct ip_reassdata*) memp_malloc(MEMP_REASSDATA);
00275     }
00276     if (ipr == NULL)
00277 #endif /* IP_REASS_FREE_OLDEST */
00278     {
00279       IPFRAG_STATS_INC(ip_frag.memerr);
00280       LWIP_DEBUGF(IP_REASS_DEBUG,("Failed to alloc reassdata struct\n"));
00281       return NULL;
00282     }
00283   }
00284   memset(ipr, 0, sizeof(struct ip_reassdata));
00285   ipr->timer = IP_REASS_MAXAGE;
00286 
00287   /* enqueue the new structure to the front of the list */
00288   ipr->next = reassdatagrams;
00289   reassdatagrams = ipr;
00290   /* copy the ip header for later tests and input */
00291   /* @todo: no ip options supported? */
00292   SMEMCPY(&(ipr->iphdr), fraghdr, IP_HLEN);
00293   return ipr;
00294 }
00295 
00296 /**
00297  * Dequeues a datagram from the datagram queue. Doesn't deallocate the pbufs.
00298  * @param ipr points to the queue entry to dequeue
00299  */
00300 static void
00301 ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev)
00302 {
00303   
00304   /* dequeue the reass struct  */
00305   if (reassdatagrams == ipr) {
00306     /* it was the first in the list */
00307     reassdatagrams = ipr->next;
00308   } else {
00309     /* it wasn't the first, so it must have a valid 'prev' */
00310     LWIP_ASSERT("sanity check linked list", prev != NULL);
00311     prev->next = ipr->next;
00312   }
00313 
00314   /* now we can free the ip_reass struct */
00315   memp_free(MEMP_REASSDATA, ipr);
00316 }
00317 
00318 /**
00319  * Chain a new pbuf into the pbuf list that composes the datagram.  The pbuf list
00320  * will grow over time as  new pbufs are rx.
00321  * Also checks that the datagram passes basic continuity checks (if the last
00322  * fragment was received at least once).
00323  * @param root_p points to the 'root' pbuf for the current datagram being assembled.
00324  * @param new_p points to the pbuf for the current fragment
00325  * @return 0 if invalid, >0 otherwise
00326  */
00327 static int
00328 ip_reass_chain_frag_into_datagram_and_validate(struct ip_reassdata *ipr, struct pbuf *new_p)
00329 {
00330   struct ip_reass_helper *iprh, *iprh_tmp, *iprh_prev=NULL;
00331   struct pbuf *q;
00332   u16_t offset,len;
00333   struct ip_hdr *fraghdr;
00334   int valid = 1;
00335 
00336   /* Extract length and fragment offset from current fragment */
00337   fraghdr = (struct ip_hdr*)new_p->payload; 
00338   len = ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4;
00339   offset = (ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8;
00340 
00341   /* overwrite the fragment's ip header from the pbuf with our helper struct,
00342    * and setup the embedded helper structure. */
00343   /* make sure the struct ip_reass_helper fits into the IP header */
00344   LWIP_ASSERT("sizeof(struct ip_reass_helper) <= IP_HLEN",
00345               sizeof(struct ip_reass_helper) <= IP_HLEN);
00346   iprh = (struct ip_reass_helper*)new_p->payload;
00347   iprh->next_pbuf = NULL;
00348   iprh->start = offset;
00349   iprh->end = offset + len;
00350 
00351   /* Iterate through until we either get to the end of the list (append),
00352    * or we find on with a larger offset (insert). */
00353   for (q = ipr->p; q != NULL;) {
00354     iprh_tmp = (struct ip_reass_helper*)q->payload;
00355     if (iprh->start < iprh_tmp->start) {
00356       /* the new pbuf should be inserted before this */
00357       iprh->next_pbuf = q;
00358       if (iprh_prev != NULL) {
00359         /* not the fragment with the lowest offset */
00360 #if IP_REASS_CHECK_OVERLAP
00361         if ((iprh->start < iprh_prev->end) || (iprh->end > iprh_tmp->start)) {
00362           /* fragment overlaps with previous or following, throw away */
00363           goto freepbuf;
00364         }
00365 #endif /* IP_REASS_CHECK_OVERLAP */
00366         iprh_prev->next_pbuf = new_p;
00367       } else {
00368         /* fragment with the lowest offset */
00369         ipr->p = new_p;
00370       }
00371       break;
00372     } else if(iprh->start == iprh_tmp->start) {
00373       /* received the same datagram twice: no need to keep the datagram */
00374       goto freepbuf;
00375 #if IP_REASS_CHECK_OVERLAP
00376     } else if(iprh->start < iprh_tmp->end) {
00377       /* overlap: no need to keep the new datagram */
00378       goto freepbuf;
00379 #endif /* IP_REASS_CHECK_OVERLAP */
00380     } else {
00381       /* Check if the fragments received so far have no wholes. */
00382       if (iprh_prev != NULL) {
00383         if (iprh_prev->end != iprh_tmp->start) {
00384           /* There is a fragment missing between the current
00385            * and the previous fragment */
00386           valid = 0;
00387         }
00388       }
00389     }
00390     q = iprh_tmp->next_pbuf;
00391     iprh_prev = iprh_tmp;
00392   }
00393 
00394   /* If q is NULL, then we made it to the end of the list. Determine what to do now */
00395   if (q == NULL) {
00396     if (iprh_prev != NULL) {
00397       /* this is (for now), the fragment with the highest offset:
00398        * chain it to the last fragment */
00399 #if IP_REASS_CHECK_OVERLAP
00400       LWIP_ASSERT("check fragments don't overlap", iprh_prev->end <= iprh->start);
00401 #endif /* IP_REASS_CHECK_OVERLAP */
00402       iprh_prev->next_pbuf = new_p;
00403       if (iprh_prev->end != iprh->start) {
00404         valid = 0;
00405       }
00406     } else {
00407 #if IP_REASS_CHECK_OVERLAP
00408       LWIP_ASSERT("no previous fragment, this must be the first fragment!",
00409         ipr->p == NULL);
00410 #endif /* IP_REASS_CHECK_OVERLAP */
00411       /* this is the first fragment we ever received for this ip datagram */
00412       ipr->p = new_p;
00413     }
00414   }
00415 
00416   /* At this point, the validation part begins: */
00417   /* If we already received the last fragment */
00418   if ((ipr->flags & IP_REASS_FLAG_LASTFRAG) != 0) {
00419     /* and had no wholes so far */
00420     if (valid) {
00421       /* then check if the rest of the fragments is here */
00422       /* Check if the queue starts with the first datagram */
00423       if (((struct ip_reass_helper*)ipr->p->payload)->start != 0) {
00424         valid = 0;
00425       } else {
00426         /* and check that there are no wholes after this datagram */
00427         iprh_prev = iprh;
00428         q = iprh->next_pbuf;
00429         while (q != NULL) {
00430           iprh = (struct ip_reass_helper*)q->payload;
00431           if (iprh_prev->end != iprh->start) {
00432             valid = 0;
00433             break;
00434           }
00435           iprh_prev = iprh;
00436           q = iprh->next_pbuf;
00437         }
00438         /* if still valid, all fragments are received
00439          * (because to the MF==0 already arrived */
00440         if (valid) {
00441           LWIP_ASSERT("sanity check", ipr->p != NULL);
00442           LWIP_ASSERT("sanity check",
00443             ((struct ip_reass_helper*)ipr->p->payload) != iprh);
00444           LWIP_ASSERT("validate_datagram:next_pbuf!=NULL",
00445             iprh->next_pbuf == NULL);
00446           LWIP_ASSERT("validate_datagram:datagram end!=datagram len",
00447             iprh->end == ipr->datagram_len);
00448         }
00449       }
00450     }
00451     /* If valid is 0 here, there are some fragments missing in the middle
00452      * (since MF == 0 has already arrived). Such datagrams simply time out if
00453      * no more fragments are received... */
00454     return valid;
00455   }
00456   /* If we come here, not all fragments were received, yet! */
00457   return 0; /* not yet valid! */
00458 #if IP_REASS_CHECK_OVERLAP
00459 freepbuf:
00460   ip_reass_pbufcount -= pbuf_clen(new_p);
00461   pbuf_free(new_p);
00462   return 0;
00463 #endif /* IP_REASS_CHECK_OVERLAP */
00464 }
00465 
00466 /**
00467  * Reassembles incoming IP fragments into an IP datagram.
00468  *
00469  * @param p points to a pbuf chain of the fragment
00470  * @return NULL if reassembly is incomplete, ? otherwise
00471  */
00472 struct pbuf *
00473 ip_reass(struct pbuf *p)
00474 {
00475   struct pbuf *r;
00476   struct ip_hdr *fraghdr;
00477   struct ip_reassdata *ipr;
00478   struct ip_reass_helper *iprh;
00479   u16_t offset, len;
00480   u8_t clen;
00481   struct ip_reassdata *ipr_prev = NULL;
00482 
00483   IPFRAG_STATS_INC(ip_frag.recv);
00484   snmp_inc_ipreasmreqds();
00485 
00486   fraghdr = (struct ip_hdr*)p->payload;
00487 
00488   if ((IPH_HL(fraghdr) * 4) != IP_HLEN) {
00489     LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: IP options currently not supported!\n"));
00490     IPFRAG_STATS_INC(ip_frag.err);
00491     goto nullreturn;
00492   }
00493 
00494   offset = (ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8;
00495   len = ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4;
00496 
00497   /* Check if we are allowed to enqueue more datagrams. */
00498   clen = pbuf_clen(p);
00499   if ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS) {
00500 #if IP_REASS_FREE_OLDEST
00501     if (!ip_reass_remove_oldest_datagram(fraghdr, clen) ||
00502         ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS))
00503 #endif /* IP_REASS_FREE_OLDEST */
00504     {
00505       /* No datagram could be freed and still too many pbufs enqueued */
00506       LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: Overflow condition: pbufct=%d, clen=%d, MAX=%d\n",
00507         ip_reass_pbufcount, clen, IP_REASS_MAX_PBUFS));
00508       IPFRAG_STATS_INC(ip_frag.memerr);
00509       /* @todo: send ICMP time exceeded here? */
00510       /* drop this pbuf */
00511       goto nullreturn;
00512     }
00513   }
00514 
00515   /* Look for the datagram the fragment belongs to in the current datagram queue,
00516    * remembering the previous in the queue for later dequeueing. */
00517   for (ipr = reassdatagrams; ipr != NULL; ipr = ipr->next) {
00518     /* Check if the incoming fragment matches the one currently present
00519        in the reassembly buffer. If so, we proceed with copying the
00520        fragment into the buffer. */
00521     if (IP_ADDRESSES_AND_ID_MATCH(&ipr->iphdr, fraghdr)) {
00522       LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass: matching previous fragment ID=%"X16_F"\n",
00523         ntohs(IPH_ID(fraghdr))));
00524       IPFRAG_STATS_INC(ip_frag.cachehit);
00525       break;
00526     }
00527     ipr_prev = ipr;
00528   }
00529 
00530   if (ipr == NULL) {
00531   /* Enqueue a new datagram into the datagram queue */
00532     ipr = ip_reass_enqueue_new_datagram(fraghdr, clen);
00533     /* Bail if unable to enqueue */
00534     if(ipr == NULL) {
00535       goto nullreturn;
00536     }
00537   } else {
00538     if (((ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) == 0) && 
00539       ((ntohs(IPH_OFFSET(&ipr->iphdr)) & IP_OFFMASK) != 0)) {
00540       /* ipr->iphdr is not the header from the first fragment, but fraghdr is
00541        * -> copy fraghdr into ipr->iphdr since we want to have the header
00542        * of the first fragment (for ICMP time exceeded and later, for copying
00543        * all options, if supported)*/
00544       SMEMCPY(&ipr->iphdr, fraghdr, IP_HLEN);
00545     }
00546   }
00547   /* Track the current number of pbufs current 'in-flight', in order to limit 
00548   the number of fragments that may be enqueued at any one time */
00549   ip_reass_pbufcount += clen;
00550 
00551   /* At this point, we have either created a new entry or pointing 
00552    * to an existing one */
00553 
00554   /* check for 'no more fragments', and update queue entry*/
00555   if ((ntohs(IPH_OFFSET(fraghdr)) & IP_MF) == 0) {
00556     ipr->flags |= IP_REASS_FLAG_LASTFRAG;
00557     ipr->datagram_len = offset + len;
00558     LWIP_DEBUGF(IP_REASS_DEBUG,
00559      ("ip_reass: last fragment seen, total len %"S16_F"\n",
00560       ipr->datagram_len));
00561   }
00562   /* find the right place to insert this pbuf */
00563   /* @todo: trim pbufs if fragments are overlapping */
00564   if (ip_reass_chain_frag_into_datagram_and_validate(ipr, p)) {
00565     /* the totally last fragment (flag more fragments = 0) was received at least
00566      * once AND all fragments are received */
00567     ipr->datagram_len += IP_HLEN;
00568 
00569     /* save the second pbuf before copying the header over the pointer */
00570     r = ((struct ip_reass_helper*)ipr->p->payload)->next_pbuf;
00571 
00572     /* copy the original ip header back to the first pbuf */
00573     fraghdr = (struct ip_hdr*)(ipr->p->payload);
00574     SMEMCPY(fraghdr, &ipr->iphdr, IP_HLEN);
00575     IPH_LEN_SET(fraghdr, htons(ipr->datagram_len));
00576     IPH_OFFSET_SET(fraghdr, 0);
00577     IPH_CHKSUM_SET(fraghdr, 0);
00578     /* @todo: do we need to set calculate the correct checksum? */
00579     IPH_CHKSUM_SET(fraghdr, inet_chksum(fraghdr, IP_HLEN));
00580 
00581     p = ipr->p;
00582 
00583     /* chain together the pbufs contained within the reass_data list. */
00584     while(r != NULL) {
00585       iprh = (struct ip_reass_helper*)r->payload;
00586 
00587       /* hide the ip header for every succeding fragment */
00588       pbuf_header(r, -IP_HLEN);
00589       pbuf_cat(p, r);
00590       r = iprh->next_pbuf;
00591     }
00592     /* release the sources allocate for the fragment queue entry */
00593     ip_reass_dequeue_datagram(ipr, ipr_prev);
00594 
00595     /* and adjust the number of pbufs currently queued for reassembly. */
00596     ip_reass_pbufcount -= pbuf_clen(p);
00597 
00598     /* Return the pbuf chain */
00599     return p;
00600   }
00601   /* the datagram is not (yet?) reassembled completely */
00602   LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass_pbufcount: %d out\n", ip_reass_pbufcount));
00603   return NULL;
00604 
00605 nullreturn:
00606   LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: nullreturn\n"));
00607   IPFRAG_STATS_INC(ip_frag.drop);
00608   pbuf_free(p);
00609   return NULL;
00610 }
00611 #endif /* IP_REASSEMBLY */
00612 
00613 #if IP_FRAG
00614 #if IP_FRAG_USES_STATIC_BUF
00615 static u8_t buf[LWIP_MEM_ALIGN_SIZE(IP_FRAG_MAX_MTU + MEM_ALIGNMENT - 1)];
00616 #endif /* IP_FRAG_USES_STATIC_BUF */
00617 
00618 /**
00619  * Fragment an IP datagram if too large for the netif.
00620  *
00621  * Chop the datagram in MTU sized chunks and send them in order
00622  * by using a fixed size static memory buffer (PBUF_REF) or
00623  * point PBUF_REFs into p (depending on IP_FRAG_USES_STATIC_BUF).
00624  *
00625  * @param p ip packet to send
00626  * @param netif the netif on which to send
00627  * @param dest destination ip address to which to send
00628  *
00629  * @return ERR_OK if sent successfully, err_t otherwise
00630  */
00631 err_t 
00632 ip_frag(struct pbuf *p, struct netif *netif, ip_addr_t *dest)
00633 {
00634   struct pbuf *rambuf;
00635 #if IP_FRAG_USES_STATIC_BUF
00636   struct pbuf *header;
00637 #else
00638   struct pbuf *newpbuf;
00639   struct ip_hdr *original_iphdr;
00640 #endif
00641   struct ip_hdr *iphdr;
00642   u16_t nfb;
00643   u16_t left, cop;
00644   u16_t mtu = netif->mtu;
00645   u16_t ofo, omf;
00646   u16_t last;
00647   u16_t poff = IP_HLEN;
00648   u16_t tmp;
00649 #if !IP_FRAG_USES_STATIC_BUF
00650   u16_t newpbuflen = 0;
00651   u16_t left_to_copy;
00652 #endif
00653 
00654   /* Get a RAM based MTU sized pbuf */
00655 #if IP_FRAG_USES_STATIC_BUF
00656   /* When using a static buffer, we use a PBUF_REF, which we will
00657    * use to reference the packet (without link header).
00658    * Layer and length is irrelevant.
00659    */
00660   rambuf = pbuf_alloc(PBUF_LINK, 0, PBUF_REF);
00661   if (rambuf == NULL) {
00662     LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_frag: pbuf_alloc(PBUF_LINK, 0, PBUF_REF) failed\n"));
00663     return ERR_MEM;
00664   }
00665   rambuf->tot_len = rambuf->len = mtu;
00666   rambuf->payload = LWIP_MEM_ALIGN((void *)buf);
00667 
00668   /* Copy the IP header in it */
00669   iphdr = (struct ip_hdr*) rambuf->payload;
00670   SMEMCPY(iphdr, p->payload, IP_HLEN);
00671 #else /* IP_FRAG_USES_STATIC_BUF */
00672   original_iphdr = (struct ip_hdr*) p->payload;
00673   iphdr = original_iphdr;
00674 #endif /* IP_FRAG_USES_STATIC_BUF */
00675 
00676   /* Save original offset */
00677   tmp = ntohs(IPH_OFFSET(iphdr));
00678   ofo = tmp & IP_OFFMASK;
00679   omf = tmp & IP_MF;
00680 
00681   left = p->tot_len - IP_HLEN;
00682 
00683   nfb = (mtu - IP_HLEN) / 8;
00684 
00685   while (left) {
00686     last = (left <= mtu - IP_HLEN);
00687 
00688     /* Set new offset and MF flag */
00689     tmp = omf | (IP_OFFMASK & (ofo));
00690     if (!last) {
00691       tmp = tmp | IP_MF;
00692     }
00693 
00694     /* Fill this fragment */
00695     cop = last ? left : nfb * 8;
00696 
00697 #if IP_FRAG_USES_STATIC_BUF
00698     poff += pbuf_copy_partial(p, (u8_t*)iphdr + IP_HLEN, cop, poff);
00699 #else /* IP_FRAG_USES_STATIC_BUF */
00700     /* When not using a static buffer, create a chain of pbufs.
00701      * The first will be a PBUF_RAM holding the link and IP header.
00702      * The rest will be PBUF_REFs mirroring the pbuf chain to be fragged,
00703      * but limited to the size of an mtu.
00704      */
00705     rambuf = pbuf_alloc(PBUF_LINK, IP_HLEN, PBUF_RAM);
00706     if (rambuf == NULL) {
00707       return ERR_MEM;
00708     }
00709     LWIP_ASSERT("this needs a pbuf in one piece!",
00710                 (p->len >= (IP_HLEN)));
00711     SMEMCPY(rambuf->payload, original_iphdr, IP_HLEN);
00712     iphdr = (struct ip_hdr*) rambuf->payload;
00713 
00714     /* Can just adjust p directly for needed offset. */
00715     p->payload = (u8_t *)p->payload + poff;
00716     p->len -= poff;
00717 
00718     left_to_copy = cop;
00719     while (left_to_copy) {
00720       newpbuflen = (left_to_copy < p->len) ? left_to_copy : p->len;
00721       /* Is this pbuf already empty? */
00722       if (!newpbuflen) {
00723         p = p->next;
00724         continue;
00725       }
00726       newpbuf = pbuf_alloc(PBUF_RAW, 0, PBUF_REF);
00727       if (newpbuf == NULL) {
00728         pbuf_free(rambuf);
00729         return ERR_MEM;
00730       }
00731       /* Mirror this pbuf, although we might not need all of it. */
00732       newpbuf->payload = p->payload;
00733       newpbuf->len = newpbuf->tot_len = newpbuflen;
00734       /* Add it to end of rambuf's chain, but using pbuf_cat, not pbuf_chain
00735        * so that it is removed when pbuf_dechain is later called on rambuf.
00736        */
00737       pbuf_cat(rambuf, newpbuf);
00738       left_to_copy -= newpbuflen;
00739       if (left_to_copy) {
00740         p = p->next;
00741       }
00742     }
00743     poff = newpbuflen;
00744 #endif /* IP_FRAG_USES_STATIC_BUF */
00745 
00746     /* Correct header */
00747     IPH_OFFSET_SET(iphdr, htons(tmp));
00748     IPH_LEN_SET(iphdr, htons(cop + IP_HLEN));
00749     IPH_CHKSUM_SET(iphdr, 0);
00750     IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN));
00751 
00752 #if IP_FRAG_USES_STATIC_BUF
00753     if (last) {
00754       pbuf_realloc(rambuf, left + IP_HLEN);
00755     }
00756 
00757     /* This part is ugly: we alloc a RAM based pbuf for 
00758      * the link level header for each chunk and then 
00759      * free it.A PBUF_ROM style pbuf for which pbuf_header
00760      * worked would make things simpler.
00761      */
00762     header = pbuf_alloc(PBUF_LINK, 0, PBUF_RAM);
00763     if (header != NULL) {
00764       pbuf_chain(header, rambuf);
00765       netif->output(netif, header, dest);
00766       IPFRAG_STATS_INC(ip_frag.xmit);
00767       snmp_inc_ipfragcreates();
00768       pbuf_free(header);
00769     } else {
00770       LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_frag: pbuf_alloc() for header failed\n"));
00771       pbuf_free(rambuf);
00772       return ERR_MEM;
00773     }
00774 #else /* IP_FRAG_USES_STATIC_BUF */
00775     /* No need for separate header pbuf - we allowed room for it in rambuf
00776      * when allocated.
00777      */
00778     netif->output(netif, rambuf, dest);
00779     IPFRAG_STATS_INC(ip_frag.xmit);
00780 
00781     /* Unfortunately we can't reuse rambuf - the hardware may still be
00782      * using the buffer. Instead we free it (and the ensuing chain) and
00783      * recreate it next time round the loop. If we're lucky the hardware
00784      * will have already sent the packet, the free will really free, and
00785      * there will be zero memory penalty.
00786      */
00787     
00788     pbuf_free(rambuf);
00789 #endif /* IP_FRAG_USES_STATIC_BUF */
00790     left -= cop;
00791     ofo += nfb;
00792   }
00793 #if IP_FRAG_USES_STATIC_BUF
00794   pbuf_free(rambuf);
00795 #endif /* IP_FRAG_USES_STATIC_BUF */
00796   snmp_inc_ipfragoks();
00797   return ERR_OK;
00798 }
00799 #endif /* IP_FRAG */