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 OmniWheels 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 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 */
Generated on Fri Jul 22 2022 04:53:52 by
1.7.2
