ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

ip_frag.c
Go to the documentation of this file.
00001 
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 
00066 #ifndef IP_REASS_CHECK_OVERLAP
00067 #define IP_REASS_CHECK_OVERLAP 1
00068 #endif /* IP_REASS_CHECK_OVERLAP */
00069 
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 
00088 #ifdef PACK_STRUCT_USE_INCLUDES
00089 #  include "arch/bpstruct.h"
00090 #endif
00091 PACK_STRUCT_BEGIN
00092 struct ip_reass_helper {
00093   PACK_STRUCT_FIELD(struct pbuf *next_pbuf);
00094   PACK_STRUCT_FIELD(u16_t start);
00095   PACK_STRUCT_FIELD(u16_t end);
00096 } PACK_STRUCT_STRUCT;
00097 PACK_STRUCT_END
00098 #ifdef PACK_STRUCT_USE_INCLUDES
00099 #  include "arch/epstruct.h"
00100 #endif
00101 
00102 #define IP_ADDRESSES_AND_ID_MATCH(iphdrA, iphdrB)  \
00103   (ip_addr_cmp(&(iphdrA)->src, &(iphdrB)->src) && \
00104    ip_addr_cmp(&(iphdrA)->dest, &(iphdrB)->dest) && \
00105    IPH_ID(iphdrA) == IPH_ID(iphdrB)) ? 1 : 0
00106 
00107 /* global variables */
00108 static struct ip_reassdata *reassdatagrams;
00109 static u16_t ip_reass_pbufcount;
00110 
00111 /* function prototypes */
00112 static void ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev);
00113 static int ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev);
00114 
00121 void
00122 ip_reass_tmr(void)
00123 {
00124   struct ip_reassdata *r, *prev = NULL;
00125 
00126   r = reassdatagrams;
00127   while (r != NULL) {
00128     /* Decrement the timer. Once it reaches 0,
00129      * clean up the incomplete fragment assembly */
00130     if (r->timer > 0) {
00131       r->timer--;
00132       LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_tmr: timer dec %"U16_F"\n",(u16_t)r->timer));
00133       prev = r;
00134       r = r->next;
00135     } else {
00136       /* reassembly timed out */
00137       struct ip_reassdata *tmp;
00138       LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_tmr: timer timed out\n"));
00139       tmp = r;
00140       /* get the next pointer before freeing */
00141       r = r->next;
00142       /* free the helper struct and all enqueued pbufs */
00143       ip_reass_free_complete_datagram(tmp, prev);
00144      }
00145    }
00146 }
00147 
00157 static int
00158 ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev)
00159 {
00160   u16_t pbufs_freed = 0;
00161   u8_t clen;
00162   struct pbuf *p;
00163   struct ip_reass_helper *iprh;
00164 
00165   LWIP_ASSERT("prev != ipr", prev != ipr);
00166   if (prev != NULL) {
00167     LWIP_ASSERT("prev->next == ipr", prev->next == ipr);
00168   }
00169 
00170   snmp_inc_ipreasmfails();
00171 #if LWIP_ICMP
00172   iprh = (struct ip_reass_helper *)ipr->p->payload;
00173   if (iprh->start == 0) {
00174     /* The first fragment was received, send ICMP time exceeded. */
00175     /* First, de-queue the first pbuf from r->p. */
00176     p = ipr->p;
00177     ipr->p = iprh->next_pbuf;
00178     /* Then, copy the original header into it. */
00179     SMEMCPY(p->payload, &ipr->iphdr, IP_HLEN);
00180     icmp_time_exceeded(p, ICMP_TE_FRAG);
00181     clen = pbuf_clen(p);
00182     LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff);
00183     pbufs_freed += clen;
00184     pbuf_free(p);
00185   }
00186 #endif /* LWIP_ICMP */
00187 
00188   /* First, free all received pbufs.  The individual pbufs need to be released 
00189      separately as they have not yet been chained */
00190   p = ipr->p;
00191   while (p != NULL) {
00192     struct pbuf *pcur;
00193     iprh = (struct ip_reass_helper *)p->payload;
00194     pcur = p;
00195     /* get the next pointer before freeing */
00196     p = iprh->next_pbuf;
00197     clen = pbuf_clen(pcur);
00198     LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff);
00199     pbufs_freed += clen;
00200     pbuf_free(pcur);
00201   }
00202   /* Then, unchain the struct ip_reassdata from the list and free it. */
00203   ip_reass_dequeue_datagram(ipr, prev);
00204   LWIP_ASSERT("ip_reass_pbufcount >= clen", ip_reass_pbufcount >= pbufs_freed);
00205   ip_reass_pbufcount -= pbufs_freed;
00206 
00207   return pbufs_freed;
00208 }
00209 
00210 #if IP_REASS_FREE_OLDEST
00211 
00220 static int
00221 ip_reass_remove_oldest_datagram(struct ip_hdr *fraghdr, int pbufs_needed)
00222 {
00223   /* @todo Can't we simply remove the last datagram in the
00224    *       linked list behind reassdatagrams?
00225    */
00226   struct ip_reassdata *r, *oldest, *prev;
00227   int pbufs_freed = 0, pbufs_freed_current;
00228   int other_datagrams;
00229 
00230   /* Free datagrams until being allowed to enqueue 'pbufs_needed' pbufs,
00231    * but don't free the datagram that 'fraghdr' belongs to! */
00232   do {
00233     oldest = NULL;
00234     prev = NULL;
00235     other_datagrams = 0;
00236     r = reassdatagrams;
00237     while (r != NULL) {
00238       if (!IP_ADDRESSES_AND_ID_MATCH(&r->iphdr, fraghdr)) {
00239         /* Not the same datagram as fraghdr */
00240         other_datagrams++;
00241         if (oldest == NULL) {
00242           oldest = r;
00243         } else if (r->timer <= oldest->timer) {
00244           /* older than the previous oldest */
00245           oldest = r;
00246         }
00247       }
00248       if (r->next != NULL) {
00249         prev = r;
00250       }
00251       r = r->next;
00252     }
00253     if (oldest != NULL) {
00254       pbufs_freed_current = ip_reass_free_complete_datagram(oldest, prev);
00255       pbufs_freed += pbufs_freed_current;
00256     }
00257   } while ((pbufs_freed < pbufs_needed) && (other_datagrams > 1));
00258   return pbufs_freed;
00259 }
00260 #endif /* IP_REASS_FREE_OLDEST */
00261 
00268 static struct ip_reassdata*
00269 ip_reass_enqueue_new_datagram(struct ip_hdr *fraghdr, int clen)
00270 {
00271   struct ip_reassdata* ipr;
00272   /* No matching previous fragment found, allocate a new reassdata struct */
00273   ipr = (struct ip_reassdata *)memp_malloc(MEMP_REASSDATA);
00274   if (ipr == NULL) {
00275 #if IP_REASS_FREE_OLDEST
00276     if (ip_reass_remove_oldest_datagram(fraghdr, clen) >= clen) {
00277       ipr = (struct ip_reassdata *)memp_malloc(MEMP_REASSDATA);
00278     }
00279     if (ipr == NULL)
00280 #endif /* IP_REASS_FREE_OLDEST */
00281     {
00282       IPFRAG_STATS_INC(ip_frag.memerr);
00283       LWIP_DEBUGF(IP_REASS_DEBUG,("Failed to alloc reassdata struct\n"));
00284       return NULL;
00285     }
00286   }
00287   memset(ipr, 0, sizeof(struct ip_reassdata));
00288   ipr->timer = IP_REASS_MAXAGE;
00289 
00290   /* enqueue the new structure to the front of the list */
00291   ipr->next = reassdatagrams;
00292   reassdatagrams = ipr;
00293   /* copy the ip header for later tests and input */
00294   /* @todo: no ip options supported? */
00295   SMEMCPY(&(ipr->iphdr), fraghdr, IP_HLEN);
00296   return ipr;
00297 }
00298 
00303 static void
00304 ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev)
00305 {
00306   
00307   /* dequeue the reass struct  */
00308   if (reassdatagrams == ipr) {
00309     /* it was the first in the list */
00310     reassdatagrams = ipr->next;
00311   } else {
00312     /* it wasn't the first, so it must have a valid 'prev' */
00313     LWIP_ASSERT("sanity check linked list", prev != NULL);
00314     prev->next = ipr->next;
00315   }
00316 
00317   /* now we can free the ip_reass struct */
00318   memp_free(MEMP_REASSDATA, ipr);
00319 }
00320 
00330 static int
00331 ip_reass_chain_frag_into_datagram_and_validate(struct ip_reassdata *ipr, struct pbuf *new_p)
00332 {
00333   struct ip_reass_helper *iprh, *iprh_tmp, *iprh_prev=NULL;
00334   struct pbuf *q;
00335   u16_t offset,len;
00336   struct ip_hdr *fraghdr;
00337   int valid = 1;
00338 
00339   /* Extract length and fragment offset from current fragment */
00340   fraghdr = (struct ip_hdr*)new_p->payload; 
00341   len = ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4;
00342   offset = (ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8;
00343 
00344   /* overwrite the fragment's ip header from the pbuf with our helper struct,
00345    * and setup the embedded helper structure. */
00346   /* make sure the struct ip_reass_helper fits into the IP header */
00347   LWIP_ASSERT("sizeof(struct ip_reass_helper) <= IP_HLEN",
00348               sizeof(struct ip_reass_helper) <= IP_HLEN);
00349   iprh = (struct ip_reass_helper*)new_p->payload;
00350   iprh->next_pbuf = NULL;
00351   iprh->start = offset;
00352   iprh->end = offset + len;
00353 
00354   /* Iterate through until we either get to the end of the list (append),
00355    * or we find on with a larger offset (insert). */
00356   for (q = ipr->p; q != NULL;) {
00357     iprh_tmp = (struct ip_reass_helper*)q->payload;
00358     if (iprh->start < iprh_tmp->start) {
00359       /* the new pbuf should be inserted before this */
00360       iprh->next_pbuf = q;
00361       if (iprh_prev != NULL) {
00362         /* not the fragment with the lowest offset */
00363 #if IP_REASS_CHECK_OVERLAP
00364         if ((iprh->start < iprh_prev->end) || (iprh->end > iprh_tmp->start)) {
00365           /* fragment overlaps with previous or following, throw away */
00366           goto freepbuf;
00367         }
00368 #endif /* IP_REASS_CHECK_OVERLAP */
00369         iprh_prev->next_pbuf = new_p;
00370       } else {
00371         /* fragment with the lowest offset */
00372         ipr->p = new_p;
00373       }
00374       break;
00375     } else if(iprh->start == iprh_tmp->start) {
00376       /* received the same datagram twice: no need to keep the datagram */
00377       goto freepbuf;
00378 #if IP_REASS_CHECK_OVERLAP
00379     } else if(iprh->start < iprh_tmp->end) {
00380       /* overlap: no need to keep the new datagram */
00381       goto freepbuf;
00382 #endif /* IP_REASS_CHECK_OVERLAP */
00383     } else {
00384       /* Check if the fragments received so far have no wholes. */
00385       if (iprh_prev != NULL) {
00386         if (iprh_prev->end != iprh_tmp->start) {
00387           /* There is a fragment missing between the current
00388            * and the previous fragment */
00389           valid = 0;
00390         }
00391       }
00392     }
00393     q = iprh_tmp->next_pbuf;
00394     iprh_prev = iprh_tmp;
00395   }
00396 
00397   /* If q is NULL, then we made it to the end of the list. Determine what to do now */
00398   if (q == NULL) {
00399     if (iprh_prev != NULL) {
00400       /* this is (for now), the fragment with the highest offset:
00401        * chain it to the last fragment */
00402 #if IP_REASS_CHECK_OVERLAP
00403       LWIP_ASSERT("check fragments don't overlap", iprh_prev->end <= iprh->start);
00404 #endif /* IP_REASS_CHECK_OVERLAP */
00405       iprh_prev->next_pbuf = new_p;
00406       if (iprh_prev->end != iprh->start) {
00407         valid = 0;
00408       }
00409     } else {
00410 #if IP_REASS_CHECK_OVERLAP
00411       LWIP_ASSERT("no previous fragment, this must be the first fragment!",
00412         ipr->p == NULL);
00413 #endif /* IP_REASS_CHECK_OVERLAP */
00414       /* this is the first fragment we ever received for this ip datagram */
00415       ipr->p = new_p;
00416     }
00417   }
00418 
00419   /* At this point, the validation part begins: */
00420   /* If we already received the last fragment */
00421   if ((ipr->flags & IP_REASS_FLAG_LASTFRAG) != 0) {
00422     /* and had no wholes so far */
00423     if (valid) {
00424       /* then check if the rest of the fragments is here */
00425       /* Check if the queue starts with the first datagram */
00426       if (((struct ip_reass_helper*)ipr->p->payload)->start != 0) {
00427         valid = 0;
00428       } else {
00429         /* and check that there are no wholes after this datagram */
00430         iprh_prev = iprh;
00431         q = iprh->next_pbuf;
00432         while (q != NULL) {
00433           iprh = (struct ip_reass_helper*)q->payload;
00434           if (iprh_prev->end != iprh->start) {
00435             valid = 0;
00436             break;
00437           }
00438           iprh_prev = iprh;
00439           q = iprh->next_pbuf;
00440         }
00441         /* if still valid, all fragments are received
00442          * (because to the MF==0 already arrived */
00443         if (valid) {
00444           LWIP_ASSERT("sanity check", ipr->p != NULL);
00445           LWIP_ASSERT("sanity check",
00446             ((struct ip_reass_helper*)ipr->p->payload) != iprh);
00447           LWIP_ASSERT("validate_datagram:next_pbuf!=NULL",
00448             iprh->next_pbuf == NULL);
00449           LWIP_ASSERT("validate_datagram:datagram end!=datagram len",
00450             iprh->end == ipr->datagram_len);
00451         }
00452       }
00453     }
00454     /* If valid is 0 here, there are some fragments missing in the middle
00455      * (since MF == 0 has already arrived). Such datagrams simply time out if
00456      * no more fragments are received... */
00457     return valid;
00458   }
00459   /* If we come here, not all fragments were received, yet! */
00460   return 0; /* not yet valid! */
00461 #if IP_REASS_CHECK_OVERLAP
00462 freepbuf:
00463   ip_reass_pbufcount -= pbuf_clen(new_p);
00464   pbuf_free(new_p);
00465   return 0;
00466 #endif /* IP_REASS_CHECK_OVERLAP */
00467 }
00468 
00475 struct pbuf *
00476 ip_reass(struct pbuf *p)
00477 {
00478   struct pbuf *r;
00479   struct ip_hdr *fraghdr;
00480   struct ip_reassdata *ipr;
00481   struct ip_reass_helper *iprh;
00482   u16_t offset, len;
00483   u8_t clen;
00484   struct ip_reassdata *ipr_prev = NULL;
00485 
00486   IPFRAG_STATS_INC(ip_frag.recv);
00487   snmp_inc_ipreasmreqds();
00488 
00489   fraghdr = (struct ip_hdr*)p->payload;
00490 
00491   if ((IPH_HL(fraghdr) * 4) != IP_HLEN) {
00492     LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: IP options currently not supported!\n"));
00493     IPFRAG_STATS_INC(ip_frag.err);
00494     goto nullreturn;
00495   }
00496 
00497   offset = (ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8;
00498   len = ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4;
00499 
00500   /* Check if we are allowed to enqueue more datagrams. */
00501   clen = pbuf_clen(p);
00502   if ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS) {
00503 #if IP_REASS_FREE_OLDEST
00504     if (!ip_reass_remove_oldest_datagram(fraghdr, clen) ||
00505         ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS))
00506 #endif /* IP_REASS_FREE_OLDEST */
00507     {
00508       /* No datagram could be freed and still too many pbufs enqueued */
00509       LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: Overflow condition: pbufct=%d, clen=%d, MAX=%d\n",
00510         ip_reass_pbufcount, clen, IP_REASS_MAX_PBUFS));
00511       IPFRAG_STATS_INC(ip_frag.memerr);
00512       /* @todo: send ICMP time exceeded here? */
00513       /* drop this pbuf */
00514       goto nullreturn;
00515     }
00516   }
00517 
00518   /* Look for the datagram the fragment belongs to in the current datagram queue,
00519    * remembering the previous in the queue for later dequeueing. */
00520   for (ipr = reassdatagrams; ipr != NULL; ipr = ipr->next) {
00521     /* Check if the incoming fragment matches the one currently present
00522        in the reassembly buffer. If so, we proceed with copying the
00523        fragment into the buffer. */
00524     if (IP_ADDRESSES_AND_ID_MATCH(&ipr->iphdr, fraghdr)) {
00525       LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass: matching previous fragment ID=%"X16_F"\n",
00526         ntohs(IPH_ID(fraghdr))));
00527       IPFRAG_STATS_INC(ip_frag.cachehit);
00528       break;
00529     }
00530     ipr_prev = ipr;
00531   }
00532 
00533   if (ipr == NULL) {
00534   /* Enqueue a new datagram into the datagram queue */
00535     ipr = ip_reass_enqueue_new_datagram(fraghdr, clen);
00536     /* Bail if unable to enqueue */
00537     if(ipr == NULL) {
00538       goto nullreturn;
00539     }
00540   } else {
00541     if (((ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) == 0) && 
00542       ((ntohs(IPH_OFFSET(&ipr->iphdr)) & IP_OFFMASK) != 0)) {
00543       /* ipr->iphdr is not the header from the first fragment, but fraghdr is
00544        * -> copy fraghdr into ipr->iphdr since we want to have the header
00545        * of the first fragment (for ICMP time exceeded and later, for copying
00546        * all options, if supported)*/
00547       SMEMCPY(&ipr->iphdr, fraghdr, IP_HLEN);
00548     }
00549   }
00550   /* Track the current number of pbufs current 'in-flight', in order to limit 
00551   the number of fragments that may be enqueued at any one time */
00552   ip_reass_pbufcount += clen;
00553 
00554   /* At this point, we have either created a new entry or pointing 
00555    * to an existing one */
00556 
00557   /* check for 'no more fragments', and update queue entry*/
00558   if ((IPH_OFFSET(fraghdr) & PP_NTOHS(IP_MF)) == 0) {
00559     ipr->flags |= IP_REASS_FLAG_LASTFRAG;
00560     ipr->datagram_len = offset + len;
00561     LWIP_DEBUGF(IP_REASS_DEBUG,
00562      ("ip_reass: last fragment seen, total len %"S16_F"\n",
00563       ipr->datagram_len));
00564   }
00565   /* find the right place to insert this pbuf */
00566   /* @todo: trim pbufs if fragments are overlapping */
00567   if (ip_reass_chain_frag_into_datagram_and_validate(ipr, p)) {
00568     /* the totally last fragment (flag more fragments = 0) was received at least
00569      * once AND all fragments are received */
00570     ipr->datagram_len += IP_HLEN;
00571 
00572     /* save the second pbuf before copying the header over the pointer */
00573     r = ((struct ip_reass_helper*)ipr->p->payload)->next_pbuf;
00574 
00575     /* copy the original ip header back to the first pbuf */
00576     fraghdr = (struct ip_hdr*)(ipr->p->payload);
00577     SMEMCPY(fraghdr, &ipr->iphdr, IP_HLEN);
00578     IPH_LEN_SET(fraghdr, htons(ipr->datagram_len));
00579     IPH_OFFSET_SET(fraghdr, 0);
00580     IPH_CHKSUM_SET(fraghdr, 0);
00581     /* @todo: do we need to set calculate the correct checksum? */
00582     IPH_CHKSUM_SET(fraghdr, inet_chksum(fraghdr, IP_HLEN));
00583 
00584     p = ipr->p;
00585 
00586     /* chain together the pbufs contained within the reass_data list. */
00587     while(r != NULL) {
00588       iprh = (struct ip_reass_helper*)r->payload;
00589 
00590       /* hide the ip header for every succeding fragment */
00591       pbuf_header(r, -IP_HLEN);
00592       pbuf_cat(p, r);
00593       r = iprh->next_pbuf;
00594     }
00595     /* release the sources allocate for the fragment queue entry */
00596     ip_reass_dequeue_datagram(ipr, ipr_prev);
00597 
00598     /* and adjust the number of pbufs currently queued for reassembly. */
00599     ip_reass_pbufcount -= pbuf_clen(p);
00600 
00601     /* Return the pbuf chain */
00602     return p;
00603   }
00604   /* the datagram is not (yet?) reassembled completely */
00605   LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass_pbufcount: %d out\n", ip_reass_pbufcount));
00606   return NULL;
00607 
00608 nullreturn:
00609   LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: nullreturn\n"));
00610   IPFRAG_STATS_INC(ip_frag.drop);
00611   pbuf_free(p);
00612   return NULL;
00613 }
00614 #endif /* IP_REASSEMBLY */
00615 
00616 #if IP_FRAG
00617 #if IP_FRAG_USES_STATIC_BUF
00618 static u8_t buf[LWIP_MEM_ALIGN_SIZE(IP_FRAG_MAX_MTU + MEM_ALIGNMENT - 1)];
00619 #else /* IP_FRAG_USES_STATIC_BUF */
00620 
00621 #if !LWIP_NETIF_TX_SINGLE_PBUF
00622 
00623 static struct pbuf_custom_ref*
00624 ip_frag_alloc_pbuf_custom_ref(void)
00625 {
00626   return (struct pbuf_custom_ref*)memp_malloc(MEMP_FRAG_PBUF);
00627 }
00628 
00630 static void
00631 ip_frag_free_pbuf_custom_ref(struct pbuf_custom_ref* p)
00632 {
00633   LWIP_ASSERT("p != NULL", p != NULL);
00634   memp_free(MEMP_FRAG_PBUF, p);
00635 }
00636 
00639 static void
00640 ipfrag_free_pbuf_custom(struct pbuf *p)
00641 {
00642   struct pbuf_custom_ref *pcr = (struct pbuf_custom_ref*)p;
00643   LWIP_ASSERT("pcr != NULL", pcr != NULL);
00644   LWIP_ASSERT("pcr == p", (void*)pcr == (void*)p);
00645   if (pcr->original != NULL) {
00646     pbuf_free(pcr->original);
00647   }
00648   ip_frag_free_pbuf_custom_ref(pcr);
00649 }
00650 #endif /* !LWIP_NETIF_TX_SINGLE_PBUF */
00651 #endif /* IP_FRAG_USES_STATIC_BUF */
00652 
00666 err_t 
00667 ip_frag(struct pbuf *p, struct netif *netif, ip_addr_t *dest)
00668 {
00669   struct pbuf *rambuf;
00670 #if IP_FRAG_USES_STATIC_BUF
00671   struct pbuf *header;
00672 #else
00673 #if !LWIP_NETIF_TX_SINGLE_PBUF
00674   struct pbuf *newpbuf;
00675 #endif
00676   struct ip_hdr *original_iphdr;
00677 #endif
00678   struct ip_hdr *iphdr;
00679   u16_t nfb;
00680   u16_t left, cop;
00681   u16_t mtu = netif->mtu;
00682   u16_t ofo, omf;
00683   u16_t last;
00684   u16_t poff = IP_HLEN;
00685   u16_t tmp;
00686 #if !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF
00687   u16_t newpbuflen = 0;
00688   u16_t left_to_copy;
00689 #endif
00690 
00691   /* Get a RAM based MTU sized pbuf */
00692 #if IP_FRAG_USES_STATIC_BUF
00693   /* When using a static buffer, we use a PBUF_REF, which we will
00694    * use to reference the packet (without link header).
00695    * Layer and length is irrelevant.
00696    */
00697   rambuf = pbuf_alloc(PBUF_LINK, 0, PBUF_REF);
00698   if (rambuf == NULL) {
00699     LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_frag: pbuf_alloc(PBUF_LINK, 0, PBUF_REF) failed\n"));
00700     return ERR_MEM;
00701   }
00702   rambuf->tot_len = rambuf->len = mtu;
00703   rambuf->payload = LWIP_MEM_ALIGN((void *)buf);
00704 
00705   /* Copy the IP header in it */
00706   iphdr = (struct ip_hdr *)rambuf->payload;
00707   SMEMCPY(iphdr, p->payload, IP_HLEN);
00708 #else /* IP_FRAG_USES_STATIC_BUF */
00709   original_iphdr = (struct ip_hdr *)p->payload;
00710   iphdr = original_iphdr;
00711 #endif /* IP_FRAG_USES_STATIC_BUF */
00712 
00713   /* Save original offset */
00714   tmp = ntohs(IPH_OFFSET(iphdr));
00715   ofo = tmp & IP_OFFMASK;
00716   omf = tmp & IP_MF;
00717 
00718   left = p->tot_len - IP_HLEN;
00719 
00720   nfb = (mtu - IP_HLEN) / 8;
00721 
00722   while (left) {
00723     last = (left <= mtu - IP_HLEN);
00724 
00725     /* Set new offset and MF flag */
00726     tmp = omf | (IP_OFFMASK & (ofo));
00727     if (!last) {
00728       tmp = tmp | IP_MF;
00729     }
00730 
00731     /* Fill this fragment */
00732     cop = last ? left : nfb * 8;
00733 
00734 #if IP_FRAG_USES_STATIC_BUF
00735     poff += pbuf_copy_partial(p, (u8_t*)iphdr + IP_HLEN, cop, poff);
00736 #else /* IP_FRAG_USES_STATIC_BUF */
00737 #if LWIP_NETIF_TX_SINGLE_PBUF
00738     rambuf = pbuf_alloc(PBUF_IP, cop, PBUF_RAM);
00739     if (rambuf == NULL) {
00740       return ERR_MEM;
00741     }
00742     LWIP_ASSERT("this needs a pbuf in one piece!",
00743       (rambuf->len == rambuf->tot_len) && (rambuf->next == NULL));
00744     poff += pbuf_copy_partial(p, rambuf->payload, cop, poff);
00745     /* make room for the IP header */
00746     if(pbuf_header(rambuf, IP_HLEN)) {
00747       pbuf_free(rambuf);
00748       return ERR_MEM;
00749     }
00750     /* fill in the IP header */
00751     SMEMCPY(rambuf->payload, original_iphdr, IP_HLEN);
00752     iphdr = rambuf->payload;
00753 #else /* LWIP_NETIF_TX_SINGLE_PBUF */
00754     /* When not using a static buffer, create a chain of pbufs.
00755      * The first will be a PBUF_RAM holding the link and IP header.
00756      * The rest will be PBUF_REFs mirroring the pbuf chain to be fragged,
00757      * but limited to the size of an mtu.
00758      */
00759     rambuf = pbuf_alloc(PBUF_LINK, IP_HLEN, PBUF_RAM);
00760     if (rambuf == NULL) {
00761       return ERR_MEM;
00762     }
00763     LWIP_ASSERT("this needs a pbuf in one piece!",
00764                 (p->len >= (IP_HLEN)));
00765     SMEMCPY(rambuf->payload, original_iphdr, IP_HLEN);
00766     iphdr = (struct ip_hdr *)rambuf->payload;
00767 
00768     /* Can just adjust p directly for needed offset. */
00769     p->payload = (u8_t *)p->payload + poff;
00770     p->len -= poff;
00771 
00772     left_to_copy = cop;
00773     while (left_to_copy) {
00774       struct pbuf_custom_ref *pcr;
00775       newpbuflen = (left_to_copy < p->len) ? left_to_copy : p->len;
00776       /* Is this pbuf already empty? */
00777       if (!newpbuflen) {
00778         p = p->next;
00779         continue;
00780       }
00781       pcr = ip_frag_alloc_pbuf_custom_ref();
00782       if (pcr == NULL) {
00783         pbuf_free(rambuf);
00784         return ERR_MEM;
00785       }
00786       /* Mirror this pbuf, although we might not need all of it. */
00787       newpbuf = pbuf_alloced_custom(PBUF_RAW, newpbuflen, PBUF_REF, &pcr->pc, p->payload, newpbuflen);
00788       if (newpbuf == NULL) {
00789         ip_frag_free_pbuf_custom_ref(pcr);
00790         pbuf_free(rambuf);
00791         return ERR_MEM;
00792       }
00793       pbuf_ref(p);
00794       pcr->original = p;
00795       pcr->pc.custom_free_function = ipfrag_free_pbuf_custom;
00796 
00797       /* Add it to end of rambuf's chain, but using pbuf_cat, not pbuf_chain
00798        * so that it is removed when pbuf_dechain is later called on rambuf.
00799        */
00800       pbuf_cat(rambuf, newpbuf);
00801       left_to_copy -= newpbuflen;
00802       if (left_to_copy) {
00803         p = p->next;
00804       }
00805     }
00806     poff = newpbuflen;
00807 #endif /* LWIP_NETIF_TX_SINGLE_PBUF */
00808 #endif /* IP_FRAG_USES_STATIC_BUF */
00809 
00810     /* Correct header */
00811     IPH_OFFSET_SET(iphdr, htons(tmp));
00812     IPH_LEN_SET(iphdr, htons(cop + IP_HLEN));
00813     IPH_CHKSUM_SET(iphdr, 0);
00814     IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN));
00815 
00816 #if IP_FRAG_USES_STATIC_BUF
00817     if (last) {
00818       pbuf_realloc(rambuf, left + IP_HLEN);
00819     }
00820 
00821     /* This part is ugly: we alloc a RAM based pbuf for 
00822      * the link level header for each chunk and then 
00823      * free it.A PBUF_ROM style pbuf for which pbuf_header
00824      * worked would make things simpler.
00825      */
00826     header = pbuf_alloc(PBUF_LINK, 0, PBUF_RAM);
00827     if (header != NULL) {
00828       pbuf_chain(header, rambuf);
00829       netif->output(netif, header, dest);
00830       IPFRAG_STATS_INC(ip_frag.xmit);
00831       snmp_inc_ipfragcreates();
00832       pbuf_free(header);
00833     } else {
00834       LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_frag: pbuf_alloc() for header failed\n"));
00835       pbuf_free(rambuf);
00836       return ERR_MEM;
00837     }
00838 #else /* IP_FRAG_USES_STATIC_BUF */
00839     /* No need for separate header pbuf - we allowed room for it in rambuf
00840      * when allocated.
00841      */
00842     netif->output(netif, rambuf, dest);
00843     IPFRAG_STATS_INC(ip_frag.xmit);
00844 
00845     /* Unfortunately we can't reuse rambuf - the hardware may still be
00846      * using the buffer. Instead we free it (and the ensuing chain) and
00847      * recreate it next time round the loop. If we're lucky the hardware
00848      * will have already sent the packet, the free will really free, and
00849      * there will be zero memory penalty.
00850      */
00851     
00852     pbuf_free(rambuf);
00853 #endif /* IP_FRAG_USES_STATIC_BUF */
00854     left -= cop;
00855     ofo += nfb;
00856   }
00857 #if IP_FRAG_USES_STATIC_BUF
00858   pbuf_free(rambuf);
00859 #endif /* IP_FRAG_USES_STATIC_BUF */
00860   snmp_inc_ipfragoks();
00861   return ERR_OK;
00862 }
00863 #endif /* IP_FRAG */

Generated on Sat May 26 2012 04:34:57 for ReactOS by doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.