Webserver+3d print

Dependents:   Nucleo

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers ipv6_frag.c Source File

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