Example program with HTTPServer and sensor data streaming over TCPSockets, using Donatien Garnier's Net APIs and services code on top of LWIP. Files StreamServer.h and .cpp encapsulate streaming over TCPSockets. Broadcast is done by sendToAll(), and all incoming data is echoed back to the client. Echo code can be replaced with some remote control of the streaming interface. See main() that shows how to periodically send some data to all subscribed clients. To subscribe, a client should open a socket at <mbed_ip> port 123. I used few lines in TCL code to set up a quick sink for the data. HTTP files are served on port 80 concurrently to the streaming.

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 */