Webserver+3d print
Embed:
(wiki syntax)
Show/hide line numbers
ipv6_frag.c
Go to the documentation of this file.
00001 /** 00002 * @file ipv6_frag.c 00003 * @brief IPv6 fragmentation and reassembly 00004 * 00005 * @section License 00006 * 00007 * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved. 00008 * 00009 * This file is part of CycloneTCP Open. 00010 * 00011 * This program is free software; you can redistribute it and/or 00012 * modify it under the terms of the GNU General Public License 00013 * as published by the Free Software Foundation; either version 2 00014 * of the License, or (at your option) any later version. 00015 * 00016 * This program is distributed in the hope that it will be useful, 00017 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00018 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00019 * GNU General Public License for more details. 00020 * 00021 * You should have received a copy of the GNU General Public License 00022 * along with this program; if not, write to the Free Software Foundation, 00023 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00024 * 00025 * @author Oryx Embedded SARL (www.oryx-embedded.com) 00026 * @version 1.7.6 00027 **/ 00028 00029 //Switch to the appropriate trace level 00030 #define TRACE_LEVEL IPV6_TRACE_LEVEL 00031 00032 //Dependencies 00033 #include "core/net.h" 00034 #include "core/ip.h" 00035 #include "ipv6/ipv6.h" 00036 #include "ipv6/ipv6_frag.h" 00037 #include "ipv6/icmpv6.h" 00038 #include "debug.h" 00039 00040 //Check TCP/IP stack configuration 00041 #if (IPV6_SUPPORT == ENABLED && IPV6_FRAG_SUPPORT == ENABLED) 00042 00043 //Tick counter to handle periodic operations 00044 systime_t ipv6FragTickCounter; 00045 00046 00047 /** 00048 * @brief Fragment IPv6 datagram into smaller packets 00049 * @param[in] interface Underlying network interface 00050 * @param[in] pseudoHeader IPv6 pseudo header 00051 * @param[in] payload Multi-part buffer containing the payload 00052 * @param[in] payloadOffset Offset to the first payload byte 00053 * @param[in] pathMtu PMTU value 00054 * @param[in] hopLimit Hop Limit value 00055 * @return Error code 00056 **/ 00057 00058 error_t ipv6FragmentDatagram(NetInterface *interface, Ipv6PseudoHeader *pseudoHeader, 00059 const NetBuffer *payload, size_t payloadOffset, size_t pathMtu, uint8_t hopLimit) 00060 { 00061 error_t error; 00062 uint32_t id; 00063 size_t offset; 00064 size_t length; 00065 size_t payloadLength; 00066 size_t fragmentOffset; 00067 size_t maxFragmentSize; 00068 NetBuffer *fragment; 00069 00070 //Identification field is used to identify fragments of an original IP datagram 00071 id = interface->ipv6Context.identification++; 00072 00073 //Retrieve the length of the payload 00074 payloadLength = netBufferGetLength(payload) - payloadOffset; 00075 00076 //Allocate a memory buffer to hold IP fragments 00077 fragment = ipAllocBuffer(0, &fragmentOffset); 00078 //Failed to allocate memory? 00079 if(!fragment) 00080 return ERROR_OUT_OF_MEMORY; 00081 00082 //The node should never set its PMTU estimate below the IPv6 minimum link MTU 00083 pathMtu = MAX(pathMtu, IPV6_DEFAULT_MTU); 00084 00085 //Determine the maximum payload size for fragmented packets 00086 maxFragmentSize = pathMtu - sizeof(Ipv6Header) - sizeof(Ipv6FragmentHeader); 00087 //The size shall be a multiple of 8-byte blocks 00088 maxFragmentSize -= (maxFragmentSize % 8); 00089 00090 //Initialize error code 00091 error = NO_ERROR; 00092 00093 //Split the payload into multiple IP fragments 00094 for(offset = 0; offset < payloadLength; offset += length) 00095 { 00096 //Flush the contents of the fragment 00097 error = netBufferSetLength(fragment, fragmentOffset); 00098 //Sanity check 00099 if(error) 00100 break; 00101 00102 //Process the last fragment? 00103 if((payloadLength - offset) <= maxFragmentSize) 00104 { 00105 //Size of the current fragment 00106 length = payloadLength - offset; 00107 //Copy fragment data 00108 netBufferConcat(fragment, payload, payloadOffset + offset, length); 00109 00110 //Do not set the MF flag for the last fragment 00111 error = ipv6SendPacket(interface, pseudoHeader, id, 00112 offset, fragment, fragmentOffset, hopLimit); 00113 } 00114 else 00115 { 00116 //Size of the current fragment (must be a multiple of 8-byte blocks) 00117 length = maxFragmentSize; 00118 //Copy fragment data 00119 netBufferConcat(fragment, payload, payloadOffset + offset, length); 00120 00121 //Fragmented packets must have the M flag set 00122 error = ipv6SendPacket(interface, pseudoHeader, id, 00123 offset | IPV6_FLAG_M, fragment, fragmentOffset, hopLimit); 00124 } 00125 00126 //Failed to send current IP fragment? 00127 if(error) 00128 break; 00129 } 00130 00131 //Free previously allocated memory 00132 netBufferFree(fragment); 00133 //Return status code 00134 return error; 00135 } 00136 00137 00138 /** 00139 * @brief Parse Fragment header and reassemble original datagram 00140 * @param[in] interface Underlying network interface 00141 * @param[in] ipPacket Multi-part buffer containing the incoming IPv6 packet 00142 * @param[in] ipPacketOffset Offset to the first byte of the IPv6 packet 00143 * @param[in] fragHeaderOffset Offset to the Fragment header 00144 * @param[in] nextHeaderOffset Offset to the Next Header field of the previous header 00145 **/ 00146 00147 void ipv6ParseFragmentHeader(NetInterface *interface, const NetBuffer *ipPacket, 00148 size_t ipPacketOffset, size_t fragHeaderOffset, size_t nextHeaderOffset) 00149 { 00150 error_t error; 00151 size_t n; 00152 size_t length; 00153 uint16_t offset; 00154 uint16_t dataFirst; 00155 uint16_t dataLast; 00156 Ipv6FragDesc *frag; 00157 Ipv6HoleDesc *hole; 00158 Ipv6HoleDesc *prevHole; 00159 Ipv6Header *ipHeader; 00160 Ipv6FragmentHeader *fragHeader; 00161 00162 //Remaining bytes to process in the payload 00163 length = netBufferGetLength(ipPacket) - fragHeaderOffset; 00164 00165 //Ensure the fragment header is valid 00166 if(length < sizeof(Ipv6FragmentHeader)) 00167 return; 00168 00169 //Point to the IPv6 header 00170 ipHeader = netBufferAt(ipPacket, ipPacketOffset); 00171 //Sanity check 00172 if(ipHeader == NULL) 00173 return; 00174 00175 //Point to the Fragment header 00176 fragHeader = netBufferAt(ipPacket, fragHeaderOffset); 00177 //Sanity check 00178 if(fragHeader == NULL) 00179 return; 00180 00181 //Calculate the length of the fragment 00182 length -= sizeof(Ipv6FragmentHeader); 00183 //Convert the fragment offset from network byte order 00184 offset = ntohs(fragHeader->fragmentOffset); 00185 00186 //Every fragment except the last must contain a multiple of 8 bytes of data 00187 if((offset & IPV6_FLAG_M) && (length % 8)) 00188 { 00189 //Compute the offset of the Payload Length field within the packet 00190 n = (uint8_t *) &ipHeader->payloadLength - (uint8_t *) ipHeader; 00191 00192 //The fragment must be discarded and an ICMP Parameter Problem 00193 //message should be sent to the source of the fragment, pointing 00194 //to the Payload Length field of the fragment packet 00195 icmpv6SendErrorMessage(interface, ICMPV6_TYPE_PARAM_PROBLEM, 00196 ICMPV6_CODE_INVALID_HEADER_FIELD, n, ipPacket, ipPacketOffset); 00197 00198 //Exit immediately 00199 return; 00200 } 00201 00202 //Calculate the index of the first byte 00203 dataFirst = offset & IPV6_OFFSET_MASK; 00204 //Calculate the index immediately following the last byte 00205 dataLast = dataFirst + (uint16_t) length; 00206 00207 //Search for a matching IP datagram being reassembled 00208 frag = ipv6SearchFragQueue(interface, ipHeader, fragHeader); 00209 //No matching entry in the reassembly queue? 00210 if(frag == NULL) 00211 return; 00212 00213 //The very first fragment requires special handling 00214 if(!(offset & IPV6_OFFSET_MASK)) 00215 { 00216 uint8_t *p; 00217 00218 //Calculate the length of the unfragmentable part 00219 frag->unfragPartLength = fragHeaderOffset - ipPacketOffset; 00220 00221 //The size of the reconstructed datagram exceeds the maximum value? 00222 if((frag->unfragPartLength + frag->fragPartLength) > IPV6_MAX_FRAG_DATAGRAM_SIZE) 00223 { 00224 //Retrieve the offset of the Fragment header within the packet 00225 n = fragHeaderOffset - ipPacketOffset; 00226 //Compute the exact offset of the Fragment Offset field 00227 n += (uint8_t *) &fragHeader->fragmentOffset - (uint8_t *) fragHeader; 00228 00229 //The fragment must be discarded and an ICMP Parameter Problem 00230 //message should be sent to the source of the fragment, pointing 00231 //to the Fragment Offset field of the fragment packet 00232 icmpv6SendErrorMessage(interface, ICMPV6_TYPE_PARAM_PROBLEM, 00233 ICMPV6_CODE_INVALID_HEADER_FIELD, n, ipPacket, ipPacketOffset); 00234 00235 //Drop the reconstructed datagram 00236 netBufferSetLength((NetBuffer *) &frag->buffer, 0); 00237 //Exit immediately 00238 return; 00239 } 00240 00241 //Make sure the unfragmentable part entirely fits in the first chunk 00242 if(frag->unfragPartLength > frag->buffer.chunk[0].size) 00243 { 00244 //Drop the reconstructed datagram 00245 netBufferSetLength((NetBuffer *) &frag->buffer, 0); 00246 //Exit immediately 00247 return; 00248 } 00249 00250 //Fix the length of the first chunk 00251 frag->buffer.chunk[0].length = frag->unfragPartLength; 00252 00253 //The unfragmentable part of the reassembled packet consists 00254 //of all headers up to, but not including, the Fragment header 00255 //of the first fragment packet 00256 netBufferCopy((NetBuffer *) &frag->buffer, 0, ipPacket, 00257 ipPacketOffset, frag->unfragPartLength); 00258 00259 //Point to the Next Header field of the last header 00260 p = netBufferAt((NetBuffer *) &frag->buffer, 00261 nextHeaderOffset - ipPacketOffset); 00262 00263 //The Next Header field of the last header of the unfragmentable 00264 //part is obtained from the Next Header field of the first 00265 //fragment's Fragment header 00266 *p = fragHeader->nextHeader; 00267 } 00268 00269 //It may be necessary to increase the size of the buffer... 00270 if(dataLast > frag->fragPartLength) 00271 { 00272 //The size of the reconstructed datagram exceeds the maximum value? 00273 if((frag->unfragPartLength + dataLast) > IPV6_MAX_FRAG_DATAGRAM_SIZE) 00274 { 00275 //Retrieve the offset of the Fragment header within the packet 00276 n = fragHeaderOffset - ipPacketOffset; 00277 //Compute the exact offset of the Fragment Offset field 00278 n += (uint8_t *) &fragHeader->fragmentOffset - (uint8_t *) fragHeader; 00279 00280 //The fragment must be discarded and an ICMP Parameter Problem 00281 //message should be sent to the source of the fragment, pointing 00282 //to the Fragment Offset field of the fragment packet 00283 icmpv6SendErrorMessage(interface, ICMPV6_TYPE_PARAM_PROBLEM, 00284 ICMPV6_CODE_INVALID_HEADER_FIELD, n, ipPacket, ipPacketOffset); 00285 00286 //Drop the reconstructed datagram 00287 netBufferSetLength((NetBuffer *) &frag->buffer, 0); 00288 //Exit immediately 00289 return; 00290 } 00291 00292 //Adjust the size of the reconstructed datagram 00293 error = netBufferSetLength((NetBuffer *) &frag->buffer, 00294 frag->unfragPartLength + dataLast + sizeof(Ipv6HoleDesc)); 00295 00296 //Any error to report? 00297 if(error) 00298 { 00299 //Drop the reconstructed datagram 00300 netBufferSetLength((NetBuffer *) &frag->buffer, 0); 00301 //Exit immediately 00302 return; 00303 } 00304 00305 //Actual length of the fragmentable part 00306 frag->fragPartLength = dataLast; 00307 } 00308 00309 //Select the first hole descriptor from the list 00310 hole = ipv6FindHole(frag, frag->firstHole); 00311 //Keep track of the previous hole in the list 00312 prevHole = NULL; 00313 00314 //Iterate through the hole descriptors 00315 while(hole != NULL) 00316 { 00317 //Save lower and upper boundaries for later use 00318 uint16_t holeFirst = hole->first; 00319 uint16_t holeLast = hole->last; 00320 00321 //Check whether the newly arrived fragment 00322 //interacts with this hole in some way 00323 if(dataFirst < holeLast && dataLast > holeFirst) 00324 { 00325 //The current descriptor is no longer valid. We will destroy 00326 //it, and in the next two steps, we will determine whether 00327 //or not it is necessary to create any new hole descriptors 00328 if(prevHole != NULL) 00329 prevHole->next = hole->next; 00330 else 00331 frag->firstHole = hole->next; 00332 00333 //Is there still a hole at the beginning of the segment? 00334 if(dataFirst > holeFirst) 00335 { 00336 //Create a new entry that describes this hole 00337 hole = ipv6FindHole(frag, holeFirst); 00338 hole->first = holeFirst; 00339 hole->last = dataFirst; 00340 00341 //Insert the newly created entry into the hole descriptor list 00342 if(prevHole != NULL) 00343 { 00344 hole->next = prevHole->next; 00345 prevHole->next = hole->first; 00346 } 00347 else 00348 { 00349 hole->next = frag->firstHole; 00350 frag->firstHole = hole->first; 00351 } 00352 00353 //Always keep track of the previous hole 00354 prevHole = hole; 00355 } 00356 00357 //Is there still a hole at the end of the segment? 00358 if(dataLast < holeLast && (offset & IPV6_FLAG_M)) 00359 { 00360 //Create a new entry that describes this hole 00361 hole = ipv6FindHole(frag, dataLast); 00362 hole->first = dataLast; 00363 hole->last = holeLast; 00364 00365 //Insert the newly created entry into the hole descriptor list 00366 if(prevHole != NULL) 00367 { 00368 hole->next = prevHole->next; 00369 prevHole->next = hole->first; 00370 } 00371 else 00372 { 00373 hole->next = frag->firstHole; 00374 frag->firstHole = hole->first; 00375 } 00376 00377 //Always keep track of the previous hole 00378 prevHole = hole; 00379 } 00380 } 00381 else 00382 { 00383 //The newly arrived fragment does not interact with the current hole 00384 prevHole = hole; 00385 } 00386 00387 //Select the next hole descriptor from the list 00388 hole = ipv6FindHole(frag, prevHole ? prevHole->next : frag->firstHole); 00389 } 00390 00391 //Copy data from the fragment to the reassembly buffer 00392 netBufferCopy((NetBuffer *) &frag->buffer, frag->unfragPartLength + dataFirst, 00393 ipPacket, fragHeaderOffset + sizeof(Ipv6FragmentHeader), length); 00394 00395 //Dump hole descriptor list 00396 ipv6DumpHoleList(frag); 00397 00398 //If the hole descriptor list is empty, the reassembly process is now complete 00399 if(!ipv6FindHole(frag, frag->firstHole)) 00400 { 00401 //Discard the extra hole descriptor that follows the reconstructed datagram 00402 error = netBufferSetLength((NetBuffer *) &frag->buffer, 00403 frag->unfragPartLength + frag->fragPartLength); 00404 00405 //Check status code 00406 if(!error) 00407 { 00408 //Point to the IPv6 header 00409 Ipv6Header *datagram = netBufferAt((NetBuffer *) &frag->buffer, 0); 00410 00411 //Fix the Payload Length field 00412 datagram->payloadLength = htons(frag->unfragPartLength + 00413 frag->fragPartLength - sizeof(Ipv6Header)); 00414 00415 //Pass the original IPv6 datagram to the higher protocol layer 00416 ipv6ProcessPacket(interface, (NetBuffer *) &frag->buffer, 0); 00417 } 00418 00419 //Release previously allocated memory 00420 netBufferSetLength((NetBuffer *) &frag->buffer, 0); 00421 } 00422 } 00423 00424 00425 /** 00426 * @brief Fragment reassembly timeout handler 00427 * 00428 * This routine must be periodically called by the TCP/IP stack to 00429 * handle IPv6 fragment reassembly timeout 00430 * 00431 * @param[in] interface Underlying network interface 00432 **/ 00433 00434 void ipv6FragTick(NetInterface *interface) 00435 { 00436 error_t error; 00437 uint_t i; 00438 systime_t time; 00439 Ipv6HoleDesc *hole; 00440 00441 //Get current time 00442 time = osGetSystemTime(); 00443 00444 //Loop through the reassembly queue 00445 for(i = 0; i < IPV6_MAX_FRAG_DATAGRAMS; i++) 00446 { 00447 //Point to the current entry in the reassembly queue 00448 Ipv6FragDesc *frag = &interface->ipv6Context.fragQueue[i]; 00449 00450 //Make sure the entry is currently in use 00451 if(frag->buffer.chunkCount > 0) 00452 { 00453 //If the timer runs out, the partially-reassembled datagram must be 00454 //discarded and ICMPv6 Time Exceeded message sent to the source host 00455 if((time - frag->timestamp) >= IPV6_FRAG_TIME_TO_LIVE) 00456 { 00457 //Debug message 00458 TRACE_INFO("IPv6 fragment reassembly timeout...\r\n"); 00459 //Dump IP header contents for debugging purpose 00460 ipv6DumpHeader(frag->buffer.chunk[0].address); 00461 00462 //Point to the first hole descriptor 00463 hole = ipv6FindHole(frag, frag->firstHole); 00464 00465 //Make sure the fragment zero has been received 00466 //before sending an ICMPv6 message 00467 if(hole != NULL && hole->first > 0) 00468 { 00469 //Fix the size of the reconstructed datagram 00470 error = netBufferSetLength((NetBuffer *) &frag->buffer, 00471 frag->unfragPartLength + hole->first); 00472 00473 //Check status code 00474 if(!error) 00475 { 00476 //Send an ICMPv6 Time Exceeded message 00477 icmpv6SendErrorMessage(interface, ICMPV6_TYPE_TIME_EXCEEDED, 00478 ICMPV6_CODE_REASSEMBLY_TIME_EXCEEDED, 0, (NetBuffer *) &frag->buffer, 0); 00479 } 00480 } 00481 00482 //Drop the partially reconstructed datagram 00483 netBufferSetLength((NetBuffer *) &frag->buffer, 0); 00484 } 00485 } 00486 } 00487 } 00488 00489 00490 /** 00491 * @brief Search for a matching datagram in the reassembly queue 00492 * @param[in] interface Underlying network interface 00493 * @param[in] packet Incoming IPv6 packet 00494 * @param[in] header Pointer to the Fragment header 00495 * @return Matching fragment descriptor 00496 **/ 00497 00498 Ipv6FragDesc *ipv6SearchFragQueue(NetInterface *interface, 00499 Ipv6Header *packet, Ipv6FragmentHeader *header) 00500 { 00501 error_t error; 00502 uint_t i; 00503 Ipv6Header *datagram; 00504 Ipv6FragDesc *frag; 00505 Ipv6HoleDesc *hole; 00506 00507 //Search for a matching IP datagram being reassembled 00508 for(i = 0; i < IPV6_MAX_FRAG_DATAGRAMS; i++) 00509 { 00510 //Point to the current entry in the reassembly queue 00511 frag = &interface->ipv6Context.fragQueue[i]; 00512 00513 //Check whether the current entry is used? 00514 if(frag->buffer.chunkCount > 0) 00515 { 00516 //Point to the corresponding datagram 00517 datagram = netBufferAt((NetBuffer *) &frag->buffer, 0); 00518 00519 //Check source and destination addresses 00520 if(!ipv6CompAddr(&datagram->srcAddr, &packet->srcAddr)) 00521 continue; 00522 if(!ipv6CompAddr(&datagram->destAddr, &packet->destAddr)) 00523 continue; 00524 //Compare fragment identification fields 00525 if(frag->identification != header->identification) 00526 continue; 00527 00528 //A matching entry has been found in the reassembly queue 00529 return frag; 00530 } 00531 } 00532 00533 //If the current packet does not match an existing entry 00534 //in the reassembly queue, then create a new entry 00535 for(i = 0; i < IPV6_MAX_FRAG_DATAGRAMS; i++) 00536 { 00537 //Point to the current entry in the reassembly queue 00538 frag = &interface->ipv6Context.fragQueue[i]; 00539 00540 //The current entry is free? 00541 if(!frag->buffer.chunkCount) 00542 { 00543 //Number of chunks that comprise the reassembly buffer 00544 frag->buffer.maxChunkCount = arraysize(frag->buffer.chunk); 00545 00546 //Allocate sufficient memory to hold the IPv6 header and 00547 //the first hole descriptor 00548 error = netBufferSetLength((NetBuffer *) &frag->buffer, 00549 NET_MEM_POOL_BUFFER_SIZE + sizeof(Ipv6HoleDesc)); 00550 00551 //Failed to allocate memory? 00552 if(error) 00553 { 00554 //Clean up side effects 00555 netBufferSetLength((NetBuffer *) &frag->buffer, 0); 00556 //Exit immediately 00557 return NULL; 00558 } 00559 00560 //Initial length of the reconstructed datagram 00561 frag->unfragPartLength = sizeof(Ipv6Header); 00562 frag->fragPartLength = 0; 00563 00564 //Fix the length of the first chunk 00565 frag->buffer.chunk[0].length = frag->unfragPartLength; 00566 //Copy IPv6 header from the incoming fragment 00567 netBufferWrite((NetBuffer *) &frag->buffer, 0, packet, frag->unfragPartLength); 00568 00569 //Save current time 00570 frag->timestamp = osGetSystemTime(); 00571 //Record fragment identification field 00572 frag->identification = header->identification; 00573 //Create a new entry in the hole descriptor list 00574 frag->firstHole = 0; 00575 00576 //Point to first hole descriptor 00577 hole = ipv6FindHole(frag, frag->firstHole); 00578 //The entry describes the datagram as being completely missing 00579 hole->first = 0; 00580 hole->last = IPV6_INFINITY; 00581 hole->next = IPV6_INFINITY; 00582 00583 //Dump hole descriptor list 00584 ipv6DumpHoleList(frag); 00585 00586 //Return the matching fragment descriptor 00587 return frag; 00588 } 00589 } 00590 00591 //The reassembly queue is full 00592 return NULL; 00593 } 00594 00595 00596 /** 00597 * @brief Flush IPv6 reassembly queue 00598 * @param[in] interface Underlying network interface 00599 **/ 00600 00601 void ipv6FlushFragQueue(NetInterface *interface) 00602 { 00603 uint_t i; 00604 00605 //Loop through the reassembly queue 00606 for(i = 0; i < IPV6_MAX_FRAG_DATAGRAMS; i++) 00607 { 00608 //Drop any partially reconstructed datagram 00609 netBufferSetLength((NetBuffer *) &interface->ipv6Context.fragQueue[i].buffer, 0); 00610 } 00611 } 00612 00613 00614 /** 00615 * @brief Retrieve hole descriptor 00616 * @param[in] frag IPv6 fragment descriptor 00617 * @param[in] offset Offset of the hole 00618 * @return A pointer to the hole descriptor is returned if the 00619 * specified offset is valid. Otherwise NULL is returned 00620 **/ 00621 00622 Ipv6HoleDesc *ipv6FindHole(Ipv6FragDesc *frag, uint16_t offset) 00623 { 00624 //Return a pointer to the hole descriptor 00625 return netBufferAt((NetBuffer *) &frag->buffer, frag->unfragPartLength + offset); 00626 } 00627 00628 00629 /** 00630 * @brief Dump hole descriptor list 00631 * @param[in] frag IPv6 fragment descriptor 00632 **/ 00633 00634 void ipv6DumpHoleList(Ipv6FragDesc *frag) 00635 { 00636 //Check debugging level 00637 #if (TRACE_LEVEL >= TRACE_LEVEL_DEBUG) 00638 Ipv6HoleDesc *hole; 00639 00640 //Debug message 00641 TRACE_DEBUG("Hole descriptor list:\r\n"); 00642 //Select the first hole descriptor from the list 00643 hole = ipv6FindHole(frag, frag->firstHole); 00644 00645 //Loop through the hole descriptor list 00646 while(hole != NULL) 00647 { 00648 //Display current hole 00649 TRACE_DEBUG(" %" PRIu16 " - %" PRIu16 "\r\n", hole->first, hole->last); 00650 //Select the next hole descriptor from the list 00651 hole = ipv6FindHole(frag, hole->next); 00652 } 00653 #endif 00654 } 00655 00656 #endif 00657
Generated on Tue Jul 12 2022 17:10:14 by
