ON Semiconductor / mbed-os

Dependents:   mbed-TFT-example-NCS36510 mbed-Accelerometer-example-NCS36510 mbed-Accelerometer-example-NCS36510

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   u8_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   u8_t clen, valid = 1;
00266   struct pbuf *q;
00267 
00268   IP6_FRAG_STATS_INC(ip6_frag.recv);
00269 
00270   if ((const void*)ip6_current_header() != ((u8_t*)p->payload) - IP6_HLEN) {
00271     /* ip6_frag_hdr must be in the first pbuf, not chained */
00272     IP6_FRAG_STATS_INC(ip6_frag.proterr);
00273     IP6_FRAG_STATS_INC(ip6_frag.drop);
00274     goto nullreturn;
00275   }
00276 
00277   frag_hdr = (struct ip6_frag_hdr *) p->payload;
00278 
00279   clen = pbuf_clen(p);
00280 
00281   offset = ntohs(frag_hdr->_fragment_offset);
00282 
00283   /* Calculate fragment length from IPv6 payload length.
00284    * Adjust for headers before Fragment Header.
00285    * And finally adjust by Fragment Header length. */
00286   len = ntohs(ip6_current_header()->_plen);
00287   len -= (u16_t)(((u8_t*)p->payload - (const u8_t*)ip6_current_header()) - IP6_HLEN);
00288   len -= IP6_FRAG_HLEN;
00289 
00290   /* Look for the datagram the fragment belongs to in the current datagram queue,
00291    * remembering the previous in the queue for later dequeueing. */
00292   for (ipr = reassdatagrams, ipr_prev = NULL; ipr != NULL; ipr = ipr->next) {
00293     /* Check if the incoming fragment matches the one currently present
00294        in the reassembly buffer. If so, we proceed with copying the
00295        fragment into the buffer. */
00296     if ((frag_hdr->_identification == ipr->identification) &&
00297         ip6_addr_cmp(ip6_current_src_addr(), &(IPV6_FRAG_HDRREF(ipr->iphdr)->src)) &&
00298         ip6_addr_cmp(ip6_current_dest_addr(), &(IPV6_FRAG_HDRREF(ipr->iphdr)->dest))) {
00299       IP6_FRAG_STATS_INC(ip6_frag.cachehit);
00300       break;
00301     }
00302     ipr_prev = ipr;
00303   }
00304 
00305   if (ipr == NULL) {
00306   /* Enqueue a new datagram into the datagram queue */
00307     ipr = (struct ip6_reassdata *)memp_malloc(MEMP_IP6_REASSDATA);
00308     if (ipr == NULL) {
00309 #if IP_REASS_FREE_OLDEST
00310       /* Make room and try again. */
00311       ip6_reass_remove_oldest_datagram(ipr, clen);
00312       ipr = (struct ip6_reassdata *)memp_malloc(MEMP_IP6_REASSDATA);
00313       if (ipr != NULL) {
00314         /* re-search ipr_prev since it might have been removed */
00315         for (ipr_prev = reassdatagrams; ipr_prev != NULL; ipr_prev = ipr_prev->next) {
00316           if (ipr_prev->next == ipr) {
00317             break;
00318           }
00319         }
00320       } else
00321 #endif /* IP_REASS_FREE_OLDEST */
00322       {
00323         IP6_FRAG_STATS_INC(ip6_frag.memerr);
00324         IP6_FRAG_STATS_INC(ip6_frag.drop);
00325         goto nullreturn;
00326       }
00327     }
00328 
00329     memset(ipr, 0, sizeof(struct ip6_reassdata));
00330     ipr->timer = IP_REASS_MAXAGE;
00331 
00332     /* enqueue the new structure to the front of the list */
00333     ipr->next = reassdatagrams;
00334     reassdatagrams = ipr;
00335 
00336     /* Use the current IPv6 header for src/dest address reference.
00337      * Eventually, we will replace it when we get the first fragment
00338      * (it might be this one, in any case, it is done later). */
00339 #if IPV6_FRAG_COPYHEADER
00340     MEMCPY(&ipr->iphdr, ip6_current_header(), IP6_HLEN);
00341 #else /* IPV6_FRAG_COPYHEADER */
00342     /* need to use the none-const pointer here: */
00343     ipr->iphdr = ip_data.current_ip6_header;
00344 #endif /* IPV6_FRAG_COPYHEADER */
00345 
00346     /* copy the fragmented packet id. */
00347     ipr->identification = frag_hdr->_identification;
00348 
00349     /* copy the nexth field */
00350     ipr->nexth = frag_hdr->_nexth;
00351   }
00352 
00353   /* Check if we are allowed to enqueue more datagrams. */
00354   if ((ip6_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS) {
00355 #if IP_REASS_FREE_OLDEST
00356     ip6_reass_remove_oldest_datagram(ipr, clen);
00357     if ((ip6_reass_pbufcount + clen) <= IP_REASS_MAX_PBUFS) {
00358       /* re-search ipr_prev since it might have been removed */
00359       for (ipr_prev = reassdatagrams; ipr_prev != NULL; ipr_prev = ipr_prev->next) {
00360         if (ipr_prev->next == ipr) {
00361           break;
00362         }
00363       }
00364     } else
00365 #endif /* IP_REASS_FREE_OLDEST */
00366     {
00367       /* @todo: send ICMPv6 time exceeded here? */
00368       /* drop this pbuf */
00369       IP6_FRAG_STATS_INC(ip6_frag.memerr);
00370       IP6_FRAG_STATS_INC(ip6_frag.drop);
00371       goto nullreturn;
00372     }
00373   }
00374 
00375   /* Overwrite Fragment Header with our own helper struct. */
00376 #if IPV6_FRAG_COPYHEADER
00377   if (IPV6_FRAG_REQROOM > 0) {
00378     /* Make room for struct ip6_reass_helper (only required if sizeof(void*) > 4).
00379        This cannot fail since we already checked when receiving this fragment. */
00380     err_t hdrerr = pbuf_header_force(p, IPV6_FRAG_REQROOM);
00381     LWIP_ASSERT("no room for struct ip6_reass_helper", hdrerr == ERR_OK);
00382   }
00383 #else /* IPV6_FRAG_COPYHEADER */
00384   LWIP_ASSERT("sizeof(struct ip6_reass_helper) <= IP6_FRAG_HLEN, set IPV6_FRAG_COPYHEADER to 1",
00385     sizeof(struct ip6_reass_helper) <= IP6_FRAG_HLEN);
00386 #endif /* IPV6_FRAG_COPYHEADER */
00387   iprh = (struct ip6_reass_helper *)p->payload;
00388   iprh->next_pbuf = NULL;
00389   iprh->start = (offset & IP6_FRAG_OFFSET_MASK);
00390   iprh->end = (offset & IP6_FRAG_OFFSET_MASK) + len;
00391 
00392   /* find the right place to insert this pbuf */
00393   /* Iterate through until we either get to the end of the list (append),
00394    * or we find on with a larger offset (insert). */
00395   for (q = ipr->p; q != NULL;) {
00396     iprh_tmp = (struct ip6_reass_helper*)q->payload;
00397     if (iprh->start < iprh_tmp->start) {
00398 #if IP_REASS_CHECK_OVERLAP
00399       if (iprh->end > iprh_tmp->start) {
00400         /* fragment overlaps with following, throw away */
00401         IP6_FRAG_STATS_INC(ip6_frag.proterr);
00402         IP6_FRAG_STATS_INC(ip6_frag.drop);
00403         goto nullreturn;
00404       }
00405       if (iprh_prev != NULL) {
00406         if (iprh->start < iprh_prev->end) {
00407           /* fragment overlaps with previous, throw away */
00408           IP6_FRAG_STATS_INC(ip6_frag.proterr);
00409           IP6_FRAG_STATS_INC(ip6_frag.drop);
00410           goto nullreturn;
00411         }
00412       }
00413 #endif /* IP_REASS_CHECK_OVERLAP */
00414       /* the new pbuf should be inserted before this */
00415       iprh->next_pbuf = q;
00416       if (iprh_prev != NULL) {
00417         /* not the fragment with the lowest offset */
00418         iprh_prev->next_pbuf = p;
00419       } else {
00420         /* fragment with the lowest offset */
00421         ipr->p = p;
00422       }
00423       break;
00424     } else if (iprh->start == iprh_tmp->start) {
00425       /* received the same datagram twice: no need to keep the datagram */
00426       IP6_FRAG_STATS_INC(ip6_frag.drop);
00427       goto nullreturn;
00428 #if IP_REASS_CHECK_OVERLAP
00429     } else if (iprh->start < iprh_tmp->end) {
00430       /* overlap: no need to keep the new datagram */
00431       IP6_FRAG_STATS_INC(ip6_frag.proterr);
00432       IP6_FRAG_STATS_INC(ip6_frag.drop);
00433       goto nullreturn;
00434 #endif /* IP_REASS_CHECK_OVERLAP */
00435     } else {
00436       /* Check if the fragments received so far have no gaps. */
00437       if (iprh_prev != NULL) {
00438         if (iprh_prev->end != iprh_tmp->start) {
00439           /* There is a fragment missing between the current
00440            * and the previous fragment */
00441           valid = 0;
00442         }
00443       }
00444     }
00445     q = iprh_tmp->next_pbuf;
00446     iprh_prev = iprh_tmp;
00447   }
00448 
00449   /* If q is NULL, then we made it to the end of the list. Determine what to do now */
00450   if (q == NULL) {
00451     if (iprh_prev != NULL) {
00452       /* this is (for now), the fragment with the highest offset:
00453        * chain it to the last fragment */
00454 #if IP_REASS_CHECK_OVERLAP
00455       LWIP_ASSERT("check fragments don't overlap", iprh_prev->end <= iprh->start);
00456 #endif /* IP_REASS_CHECK_OVERLAP */
00457       iprh_prev->next_pbuf = p;
00458       if (iprh_prev->end != iprh->start) {
00459         valid = 0;
00460       }
00461     } else {
00462 #if IP_REASS_CHECK_OVERLAP
00463       LWIP_ASSERT("no previous fragment, this must be the first fragment!",
00464         ipr->p == NULL);
00465 #endif /* IP_REASS_CHECK_OVERLAP */
00466       /* this is the first fragment we ever received for this ip datagram */
00467       ipr->p = p;
00468     }
00469   }
00470 
00471   /* Track the current number of pbufs current 'in-flight', in order to limit
00472   the number of fragments that may be enqueued at any one time */
00473   ip6_reass_pbufcount += clen;
00474 
00475   /* Remember IPv6 header if this is the first fragment. */
00476   if (iprh->start == 0) {
00477 #if IPV6_FRAG_COPYHEADER
00478     if (iprh->next_pbuf != NULL) {
00479       MEMCPY(&ipr->iphdr, ip6_current_header(), IP6_HLEN);
00480     }
00481 #else /* IPV6_FRAG_COPYHEADER */
00482     /* need to use the none-const pointer here: */
00483     ipr->iphdr = ip_data.current_ip6_header;
00484 #endif /* IPV6_FRAG_COPYHEADER */
00485   }
00486 
00487   /* If this is the last fragment, calculate total packet length. */
00488   if ((offset & IP6_FRAG_MORE_FLAG) == 0) {
00489     ipr->datagram_len = iprh->end;
00490   }
00491 
00492   /* Additional validity tests: we have received first and last fragment. */
00493   iprh_tmp = (struct ip6_reass_helper*)ipr->p->payload;
00494   if (iprh_tmp->start != 0) {
00495     valid = 0;
00496   }
00497   if (ipr->datagram_len == 0) {
00498     valid = 0;
00499   }
00500 
00501   /* Final validity test: no gaps between current and last fragment. */
00502   iprh_prev = iprh;
00503   q = iprh->next_pbuf;
00504   while ((q != NULL) && valid) {
00505     iprh = (struct ip6_reass_helper*)q->payload;
00506     if (iprh_prev->end != iprh->start) {
00507       valid = 0;
00508       break;
00509     }
00510     iprh_prev = iprh;
00511     q = iprh->next_pbuf;
00512   }
00513 
00514   if (valid) {
00515     /* All fragments have been received */
00516     struct ip6_hdr* iphdr_ptr;
00517 
00518     /* chain together the pbufs contained within the ip6_reassdata list. */
00519     iprh = (struct ip6_reass_helper*) ipr->p->payload;
00520     while (iprh != NULL) {
00521       struct pbuf* next_pbuf = iprh->next_pbuf;
00522       if (next_pbuf != NULL) {
00523         /* Save next helper struct (will be hidden in next step). */
00524         iprh_tmp = (struct ip6_reass_helper*)next_pbuf->payload;
00525 
00526         /* hide the fragment header for every succeeding fragment */
00527         pbuf_header(next_pbuf, -IP6_FRAG_HLEN);
00528 #if IPV6_FRAG_COPYHEADER
00529         if (IPV6_FRAG_REQROOM > 0) {
00530           /* hide the extra bytes borrowed from ip6_hdr for struct ip6_reass_helper */
00531           err_t hdrerr = pbuf_header(next_pbuf, -(s16_t)(IPV6_FRAG_REQROOM));
00532           LWIP_ASSERT("no room for struct ip6_reass_helper", hdrerr == ERR_OK);
00533         }
00534 #endif
00535         pbuf_cat(ipr->p, next_pbuf);
00536       }
00537       else {
00538         iprh_tmp = NULL;
00539       }
00540 
00541       iprh = iprh_tmp;
00542     }
00543 
00544 #if IPV6_FRAG_COPYHEADER
00545     if (IPV6_FRAG_REQROOM > 0) {
00546       /* get back room for struct ip6_reass_helper (only required if sizeof(void*) > 4) */
00547       err_t hdrerr = pbuf_header(ipr->p, -(s16_t)(IPV6_FRAG_REQROOM));
00548       LWIP_ASSERT("no room for struct ip6_reass_helper", hdrerr == ERR_OK);
00549     }
00550     iphdr_ptr = (struct ip6_hdr*)((u8_t*)ipr->p->payload - IP6_HLEN);
00551     MEMCPY(iphdr_ptr, &ipr->iphdr, IP6_HLEN);
00552 #else
00553     iphdr_ptr = ipr->iphdr;
00554 #endif
00555 
00556     /* Adjust datagram length by adding header lengths. */
00557     ipr->datagram_len += (u16_t)(((u8_t*)ipr->p->payload - (u8_t*)iphdr_ptr)
00558                          + IP6_FRAG_HLEN
00559                          - IP6_HLEN);
00560 
00561     /* Set payload length in ip header. */
00562     iphdr_ptr->_plen = htons(ipr->datagram_len);
00563 
00564     /* Get the first pbuf. */
00565     p = ipr->p;
00566 
00567     /* Restore Fragment Header in first pbuf. Mark as "single fragment"
00568      * packet. Restore nexth. */
00569     frag_hdr = (struct ip6_frag_hdr *) p->payload;
00570     frag_hdr->_nexth = ipr->nexth;
00571     frag_hdr->reserved = 0;
00572     frag_hdr->_fragment_offset = 0;
00573     frag_hdr->_identification = 0;
00574 
00575     /* release the sources allocate for the fragment queue entry */
00576     if (reassdatagrams == ipr) {
00577       /* it was the first in the list */
00578       reassdatagrams = ipr->next;
00579     } else {
00580       /* it wasn't the first, so it must have a valid 'prev' */
00581       LWIP_ASSERT("sanity check linked list", ipr_prev != NULL);
00582       ipr_prev->next = ipr->next;
00583     }
00584     memp_free(MEMP_IP6_REASSDATA, ipr);
00585 
00586     /* adjust the number of pbufs currently queued for reassembly. */
00587     ip6_reass_pbufcount -= pbuf_clen(p);
00588 
00589     /* Move pbuf back to IPv6 header.
00590        This cannot fail since we already checked when receiving this fragment. */
00591     if (pbuf_header_force(p, (s16_t)((u8_t*)p->payload - (u8_t*)iphdr_ptr))) {
00592       LWIP_ASSERT("ip6_reass: moving p->payload to ip6 header failed\n", 0);
00593       pbuf_free(p);
00594       return NULL;
00595     }
00596 
00597     /* Return the pbuf chain */
00598     return p;
00599   }
00600   /* the datagram is not (yet?) reassembled completely */
00601   return NULL;
00602 
00603 nullreturn:
00604   pbuf_free(p);
00605   return NULL;
00606 }
00607 
00608 #endif /* LWIP_IPV6 && LWIP_IPV6_REASS */
00609 
00610 #if LWIP_IPV6 && LWIP_IPV6_FRAG
00611 
00612 /** Allocate a new struct pbuf_custom_ref */
00613 static struct pbuf_custom_ref*
00614 ip6_frag_alloc_pbuf_custom_ref(void)
00615 {
00616   return (struct pbuf_custom_ref*)memp_malloc(MEMP_FRAG_PBUF);
00617 }
00618 
00619 /** Free a struct pbuf_custom_ref */
00620 static void
00621 ip6_frag_free_pbuf_custom_ref(struct pbuf_custom_ref* p)
00622 {
00623   LWIP_ASSERT("p != NULL", p != NULL);
00624   memp_free(MEMP_FRAG_PBUF, p);
00625 }
00626 
00627 /** Free-callback function to free a 'struct pbuf_custom_ref', called by
00628  * pbuf_free. */
00629 static void
00630 ip6_frag_free_pbuf_custom(struct pbuf *p)
00631 {
00632   struct pbuf_custom_ref *pcr = (struct pbuf_custom_ref*)p;
00633   LWIP_ASSERT("pcr != NULL", pcr != NULL);
00634   LWIP_ASSERT("pcr == p", (void*)pcr == (void*)p);
00635   if (pcr->original != NULL) {
00636     pbuf_free(pcr->original);
00637   }
00638   ip6_frag_free_pbuf_custom_ref(pcr);
00639 }
00640 
00641 /**
00642  * Fragment an IPv6 datagram if too large for the netif or path MTU.
00643  *
00644  * Chop the datagram in MTU sized chunks and send them in order
00645  * by pointing PBUF_REFs into p
00646  *
00647  * @param p ipv6 packet to send
00648  * @param netif the netif on which to send
00649  * @param dest destination ipv6 address to which to send
00650  *
00651  * @return ERR_OK if sent successfully, err_t otherwise
00652  */
00653 err_t
00654 ip6_frag(struct pbuf *p, struct netif *netif, const ip6_addr_t *dest)
00655 {
00656   struct ip6_hdr *original_ip6hdr;
00657   struct ip6_hdr *ip6hdr;
00658   struct ip6_frag_hdr * frag_hdr;
00659   struct pbuf *rambuf;
00660   struct pbuf *newpbuf;
00661   static u32_t identification;
00662   u16_t nfb;
00663   u16_t left, cop;
00664   u16_t mtu;
00665   u16_t fragment_offset = 0;
00666   u16_t last;
00667   u16_t poff = IP6_HLEN;
00668   u16_t newpbuflen = 0;
00669   u16_t left_to_copy;
00670 
00671   identification++;
00672 
00673   original_ip6hdr = (struct ip6_hdr *)p->payload;
00674 
00675   mtu = nd6_get_destination_mtu(dest, netif);
00676 
00677   /* @todo we assume there are no options in the unfragmentable part (IPv6 header). */
00678   left = p->tot_len - IP6_HLEN;
00679 
00680   nfb = (mtu - (IP6_HLEN + IP6_FRAG_HLEN)) & IP6_FRAG_OFFSET_MASK;
00681 
00682   while (left) {
00683     last = (left <= nfb);
00684 
00685     /* Fill this fragment */
00686     cop = last ? left : nfb;
00687 
00688     /* When not using a static buffer, create a chain of pbufs.
00689      * The first will be a PBUF_RAM holding the link, IPv6, and Fragment header.
00690      * The rest will be PBUF_REFs mirroring the pbuf chain to be fragged,
00691      * but limited to the size of an mtu.
00692      */
00693     rambuf = pbuf_alloc(PBUF_LINK, IP6_HLEN + IP6_FRAG_HLEN, PBUF_RAM);
00694     if (rambuf == NULL) {
00695       IP6_FRAG_STATS_INC(ip6_frag.memerr);
00696       return ERR_MEM;
00697     }
00698     LWIP_ASSERT("this needs a pbuf in one piece!",
00699                 (p->len >= (IP6_HLEN + IP6_FRAG_HLEN)));
00700     SMEMCPY(rambuf->payload, original_ip6hdr, IP6_HLEN);
00701     ip6hdr = (struct ip6_hdr *)rambuf->payload;
00702     frag_hdr = (struct ip6_frag_hdr *)((u8_t*)rambuf->payload + IP6_HLEN);
00703 
00704     /* Can just adjust p directly for needed offset. */
00705     p->payload = (u8_t *)p->payload + poff;
00706     p->len -= poff;
00707     p->tot_len -= poff;
00708 
00709     left_to_copy = cop;
00710     while (left_to_copy) {
00711       struct pbuf_custom_ref *pcr;
00712       newpbuflen = (left_to_copy < p->len) ? left_to_copy : p->len;
00713       /* Is this pbuf already empty? */
00714       if (!newpbuflen) {
00715         p = p->next;
00716         continue;
00717       }
00718       pcr = ip6_frag_alloc_pbuf_custom_ref();
00719       if (pcr == NULL) {
00720         pbuf_free(rambuf);
00721         IP6_FRAG_STATS_INC(ip6_frag.memerr);
00722         return ERR_MEM;
00723       }
00724       /* Mirror this pbuf, although we might not need all of it. */
00725       newpbuf = pbuf_alloced_custom(PBUF_RAW, newpbuflen, PBUF_REF, &pcr->pc, p->payload, newpbuflen);
00726       if (newpbuf == NULL) {
00727         ip6_frag_free_pbuf_custom_ref(pcr);
00728         pbuf_free(rambuf);
00729         IP6_FRAG_STATS_INC(ip6_frag.memerr);
00730         return ERR_MEM;
00731       }
00732       pbuf_ref(p);
00733       pcr->original = p;
00734       pcr->pc.custom_free_function = ip6_frag_free_pbuf_custom;
00735 
00736       /* Add it to end of rambuf's chain, but using pbuf_cat, not pbuf_chain
00737        * so that it is removed when pbuf_dechain is later called on rambuf.
00738        */
00739       pbuf_cat(rambuf, newpbuf);
00740       left_to_copy -= newpbuflen;
00741       if (left_to_copy) {
00742         p = p->next;
00743       }
00744     }
00745     poff = newpbuflen;
00746 
00747     /* Set headers */
00748     frag_hdr->_nexth = original_ip6hdr->_nexth;
00749     frag_hdr->reserved = 0;
00750     frag_hdr->_fragment_offset = htons((fragment_offset & IP6_FRAG_OFFSET_MASK) | (last ? 0 : IP6_FRAG_MORE_FLAG));
00751     frag_hdr->_identification = htonl(identification);
00752 
00753     IP6H_NEXTH_SET(ip6hdr, IP6_NEXTH_FRAGMENT);
00754     IP6H_PLEN_SET(ip6hdr, cop + IP6_FRAG_HLEN);
00755 
00756     /* No need for separate header pbuf - we allowed room for it in rambuf
00757      * when allocated.
00758      */
00759     IP6_FRAG_STATS_INC(ip6_frag.xmit);
00760     netif->output_ip6(netif, rambuf, dest);
00761 
00762     /* Unfortunately we can't reuse rambuf - the hardware may still be
00763      * using the buffer. Instead we free it (and the ensuing chain) and
00764      * recreate it next time round the loop. If we're lucky the hardware
00765      * will have already sent the packet, the free will really free, and
00766      * there will be zero memory penalty.
00767      */
00768 
00769     pbuf_free(rambuf);
00770     left -= cop;
00771     fragment_offset += cop;
00772   }
00773   return ERR_OK;
00774 }
00775 
00776 #endif /* LWIP_IPV6 && LWIP_IPV6_FRAG */