Rtos API example

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers lwip_ip6_frag.c Source File

lwip_ip6_frag.c

Go to the documentation of this file.
00001 /**
00002  * @file
00003  *
00004  * IPv6 fragmentation and reassembly.
00005  */
00006 
00007 /*
00008  * Copyright (c) 2010 Inico Technologies Ltd.
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: Ivan Delamer <delamer@inicotech.com>
00036  *
00037  *
00038  * Please coordinate changes and requests with Ivan Delamer
00039  * <delamer@inicotech.com>
00040  */
00041 
00042 #include "lwip/opt.h"
00043 #include "lwip/ip6_frag.h"
00044 #include "lwip/ip6.h"
00045 #include "lwip/icmp6.h"
00046 #include "lwip/nd6.h"
00047 #include "lwip/ip.h"
00048 
00049 #include "lwip/pbuf.h"
00050 #include "lwip/memp.h"
00051 #include "lwip/stats.h"
00052 
00053 #include <string.h>
00054 
00055 #if LWIP_IPV6 && LWIP_IPV6_REASS  /* don't build if not configured for use in lwipopts.h */
00056 
00057 
00058 /** Setting this to 0, you can turn off checking the fragments for overlapping
00059  * regions. The code gets a little smaller. Only use this if you know that
00060  * overlapping won't occur on your network! */
00061 #ifndef IP_REASS_CHECK_OVERLAP
00062 #define IP_REASS_CHECK_OVERLAP 1
00063 #endif /* IP_REASS_CHECK_OVERLAP */
00064 
00065 /** Set to 0 to prevent freeing the oldest datagram when the reassembly buffer is
00066  * full (IP_REASS_MAX_PBUFS pbufs are enqueued). The code gets a little smaller.
00067  * Datagrams will be freed by timeout only. Especially useful when MEMP_NUM_REASSDATA
00068  * is set to 1, so one datagram can be reassembled at a time, only. */
00069 #ifndef IP_REASS_FREE_OLDEST
00070 #define IP_REASS_FREE_OLDEST 1
00071 #endif /* IP_REASS_FREE_OLDEST */
00072 
00073 #if IPV6_FRAG_COPYHEADER
00074 #define IPV6_FRAG_REQROOM ((s16_t)(sizeof(struct ip6_reass_helper) - IP6_FRAG_HLEN))
00075 #endif
00076 
00077 #define IP_REASS_FLAG_LASTFRAG 0x01
00078 
00079 /** This is a helper struct which holds the starting
00080  * offset and the ending offset of this fragment to
00081  * easily chain the fragments.
00082  * It has the same packing requirements as the IPv6 header, since it replaces
00083  * the Fragment Header in memory in incoming fragments to keep
00084  * track of the various fragments.
00085  */
00086 #ifdef PACK_STRUCT_USE_INCLUDES
00087 #  include "arch/bpstruct.h"
00088 #endif
00089 PACK_STRUCT_BEGIN
00090 struct ip6_reass_helper {
00091   PACK_STRUCT_FIELD(struct pbuf *next_pbuf);
00092   PACK_STRUCT_FIELD(u16_t start);
00093   PACK_STRUCT_FIELD(u16_t end);
00094 } PACK_STRUCT_STRUCT;
00095 PACK_STRUCT_END
00096 #ifdef PACK_STRUCT_USE_INCLUDES
00097 #  include "arch/epstruct.h"
00098 #endif
00099 
00100 /* static variables */
00101 static struct ip6_reassdata *reassdatagrams;
00102 static u16_t ip6_reass_pbufcount;
00103 
00104 /* Forward declarations. */
00105 static void ip6_reass_free_complete_datagram(struct ip6_reassdata *ipr);
00106 #if IP_REASS_FREE_OLDEST
00107 static void ip6_reass_remove_oldest_datagram(struct ip6_reassdata *ipr, int pbufs_needed);
00108 #endif /* IP_REASS_FREE_OLDEST */
00109 
00110 void
00111 ip6_reass_tmr(void)
00112 {
00113   struct ip6_reassdata *r, *tmp;
00114 
00115 #if !IPV6_FRAG_COPYHEADER
00116   LWIP_ASSERT("sizeof(struct ip6_reass_helper) <= IP6_FRAG_HLEN, set IPV6_FRAG_COPYHEADER to 1",
00117     sizeof(struct ip6_reass_helper) <= IP6_FRAG_HLEN);
00118 #endif /* !IPV6_FRAG_COPYHEADER */
00119 
00120   r = reassdatagrams;
00121   while (r != NULL) {
00122     /* Decrement the timer. Once it reaches 0,
00123      * clean up the incomplete fragment assembly */
00124     if (r->timer > 0) {
00125       r->timer--;
00126       r = r->next;
00127     } else {
00128       /* reassembly timed out */
00129       tmp = r;
00130       /* get the next pointer before freeing */
00131       r = r->next;
00132       /* free the helper struct and all enqueued pbufs */
00133       ip6_reass_free_complete_datagram(tmp);
00134      }
00135    }
00136 }
00137 
00138 /**
00139  * Free a datagram (struct ip6_reassdata) and all its pbufs.
00140  * Updates the total count of enqueued pbufs (ip6_reass_pbufcount),
00141  * sends an ICMP time exceeded packet.
00142  *
00143  * @param ipr datagram to free
00144  */
00145 static void
00146 ip6_reass_free_complete_datagram(struct ip6_reassdata *ipr)
00147 {
00148   struct ip6_reassdata *prev;
00149   u16_t pbufs_freed = 0;
00150   u16_t clen;
00151   struct pbuf *p;
00152   struct ip6_reass_helper *iprh;
00153 
00154 #if LWIP_ICMP6
00155   iprh = (struct ip6_reass_helper *)ipr->p->payload;
00156   if (iprh->start == 0) {
00157     /* The first fragment was received, send ICMP time exceeded. */
00158     /* First, de-queue the first pbuf from r->p. */
00159     p = ipr->p;
00160     ipr->p = iprh->next_pbuf;
00161     /* Then, move back to the original ipv6 header (we are now pointing to Fragment header).
00162        This cannot fail since we already checked when receiving this fragment. */
00163     if (pbuf_header_force(p, (s16_t)((u8_t*)p->payload - (u8_t*)IPV6_FRAG_HDRREF(ipr->iphdr)))) {
00164       LWIP_ASSERT("ip6_reass_free: moving p->payload to ip6 header failed\n", 0);
00165     }
00166     else {
00167       icmp6_time_exceeded(p, ICMP6_TE_FRAG);
00168     }
00169     clen = pbuf_clen(p);
00170     LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff);
00171     pbufs_freed += clen;
00172     pbuf_free(p);
00173   }
00174 #endif /* LWIP_ICMP6 */
00175 
00176   /* First, free all received pbufs.  The individual pbufs need to be released
00177      separately as they have not yet been chained */
00178   p = ipr->p;
00179   while (p != NULL) {
00180     struct pbuf *pcur;
00181     iprh = (struct ip6_reass_helper *)p->payload;
00182     pcur = p;
00183     /* get the next pointer before freeing */
00184     p = iprh->next_pbuf;
00185     clen = pbuf_clen(pcur);
00186     LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff);
00187     pbufs_freed += clen;
00188     pbuf_free(pcur);
00189   }
00190 
00191   /* Then, unchain the struct ip6_reassdata from the list and free it. */
00192   if (ipr == reassdatagrams) {
00193     reassdatagrams = ipr->next;
00194   } else {
00195     prev = reassdatagrams;
00196     while (prev != NULL) {
00197       if (prev->next == ipr) {
00198         break;
00199       }
00200       prev = prev->next;
00201     }
00202     if (prev != NULL) {
00203       prev->next = ipr->next;
00204     }
00205   }
00206   memp_free(MEMP_IP6_REASSDATA, ipr);
00207 
00208   /* Finally, update number of pbufs in reassembly queue */
00209   LWIP_ASSERT("ip_reass_pbufcount >= clen", ip6_reass_pbufcount >= pbufs_freed);
00210   ip6_reass_pbufcount -= pbufs_freed;
00211 }
00212 
00213 #if IP_REASS_FREE_OLDEST
00214 /**
00215  * Free the oldest datagram to make room for enqueueing new fragments.
00216  * The datagram ipr is not freed!
00217  *
00218  * @param ipr ip6_reassdata for the current fragment
00219  * @param pbufs_needed number of pbufs needed to enqueue
00220  *        (used for freeing other datagrams if not enough space)
00221  */
00222 static void
00223 ip6_reass_remove_oldest_datagram(struct ip6_reassdata *ipr, int pbufs_needed)
00224 {
00225   struct ip6_reassdata *r, *oldest;
00226 
00227   /* Free datagrams until being allowed to enqueue 'pbufs_needed' pbufs,
00228    * but don't free the current datagram! */
00229   do {
00230     r = oldest = reassdatagrams;
00231     while (r != NULL) {
00232       if (r != ipr) {
00233         if (r->timer <= oldest->timer) {
00234           /* older than the previous oldest */
00235           oldest = r;
00236         }
00237       }
00238       r = r->next;
00239     }
00240     if (oldest == ipr) {
00241       /* nothing to free, ipr is the only element on the list */
00242       return;
00243     }
00244     if (oldest != NULL) {
00245       ip6_reass_free_complete_datagram(oldest);
00246     }
00247   } while (((ip6_reass_pbufcount + pbufs_needed) > IP_REASS_MAX_PBUFS) && (reassdatagrams != NULL));
00248 }
00249 #endif /* IP_REASS_FREE_OLDEST */
00250 
00251 /**
00252  * Reassembles incoming IPv6 fragments into an IPv6 datagram.
00253  *
00254  * @param p points to the IPv6 Fragment Header
00255  * @return NULL if reassembly is incomplete, pbuf pointing to
00256  *         IPv6 Header if reassembly is complete
00257  */
00258 struct pbuf *
00259 ip6_reass(struct pbuf *p)
00260 {
00261   struct ip6_reassdata *ipr, *ipr_prev;
00262   struct ip6_reass_helper *iprh, *iprh_tmp, *iprh_prev=NULL;
00263   struct ip6_frag_hdr *frag_hdr;
00264   u16_t offset, len;
00265   u16_t clen;
00266   u8_t valid = 1;
00267   struct pbuf *q;
00268 
00269   IP6_FRAG_STATS_INC(ip6_frag.recv);
00270 
00271   if ((const void*)ip6_current_header() != ((u8_t*)p->payload) - IP6_HLEN) {
00272     /* ip6_frag_hdr must be in the first pbuf, not chained */
00273     IP6_FRAG_STATS_INC(ip6_frag.proterr);
00274     IP6_FRAG_STATS_INC(ip6_frag.drop);
00275     goto nullreturn;
00276   }
00277 
00278   frag_hdr = (struct ip6_frag_hdr *) p->payload;
00279 
00280   clen = pbuf_clen(p);
00281 
00282   offset = lwip_ntohs(frag_hdr->_fragment_offset);
00283 
00284   /* Calculate fragment length from IPv6 payload length.
00285    * Adjust for headers before Fragment Header.
00286    * And finally adjust by Fragment Header length. */
00287   len = lwip_ntohs(ip6_current_header()->_plen);
00288   len -= (u16_t)(((u8_t*)p->payload - (const u8_t*)ip6_current_header()) - IP6_HLEN);
00289   len -= IP6_FRAG_HLEN;
00290 
00291   /* Look for the datagram the fragment belongs to in the current datagram queue,
00292    * remembering the previous in the queue for later dequeueing. */
00293   for (ipr = reassdatagrams, ipr_prev = NULL; ipr != NULL; ipr = ipr->next) {
00294     /* Check if the incoming fragment matches the one currently present
00295        in the reassembly buffer. If so, we proceed with copying the
00296        fragment into the buffer. */
00297     if ((frag_hdr->_identification == ipr->identification) &&
00298         ip6_addr_cmp(ip6_current_src_addr(), &(IPV6_FRAG_HDRREF(ipr->iphdr)->src)) &&
00299         ip6_addr_cmp(ip6_current_dest_addr(), &(IPV6_FRAG_HDRREF(ipr->iphdr)->dest))) {
00300       IP6_FRAG_STATS_INC(ip6_frag.cachehit);
00301       break;
00302     }
00303     ipr_prev = ipr;
00304   }
00305 
00306   if (ipr == NULL) {
00307   /* Enqueue a new datagram into the datagram queue */
00308     ipr = (struct ip6_reassdata *)memp_malloc(MEMP_IP6_REASSDATA);
00309     if (ipr == NULL) {
00310 #if IP_REASS_FREE_OLDEST
00311       /* Make room and try again. */
00312       ip6_reass_remove_oldest_datagram(ipr, clen);
00313       ipr = (struct ip6_reassdata *)memp_malloc(MEMP_IP6_REASSDATA);
00314       if (ipr != NULL) {
00315         /* re-search ipr_prev since it might have been removed */
00316         for (ipr_prev = reassdatagrams; ipr_prev != NULL; ipr_prev = ipr_prev->next) {
00317           if (ipr_prev->next == ipr) {
00318             break;
00319           }
00320         }
00321       } else
00322 #endif /* IP_REASS_FREE_OLDEST */
00323       {
00324         IP6_FRAG_STATS_INC(ip6_frag.memerr);
00325         IP6_FRAG_STATS_INC(ip6_frag.drop);
00326         goto nullreturn;
00327       }
00328     }
00329 
00330     memset(ipr, 0, sizeof(struct ip6_reassdata));
00331     ipr->timer = IP_REASS_MAXAGE;
00332 
00333     /* enqueue the new structure to the front of the list */
00334     ipr->next = reassdatagrams;
00335     reassdatagrams = ipr;
00336 
00337     /* Use the current IPv6 header for src/dest address reference.
00338      * Eventually, we will replace it when we get the first fragment
00339      * (it might be this one, in any case, it is done later). */
00340 #if IPV6_FRAG_COPYHEADER
00341     MEMCPY(&ipr->iphdr, ip6_current_header(), IP6_HLEN);
00342 #else /* IPV6_FRAG_COPYHEADER */
00343     /* need to use the none-const pointer here: */
00344     ipr->iphdr = ip_data.current_ip6_header;
00345 #endif /* IPV6_FRAG_COPYHEADER */
00346 
00347     /* copy the fragmented packet id. */
00348     ipr->identification = frag_hdr->_identification;
00349 
00350     /* copy the nexth field */
00351     ipr->nexth = frag_hdr->_nexth;
00352   }
00353 
00354   /* Check if we are allowed to enqueue more datagrams. */
00355   if ((ip6_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS) {
00356 #if IP_REASS_FREE_OLDEST
00357     ip6_reass_remove_oldest_datagram(ipr, clen);
00358     if ((ip6_reass_pbufcount + clen) <= IP_REASS_MAX_PBUFS) {
00359       /* re-search ipr_prev since it might have been removed */
00360       for (ipr_prev = reassdatagrams; ipr_prev != NULL; ipr_prev = ipr_prev->next) {
00361         if (ipr_prev->next == ipr) {
00362           break;
00363         }
00364       }
00365     } else
00366 #endif /* IP_REASS_FREE_OLDEST */
00367     {
00368       /* @todo: send ICMPv6 time exceeded here? */
00369       /* drop this pbuf */
00370       IP6_FRAG_STATS_INC(ip6_frag.memerr);
00371       IP6_FRAG_STATS_INC(ip6_frag.drop);
00372       goto nullreturn;
00373     }
00374   }
00375 
00376   /* Overwrite Fragment Header with our own helper struct. */
00377 #if IPV6_FRAG_COPYHEADER
00378   if (IPV6_FRAG_REQROOM > 0) {
00379     /* Make room for struct ip6_reass_helper (only required if sizeof(void*) > 4).
00380        This cannot fail since we already checked when receiving this fragment. */
00381     u8_t hdrerr = pbuf_header_force(p, IPV6_FRAG_REQROOM);
00382     LWIP_UNUSED_ARG(hdrerr); /* in case of LWIP_NOASSERT */
00383     LWIP_ASSERT("no room for struct ip6_reass_helper", hdrerr == 0);
00384   }
00385 #else /* IPV6_FRAG_COPYHEADER */
00386   LWIP_ASSERT("sizeof(struct ip6_reass_helper) <= IP6_FRAG_HLEN, set IPV6_FRAG_COPYHEADER to 1",
00387     sizeof(struct ip6_reass_helper) <= IP6_FRAG_HLEN);
00388 #endif /* IPV6_FRAG_COPYHEADER */
00389   iprh = (struct ip6_reass_helper *)p->payload;
00390   iprh->next_pbuf = NULL;
00391   iprh->start = (offset & IP6_FRAG_OFFSET_MASK);
00392   iprh->end = (offset & IP6_FRAG_OFFSET_MASK) + len;
00393 
00394   /* find the right place to insert this pbuf */
00395   /* Iterate through until we either get to the end of the list (append),
00396    * or we find on with a larger offset (insert). */
00397   for (q = ipr->p; q != NULL;) {
00398     iprh_tmp = (struct ip6_reass_helper*)q->payload;
00399     if (iprh->start < iprh_tmp->start) {
00400 #if IP_REASS_CHECK_OVERLAP
00401       if (iprh->end > iprh_tmp->start) {
00402         /* fragment overlaps with following, throw away */
00403         IP6_FRAG_STATS_INC(ip6_frag.proterr);
00404         IP6_FRAG_STATS_INC(ip6_frag.drop);
00405         goto nullreturn;
00406       }
00407       if (iprh_prev != NULL) {
00408         if (iprh->start < iprh_prev->end) {
00409           /* fragment overlaps with previous, throw away */
00410           IP6_FRAG_STATS_INC(ip6_frag.proterr);
00411           IP6_FRAG_STATS_INC(ip6_frag.drop);
00412           goto nullreturn;
00413         }
00414       }
00415 #endif /* IP_REASS_CHECK_OVERLAP */
00416       /* the new pbuf should be inserted before this */
00417       iprh->next_pbuf = q;
00418       if (iprh_prev != NULL) {
00419         /* not the fragment with the lowest offset */
00420         iprh_prev->next_pbuf = p;
00421       } else {
00422         /* fragment with the lowest offset */
00423         ipr->p = p;
00424       }
00425       break;
00426     } else if (iprh->start == iprh_tmp->start) {
00427       /* received the same datagram twice: no need to keep the datagram */
00428       IP6_FRAG_STATS_INC(ip6_frag.drop);
00429       goto nullreturn;
00430 #if IP_REASS_CHECK_OVERLAP
00431     } else if (iprh->start < iprh_tmp->end) {
00432       /* overlap: no need to keep the new datagram */
00433       IP6_FRAG_STATS_INC(ip6_frag.proterr);
00434       IP6_FRAG_STATS_INC(ip6_frag.drop);
00435       goto nullreturn;
00436 #endif /* IP_REASS_CHECK_OVERLAP */
00437     } else {
00438       /* Check if the fragments received so far have no gaps. */
00439       if (iprh_prev != NULL) {
00440         if (iprh_prev->end != iprh_tmp->start) {
00441           /* There is a fragment missing between the current
00442            * and the previous fragment */
00443           valid = 0;
00444         }
00445       }
00446     }
00447     q = iprh_tmp->next_pbuf;
00448     iprh_prev = iprh_tmp;
00449   }
00450 
00451   /* If q is NULL, then we made it to the end of the list. Determine what to do now */
00452   if (q == NULL) {
00453     if (iprh_prev != NULL) {
00454       /* this is (for now), the fragment with the highest offset:
00455        * chain it to the last fragment */
00456 #if IP_REASS_CHECK_OVERLAP
00457       LWIP_ASSERT("check fragments don't overlap", iprh_prev->end <= iprh->start);
00458 #endif /* IP_REASS_CHECK_OVERLAP */
00459       iprh_prev->next_pbuf = p;
00460       if (iprh_prev->end != iprh->start) {
00461         valid = 0;
00462       }
00463     } else {
00464 #if IP_REASS_CHECK_OVERLAP
00465       LWIP_ASSERT("no previous fragment, this must be the first fragment!",
00466         ipr->p == NULL);
00467 #endif /* IP_REASS_CHECK_OVERLAP */
00468       /* this is the first fragment we ever received for this ip datagram */
00469       ipr->p = p;
00470     }
00471   }
00472 
00473   /* Track the current number of pbufs current 'in-flight', in order to limit
00474   the number of fragments that may be enqueued at any one time */
00475   ip6_reass_pbufcount += clen;
00476 
00477   /* Remember IPv6 header if this is the first fragment. */
00478   if (iprh->start == 0) {
00479 #if IPV6_FRAG_COPYHEADER
00480     if (iprh->next_pbuf != NULL) {
00481       MEMCPY(&ipr->iphdr, ip6_current_header(), IP6_HLEN);
00482     }
00483 #else /* IPV6_FRAG_COPYHEADER */
00484     /* need to use the none-const pointer here: */
00485     ipr->iphdr = ip_data.current_ip6_header;
00486 #endif /* IPV6_FRAG_COPYHEADER */
00487   }
00488 
00489   /* If this is the last fragment, calculate total packet length. */
00490   if ((offset & IP6_FRAG_MORE_FLAG) == 0) {
00491     ipr->datagram_len = iprh->end;
00492   }
00493 
00494   /* Additional validity tests: we have received first and last fragment. */
00495   iprh_tmp = (struct ip6_reass_helper*)ipr->p->payload;
00496   if (iprh_tmp->start != 0) {
00497     valid = 0;
00498   }
00499   if (ipr->datagram_len == 0) {
00500     valid = 0;
00501   }
00502 
00503   /* Final validity test: no gaps between current and last fragment. */
00504   iprh_prev = iprh;
00505   q = iprh->next_pbuf;
00506   while ((q != NULL) && valid) {
00507     iprh = (struct ip6_reass_helper*)q->payload;
00508     if (iprh_prev->end != iprh->start) {
00509       valid = 0;
00510       break;
00511     }
00512     iprh_prev = iprh;
00513     q = iprh->next_pbuf;
00514   }
00515 
00516   if (valid) {
00517     /* All fragments have been received */
00518     struct ip6_hdr* iphdr_ptr;
00519 
00520     /* chain together the pbufs contained within the ip6_reassdata list. */
00521     iprh = (struct ip6_reass_helper*) ipr->p->payload;
00522     while (iprh != NULL) {
00523       struct pbuf* next_pbuf = iprh->next_pbuf;
00524       if (next_pbuf != NULL) {
00525         /* Save next helper struct (will be hidden in next step). */
00526         iprh_tmp = (struct ip6_reass_helper*)next_pbuf->payload;
00527 
00528         /* hide the fragment header for every succeeding fragment */
00529         pbuf_header(next_pbuf, -IP6_FRAG_HLEN);
00530 #if IPV6_FRAG_COPYHEADER
00531         if (IPV6_FRAG_REQROOM > 0) {
00532           /* hide the extra bytes borrowed from ip6_hdr for struct ip6_reass_helper */
00533           u8_t hdrerr = pbuf_header(next_pbuf, -(s16_t)(IPV6_FRAG_REQROOM));
00534           LWIP_UNUSED_ARG(hdrerr); /* in case of LWIP_NOASSERT */
00535           LWIP_ASSERT("no room for struct ip6_reass_helper", hdrerr == 0);
00536         }
00537 #endif
00538         pbuf_cat(ipr->p, next_pbuf);
00539       }
00540       else {
00541         iprh_tmp = NULL;
00542       }
00543 
00544       iprh = iprh_tmp;
00545     }
00546 
00547 #if IPV6_FRAG_COPYHEADER
00548     if (IPV6_FRAG_REQROOM > 0) {
00549       /* get back room for struct ip6_reass_helper (only required if sizeof(void*) > 4) */
00550       u8_t hdrerr = pbuf_header(ipr->p, -(s16_t)(IPV6_FRAG_REQROOM));
00551       LWIP_UNUSED_ARG(hdrerr); /* in case of LWIP_NOASSERT */
00552       LWIP_ASSERT("no room for struct ip6_reass_helper", hdrerr == 0);
00553     }
00554     iphdr_ptr = (struct ip6_hdr*)((u8_t*)ipr->p->payload - IP6_HLEN);
00555     MEMCPY(iphdr_ptr, &ipr->iphdr, IP6_HLEN);
00556 #else
00557     iphdr_ptr = ipr->iphdr;
00558 #endif
00559 
00560     /* Adjust datagram length by adding header lengths. */
00561     ipr->datagram_len += (u16_t)(((u8_t*)ipr->p->payload - (u8_t*)iphdr_ptr)
00562                          + IP6_FRAG_HLEN
00563                          - IP6_HLEN);
00564 
00565     /* Set payload length in ip header. */
00566     iphdr_ptr->_plen = lwip_htons(ipr->datagram_len);
00567 
00568     /* Get the first pbuf. */
00569     p = ipr->p;
00570 
00571     /* Restore Fragment Header in first pbuf. Mark as "single fragment"
00572      * packet. Restore nexth. */
00573     frag_hdr = (struct ip6_frag_hdr *) p->payload;
00574     frag_hdr->_nexth = ipr->nexth;
00575     frag_hdr->reserved = 0;
00576     frag_hdr->_fragment_offset = 0;
00577     frag_hdr->_identification = 0;
00578 
00579     /* release the sources allocate for the fragment queue entry */
00580     if (reassdatagrams == ipr) {
00581       /* it was the first in the list */
00582       reassdatagrams = ipr->next;
00583     } else {
00584       /* it wasn't the first, so it must have a valid 'prev' */
00585       LWIP_ASSERT("sanity check linked list", ipr_prev != NULL);
00586       ipr_prev->next = ipr->next;
00587     }
00588     memp_free(MEMP_IP6_REASSDATA, ipr);
00589 
00590     /* adjust the number of pbufs currently queued for reassembly. */
00591     ip6_reass_pbufcount -= pbuf_clen(p);
00592 
00593     /* Move pbuf back to IPv6 header.
00594        This cannot fail since we already checked when receiving this fragment. */
00595     if (pbuf_header_force(p, (s16_t)((u8_t*)p->payload - (u8_t*)iphdr_ptr))) {
00596       LWIP_ASSERT("ip6_reass: moving p->payload to ip6 header failed\n", 0);
00597       pbuf_free(p);
00598       return NULL;
00599     }
00600 
00601     /* Return the pbuf chain */
00602     return p;
00603   }
00604   /* the datagram is not (yet?) reassembled completely */
00605   return NULL;
00606 
00607 nullreturn:
00608   pbuf_free(p);
00609   return NULL;
00610 }
00611 
00612 #endif /* LWIP_IPV6 && LWIP_IPV6_REASS */
00613 
00614 #if LWIP_IPV6 && LWIP_IPV6_FRAG
00615 
00616 #if !LWIP_NETIF_TX_SINGLE_PBUF
00617 /** Allocate a new struct pbuf_custom_ref */
00618 static struct pbuf_custom_ref*
00619 ip6_frag_alloc_pbuf_custom_ref(void)
00620 {
00621   return (struct pbuf_custom_ref*)memp_malloc(MEMP_FRAG_PBUF);
00622 }
00623 
00624 /** Free a struct pbuf_custom_ref */
00625 static void
00626 ip6_frag_free_pbuf_custom_ref(struct pbuf_custom_ref* p)
00627 {
00628   LWIP_ASSERT("p != NULL", p != NULL);
00629   memp_free(MEMP_FRAG_PBUF, p);
00630 }
00631 
00632 /** Free-callback function to free a 'struct pbuf_custom_ref', called by
00633  * pbuf_free. */
00634 static void
00635 ip6_frag_free_pbuf_custom(struct pbuf *p)
00636 {
00637   struct pbuf_custom_ref *pcr = (struct pbuf_custom_ref*)p;
00638   LWIP_ASSERT("pcr != NULL", pcr != NULL);
00639   LWIP_ASSERT("pcr == p", (void*)pcr == (void*)p);
00640   if (pcr->original != NULL) {
00641     pbuf_free(pcr->original);
00642   }
00643   ip6_frag_free_pbuf_custom_ref(pcr);
00644 }
00645 #endif /* !LWIP_NETIF_TX_SINGLE_PBUF */
00646 
00647 /**
00648  * Fragment an IPv6 datagram if too large for the netif or path MTU.
00649  *
00650  * Chop the datagram in MTU sized chunks and send them in order
00651  * by pointing PBUF_REFs into p
00652  *
00653  * @param p ipv6 packet to send
00654  * @param netif the netif on which to send
00655  * @param dest destination ipv6 address to which to send
00656  *
00657  * @return ERR_OK if sent successfully, err_t otherwise
00658  */
00659 err_t
00660 ip6_frag(struct pbuf *p, struct netif *netif, const ip6_addr_t *dest)
00661 {
00662   struct ip6_hdr *original_ip6hdr;
00663   struct ip6_hdr *ip6hdr;
00664   struct ip6_frag_hdr *frag_hdr;
00665   struct pbuf *rambuf;
00666 #if !LWIP_NETIF_TX_SINGLE_PBUF
00667   struct pbuf *newpbuf;
00668   u16_t newpbuflen = 0;
00669   u16_t left_to_copy;
00670 #endif
00671   static u32_t identification;
00672   u16_t nfb;
00673   u16_t left, cop;
00674   u16_t mtu;
00675   u16_t fragment_offset = 0;
00676   u16_t last;
00677   u16_t poff = IP6_HLEN;
00678 
00679   identification++;
00680 
00681   original_ip6hdr = (struct ip6_hdr *)p->payload;
00682 
00683   mtu = nd6_get_destination_mtu(dest, netif);
00684 
00685   /* @todo we assume there are no options in the unfragmentable part (IPv6 header). */
00686   left = p->tot_len - IP6_HLEN;
00687 
00688   nfb = (mtu - (IP6_HLEN + IP6_FRAG_HLEN)) & IP6_FRAG_OFFSET_MASK;
00689 
00690   while (left) {
00691     last = (left <= nfb);
00692 
00693     /* Fill this fragment */
00694     cop = last ? left : nfb;
00695 
00696 #if LWIP_NETIF_TX_SINGLE_PBUF
00697     rambuf = pbuf_alloc(PBUF_IP, cop + IP6_FRAG_HLEN, PBUF_RAM);
00698     if (rambuf == NULL) {
00699       IP6_FRAG_STATS_INC(ip6_frag.memerr);
00700       return ERR_MEM;
00701     }
00702     LWIP_ASSERT("this needs a pbuf in one piece!",
00703       (rambuf->len == rambuf->tot_len) && (rambuf->next == NULL));
00704     poff += pbuf_copy_partial(p, (u8_t*)rambuf->payload + IP6_FRAG_HLEN, cop, poff);
00705     /* make room for the IP header */
00706     if (pbuf_header(rambuf, IP6_HLEN)) {
00707       pbuf_free(rambuf);
00708       IP6_FRAG_STATS_INC(ip6_frag.memerr);
00709       return ERR_MEM;
00710     }
00711     /* fill in the IP header */
00712     SMEMCPY(rambuf->payload, original_ip6hdr, IP6_HLEN);
00713     ip6hdr = (struct ip6_hdr *)rambuf->payload;
00714     frag_hdr = (struct ip6_frag_hdr *)((u8_t*)rambuf->payload + IP6_HLEN);
00715 #else
00716     /* When not using a static buffer, create a chain of pbufs.
00717      * The first will be a PBUF_RAM holding the link, IPv6, and Fragment header.
00718      * The rest will be PBUF_REFs mirroring the pbuf chain to be fragged,
00719      * but limited to the size of an mtu.
00720      */
00721     rambuf = pbuf_alloc(PBUF_LINK, IP6_HLEN + IP6_FRAG_HLEN, PBUF_RAM);
00722     if (rambuf == NULL) {
00723       IP6_FRAG_STATS_INC(ip6_frag.memerr);
00724       return ERR_MEM;
00725     }
00726     LWIP_ASSERT("this needs a pbuf in one piece!",
00727                 (p->len >= (IP6_HLEN)));
00728     SMEMCPY(rambuf->payload, original_ip6hdr, IP6_HLEN);
00729     ip6hdr = (struct ip6_hdr *)rambuf->payload;
00730     frag_hdr = (struct ip6_frag_hdr *)((u8_t*)rambuf->payload + IP6_HLEN);
00731 
00732     /* Can just adjust p directly for needed offset. */
00733     p->payload = (u8_t *)p->payload + poff;
00734     p->len -= poff;
00735     p->tot_len -= poff;
00736 
00737     left_to_copy = cop;
00738     while (left_to_copy) {
00739       struct pbuf_custom_ref *pcr;
00740       newpbuflen = (left_to_copy < p->len) ? left_to_copy : p->len;
00741       /* Is this pbuf already empty? */
00742       if (!newpbuflen) {
00743         p = p->next;
00744         continue;
00745       }
00746       pcr = ip6_frag_alloc_pbuf_custom_ref();
00747       if (pcr == NULL) {
00748         pbuf_free(rambuf);
00749         IP6_FRAG_STATS_INC(ip6_frag.memerr);
00750         return ERR_MEM;
00751       }
00752       /* Mirror this pbuf, although we might not need all of it. */
00753       newpbuf = pbuf_alloced_custom(PBUF_RAW, newpbuflen, PBUF_REF, &pcr->pc, p->payload, newpbuflen);
00754       if (newpbuf == NULL) {
00755         ip6_frag_free_pbuf_custom_ref(pcr);
00756         pbuf_free(rambuf);
00757         IP6_FRAG_STATS_INC(ip6_frag.memerr);
00758         return ERR_MEM;
00759       }
00760       pbuf_ref(p);
00761       pcr->original = p;
00762       pcr->pc.custom_free_function = ip6_frag_free_pbuf_custom;
00763 
00764       /* Add it to end of rambuf's chain, but using pbuf_cat, not pbuf_chain
00765        * so that it is removed when pbuf_dechain is later called on rambuf.
00766        */
00767       pbuf_cat(rambuf, newpbuf);
00768       left_to_copy -= newpbuflen;
00769       if (left_to_copy) {
00770         p = p->next;
00771       }
00772     }
00773     poff = newpbuflen;
00774 #endif /* LWIP_NETIF_TX_SINGLE_PBUF */
00775 
00776     /* Set headers */
00777     frag_hdr->_nexth = original_ip6hdr->_nexth;
00778     frag_hdr->reserved = 0;
00779     frag_hdr->_fragment_offset = lwip_htons((fragment_offset & IP6_FRAG_OFFSET_MASK) | (last ? 0 : IP6_FRAG_MORE_FLAG));
00780     frag_hdr->_identification = lwip_htonl(identification);
00781 
00782     IP6H_NEXTH_SET(ip6hdr, IP6_NEXTH_FRAGMENT);
00783     IP6H_PLEN_SET(ip6hdr, cop + IP6_FRAG_HLEN);
00784 
00785     /* No need for separate header pbuf - we allowed room for it in rambuf
00786      * when allocated.
00787      */
00788     IP6_FRAG_STATS_INC(ip6_frag.xmit);
00789     netif->output_ip6(netif, rambuf, dest);
00790 
00791     /* Unfortunately we can't reuse rambuf - the hardware may still be
00792      * using the buffer. Instead we free it (and the ensuing chain) and
00793      * recreate it next time round the loop. If we're lucky the hardware
00794      * will have already sent the packet, the free will really free, and
00795      * there will be zero memory penalty.
00796      */
00797 
00798     pbuf_free(rambuf);
00799     left -= cop;
00800     fragment_offset += cop;
00801   }
00802   return ERR_OK;
00803 }
00804 
00805 #endif /* LWIP_IPV6 && LWIP_IPV6_FRAG */