Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependents: mbed-TFT-example-NCS36510 mbed-Accelerometer-example-NCS36510 mbed-Accelerometer-example-NCS36510
lwip_ip6_frag.c
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 */
Generated on Tue Jul 12 2022 11:02:41 by
