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