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.
Fork of mbed-os by
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 13:15:53 by
