Webserver+3d print

Dependents:   Nucleo

Revision:
0:8918a71cdbe9
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ipv4/ipv4_frag.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,636 @@
+/**
+ * @file ipv4_frag.c
+ * @brief IPv4 fragmentation and reassembly
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ * @section Description
+ *
+ * The Internet Protocol (IP) implements datagram fragmentation, so that
+ * packets may be formed that can pass through a link with a smaller maximum
+ * transmission unit (MTU) than the original datagram size. Refer to the
+ * following RFCs for complete details:
+ * - RFC 791: Internet Protocol specification
+ * - RFC 815: IP datagram reassembly algorithms
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL IPV4_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "core/ip.h"
+#include "ipv4/ipv4.h"
+#include "ipv4/ipv4_frag.h"
+#include "ipv4/icmp.h"
+#include "mibs/mib2_module.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (IPV4_SUPPORT == ENABLED && IPV4_FRAG_SUPPORT == ENABLED)
+
+//Tick counter to handle periodic operations
+systime_t ipv4FragTickCounter;
+
+
+/**
+ * @brief Fragment an IPv4 datagram into smaller packets
+ * @param[in] interface Underlying network interface
+ * @param[in] pseudoHeader IPv4 pseudo header
+ * @param[in] id Fragment identification
+ * @param[in] payload Multi-part buffer containing the payload
+ * @param[in] payloadOffset Offset to the first payload byte
+ * @param[in] timeToLive TTL value
+ * @return Error code
+ **/
+
+error_t ipv4FragmentDatagram(NetInterface *interface, Ipv4PseudoHeader *pseudoHeader,
+   uint16_t id, const NetBuffer *payload, size_t payloadOffset, uint8_t timeToLive)
+{
+   error_t error;
+   size_t offset;
+   size_t length;
+   size_t payloadLength;
+   size_t fragmentOffset;
+   size_t maxFragmentSize;
+   NetBuffer *fragment;
+
+   //Retrieve the length of the payload
+   payloadLength = netBufferGetLength(payload) - payloadOffset;
+
+   //Allocate a memory buffer to hold IP fragments
+   fragment = ipAllocBuffer(0, &fragmentOffset);
+   //Failed to allocate memory?
+   if(!fragment)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Determine the maximum payload size for fragmented packets
+   maxFragmentSize = interface->ipv4Context.linkMtu - sizeof(Ipv4Header);
+   //The size shall be a multiple of 8-byte blocks
+   maxFragmentSize -= (maxFragmentSize % 8);
+
+   //Initialize error code
+   error = NO_ERROR;
+
+   //Split the payload into multiple IP fragments
+   for(offset = 0; offset < payloadLength; offset += length)
+   {
+      //Flush the contents of the fragment
+      error = netBufferSetLength(fragment, fragmentOffset);
+      //Sanity check
+      if(error)
+         break;
+
+      //Process the last fragment?
+      if((payloadLength - offset) <= maxFragmentSize)
+      {
+         //Size of the current fragment
+         length = payloadLength - offset;
+         //Copy fragment data
+         netBufferConcat(fragment, payload, payloadOffset + offset, length);
+
+         //Do not set the MF flag for the last fragment
+         error = ipv4SendPacket(interface, pseudoHeader, id,
+            offset / 8, fragment, fragmentOffset, timeToLive);
+      }
+      else
+      {
+         //Size of the current fragment (must be a multiple of 8-byte blocks)
+         length = maxFragmentSize;
+         //Copy fragment data
+         netBufferConcat(fragment, payload, payloadOffset + offset, length);
+
+         //Fragmented packets must have the MF flag set
+         error = ipv4SendPacket(interface, pseudoHeader, id,
+            IPV4_FLAG_MF | (offset / 8), fragment, fragmentOffset, timeToLive);
+      }
+
+      //Failed to send current IP packet?
+      if(error)
+         break;
+
+      //Number of IP datagram fragments that have been generated as a result
+      //of fragmentation at this entity
+      MIB2_INC_COUNTER32(mib2Base.ipGroup.ipFragCreates, 1);
+   }
+
+   //Check status code
+   if(error)
+   {
+      //Number of IP datagrams that have been discarded because they needed
+      //to be fragmented at this entity but could not be
+      MIB2_INC_COUNTER32(mib2Base.ipGroup.ipFragFails, 1);
+   }
+   else
+   {
+      //Number of IP datagrams that have been successfully fragmented at
+      //this entity
+      MIB2_INC_COUNTER32(mib2Base.ipGroup.ipFragOKs, 1);
+   }
+
+   //Free previously allocated memory
+   netBufferFree(fragment);
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief IPv4 datagram reassembly algorithm
+ * @param[in] interface Underlying network interface
+ * @param[in] packet Pointer to the IPv4 fragmented packet
+ * @param[in] length Packet length including header and payload
+ **/
+
+void ipv4ReassembleDatagram(NetInterface *interface,
+   const Ipv4Header *packet, size_t length)
+{
+   error_t error;
+   uint16_t offset;
+   uint16_t dataFirst;
+   uint16_t dataLast;
+   Ipv4FragDesc *frag;
+   Ipv4HoleDesc *hole;
+   Ipv4HoleDesc *prevHole;
+
+   //Number of IP fragments received which needed to be reassembled
+   MIB2_INC_COUNTER32(mib2Base.ipGroup.ipReasmReqds, 1);
+
+   //Get the length of the payload
+   length -= packet->headerLength * 4;
+   //Convert the fragment offset from network byte order
+   offset = ntohs(packet->fragmentOffset);
+
+   //Every fragment except the last must contain a multiple of 8 bytes of data
+   if((offset & IPV4_FLAG_MF) && (length % 8))
+   {
+      //Number of failures detected by the IP reassembly algorithm
+      MIB2_INC_COUNTER32(mib2Base.ipGroup.ipReasmFails, 1);
+      //Drop the incoming fragment
+      return;
+   }
+
+   //Calculate the index of the first byte
+   dataFirst = (offset & IPV4_OFFSET_MASK) * 8;
+   //Calculate the index immediately following the last byte
+   dataLast = dataFirst + (uint16_t) length;
+
+   //Search for a matching IP datagram being reassembled
+   frag = ipv4SearchFragQueue(interface, packet);
+
+   //No matching entry in the reassembly queue?
+   if(frag == NULL)
+   {
+      //Number of failures detected by the IP reassembly algorithm
+      MIB2_INC_COUNTER32(mib2Base.ipGroup.ipReasmFails, 1);
+      //Drop the incoming fragment
+      return;
+   }
+
+   //The very first fragment requires special handling
+   if(!(offset & IPV4_OFFSET_MASK))
+   {
+      //Calculate the length of the IP header including options
+      frag->headerLength = packet->headerLength * 4;
+
+      //Enforce the size of the reconstructed datagram
+      if((frag->headerLength + frag->dataLength) > IPV4_MAX_FRAG_DATAGRAM_SIZE)
+      {
+         //Number of failures detected by the IP reassembly algorithm
+         MIB2_INC_COUNTER32(mib2Base.ipGroup.ipReasmFails, 1);
+         //Drop the reconstructed datagram
+         netBufferSetLength((NetBuffer *) &frag->buffer, 0);
+         //Exit immediately
+         return;
+      }
+
+      //Make sure the IP header entirely fits in the first chunk
+      if(frag->headerLength > frag->buffer.chunk[0].size)
+      {
+         //Number of failures detected by the IP reassembly algorithm
+         MIB2_INC_COUNTER32(mib2Base.ipGroup.ipReasmFails, 1);
+         //Drop the reconstructed datagram
+         netBufferSetLength((NetBuffer *) &frag->buffer, 0);
+         //Exit immediately
+         return;
+      }
+
+      //Fix the length of the first chunk
+      frag->buffer.chunk[0].length = frag->headerLength;
+      //Always take the IP header from the first fragment
+      netBufferWrite((NetBuffer *) &frag->buffer, 0, packet, frag->headerLength);
+   }
+
+   //It may be necessary to increase the size of the buffer...
+   if(dataLast > frag->dataLength)
+   {
+      //Enforce the size of the reconstructed datagram
+      if((frag->headerLength + dataLast) > IPV4_MAX_FRAG_DATAGRAM_SIZE)
+      {
+         //Number of failures detected by the IP reassembly algorithm
+         MIB2_INC_COUNTER32(mib2Base.ipGroup.ipReasmFails, 1);
+         //Drop the reconstructed datagram
+         netBufferSetLength((NetBuffer *) &frag->buffer, 0);
+         //Exit immediately
+         return;
+      }
+
+      //Adjust the size of the reconstructed datagram
+      error = netBufferSetLength((NetBuffer *) &frag->buffer,
+         frag->headerLength + dataLast + sizeof(Ipv4HoleDesc));
+
+      //Any error to report?
+      if(error)
+      {
+         //Number of failures detected by the IP reassembly algorithm
+         MIB2_INC_COUNTER32(mib2Base.ipGroup.ipReasmFails, 1);
+         //Drop the reconstructed datagram
+         netBufferSetLength((NetBuffer *) &frag->buffer, 0);
+         //Exit immediately
+         return;
+      }
+
+      //Actual length of the payload
+      frag->dataLength = dataLast;
+   }
+
+   //Select the first hole descriptor from the list
+   hole = ipv4FindHole(frag, frag->firstHole);
+   //Keep track of the previous hole in the list
+   prevHole = NULL;
+
+   //Iterate through the hole descriptors
+   while(hole != NULL)
+   {
+      //Save lower and upper boundaries for later use
+      uint16_t holeFirst = hole->first;
+      uint16_t holeLast = hole->last;
+
+      //Check whether the newly arrived fragment
+      //interacts with this hole in some way
+      if(dataFirst < holeLast && dataLast > holeFirst)
+      {
+         //The current descriptor is no longer valid. We will destroy
+         //it, and in the next two steps, we will determine whether
+         //or not it is necessary to create any new hole descriptors
+         if(prevHole != NULL)
+            prevHole->next = hole->next;
+         else
+            frag->firstHole = hole->next;
+
+         //Is there still a hole at the beginning of the segment?
+         if(dataFirst > holeFirst)
+         {
+            //Create a new entry that describes this hole
+            hole = ipv4FindHole(frag, holeFirst);
+            hole->first = holeFirst;
+            hole->last = dataFirst;
+
+            //Insert the newly created entry into the hole descriptor list
+            if(prevHole != NULL)
+            {
+               hole->next = prevHole->next;
+               prevHole->next = hole->first;
+            }
+            else
+            {
+               hole->next = frag->firstHole;
+               frag->firstHole = hole->first;
+            }
+
+            //Always keep track of the previous hole
+            prevHole = hole;
+         }
+
+         //Is there still a hole at the end of the segment?
+         if(dataLast < holeLast && (offset & IPV4_FLAG_MF))
+         {
+            //Create a new entry that describes this hole
+            hole = ipv4FindHole(frag, dataLast);
+            hole->first = dataLast;
+            hole->last = holeLast;
+
+            //Insert the newly created entry into the hole descriptor list
+            if(prevHole != NULL)
+            {
+               hole->next = prevHole->next;
+               prevHole->next = hole->first;
+            }
+            else
+            {
+               hole->next = frag->firstHole;
+               frag->firstHole = hole->first;
+            }
+
+            //Always keep track of the previous hole
+            prevHole = hole;
+         }
+      }
+      else
+      {
+         //The newly arrived fragment does not interact with the current hole
+         prevHole = hole;
+      }
+
+      //Select the next hole descriptor from the list
+      hole = ipv4FindHole(frag, prevHole ? prevHole->next : frag->firstHole);
+   }
+
+   //Copy data from the fragment to the reassembly buffer
+   netBufferWrite((NetBuffer *) &frag->buffer,
+      frag->headerLength + dataFirst, IPV4_DATA(packet), length);
+
+   //Dump hole descriptor list
+   ipv4DumpHoleList(frag);
+
+   //If the hole descriptor list is empty, the reassembly process is now complete
+   if(!ipv4FindHole(frag, frag->firstHole))
+   {
+      //Discard the extra hole descriptor that follows the reconstructed datagram
+      error = netBufferSetLength((NetBuffer *) &frag->buffer,
+         frag->headerLength + frag->dataLength);
+
+      //Check status code
+      if(error)
+      {
+         //Number of failures detected by the IP reassembly algorithm
+         MIB2_INC_COUNTER32(mib2Base.ipGroup.ipReasmFails, 1);
+      }
+      else
+      {
+         //Point to the IP header
+         Ipv4Header *datagram = netBufferAt((NetBuffer *) &frag->buffer, 0);
+
+         //Fix IP header
+         datagram->totalLength = htons(frag->headerLength + frag->dataLength);
+         datagram->fragmentOffset = 0;
+         datagram->headerChecksum = 0;
+
+         //Number of IP datagrams successfully reassembled
+         MIB2_INC_COUNTER32(mib2Base.ipGroup.ipReasmOKs, 1);
+
+         //Pass the original IPv4 datagram to the higher protocol layer
+         ipv4ProcessDatagram(interface, (NetBuffer *) &frag->buffer);
+      }
+
+      //Release previously allocated memory
+      netBufferSetLength((NetBuffer *) &frag->buffer, 0);
+   }
+}
+
+
+/**
+ * @brief Fragment reassembly timeout handler
+ *
+ * This routine must be periodically called by the TCP/IP stack to
+ * handle IPv4 fragment reassembly timeout
+ *
+ * @param[in] interface Underlying network interface
+ **/
+
+void ipv4FragTick(NetInterface *interface)
+{
+   error_t error;
+   uint_t i;
+   systime_t time;
+   Ipv4HoleDesc *hole;
+
+   //Get current time
+   time = osGetSystemTime();
+
+   //Loop through the reassembly queue
+   for(i = 0; i < IPV4_MAX_FRAG_DATAGRAMS; i++)
+   {
+      //Point to the current entry in the reassembly queue
+      Ipv4FragDesc *frag = &interface->ipv4Context.fragQueue[i];
+
+      //Make sure the entry is currently in use
+      if(frag->buffer.chunkCount > 0)
+      {
+         //If the timer runs out, the partially-reassembled datagram must be
+         //discarded and ICMP Time Exceeded message sent to the source host
+         if((time - frag->timestamp) >= IPV4_FRAG_TIME_TO_LIVE)
+         {
+            //Debug message
+            TRACE_INFO("IPv4 fragment reassembly timeout...\r\n");
+            //Dump IP header contents for debugging purpose
+            ipv4DumpHeader(frag->buffer.chunk[0].address);
+
+            //Number of failures detected by the IP reassembly algorithm
+            MIB2_INC_COUNTER32(mib2Base.ipGroup.ipReasmFails, 1);
+
+            //Point to the first hole descriptor
+            hole = ipv4FindHole(frag, frag->firstHole);
+
+            //Make sure the fragment zero has been received
+            //before sending an ICMP message
+            if(hole != NULL && hole->first > 0)
+            {
+               //Fix the size of the reconstructed datagram
+               error = netBufferSetLength((NetBuffer *) &frag->buffer,
+                  frag->headerLength + hole->first);
+
+               //Check status code
+               if(!error)
+               {
+                  //Send an ICMP Time Exceeded message
+                  icmpSendErrorMessage(interface, ICMP_TYPE_TIME_EXCEEDED,
+                     ICMP_CODE_REASSEMBLY_TIME_EXCEEDED, 0, (NetBuffer *) &frag->buffer, 0);
+               }
+            }
+
+            //Drop the partially reconstructed datagram
+            netBufferSetLength((NetBuffer *) &frag->buffer, 0);
+         }
+      }
+   }
+}
+
+
+/**
+ * @brief Search for a matching datagram in the reassembly queue
+ * @param[in] interface Underlying network interface
+ * @param[in] packet Incoming IPv4 packet
+ * @return Matching fragment descriptor
+ **/
+
+Ipv4FragDesc *ipv4SearchFragQueue(NetInterface *interface, const Ipv4Header *packet)
+{
+   error_t error;
+   uint_t i;
+   Ipv4Header *datagram;
+   Ipv4FragDesc *frag;
+   Ipv4HoleDesc *hole;
+
+   //Search for a matching IP datagram being reassembled
+   for(i = 0; i < IPV4_MAX_FRAG_DATAGRAMS; i++)
+   {
+      //Point to the current entry in the reassembly queue
+      frag = &interface->ipv4Context.fragQueue[i];
+
+      //Check whether the current entry is used?
+      if(frag->buffer.chunkCount > 0)
+      {
+         //Point to the corresponding datagram
+         datagram = netBufferAt((NetBuffer *) &frag->buffer, 0);
+
+         //Check source and destination addresses
+         if(datagram->srcAddr != packet->srcAddr)
+            continue;
+         if(datagram->destAddr != packet->destAddr)
+            continue;
+         //Compare identification and protocol fields
+         if(datagram->identification != packet->identification)
+            continue;
+         if(datagram->protocol != packet->protocol)
+            continue;
+
+         //A matching entry has been found in the reassembly queue
+         return frag;
+      }
+   }
+
+   //If the current packet does not match an existing entry
+   //in the reassembly queue, then create a new entry
+   for(i = 0; i < IPV4_MAX_FRAG_DATAGRAMS; i++)
+   {
+      //Point to the current entry in the reassembly queue
+      frag = &interface->ipv4Context.fragQueue[i];
+
+      //The current entry is free?
+      if(!frag->buffer.chunkCount)
+      {
+         //Number of chunks that comprise the reassembly buffer
+         frag->buffer.maxChunkCount = arraysize(frag->buffer.chunk);
+
+         //Allocate sufficient memory to hold the IPv4 header and
+         //the first hole descriptor
+         error = netBufferSetLength((NetBuffer *) &frag->buffer,
+            NET_MEM_POOL_BUFFER_SIZE + sizeof(Ipv4HoleDesc));
+
+         //Failed to allocate memory?
+         if(error)
+         {
+            //Clean up side effects
+            netBufferSetLength((NetBuffer *) &frag->buffer, 0);
+            //Exit immediately
+            return NULL;
+         }
+
+         //Initial length of the reconstructed datagram
+         frag->headerLength = packet->headerLength * 4;
+         frag->dataLength = 0;
+
+         //Fix the length of the first chunk
+         frag->buffer.chunk[0].length = frag->headerLength;
+         //Copy IPv4 header from the incoming fragment
+         netBufferWrite((NetBuffer *) &frag->buffer, 0, packet, frag->headerLength);
+
+         //Save current time
+         frag->timestamp = osGetSystemTime();
+         //Create a new entry in the hole descriptor list
+         frag->firstHole = 0;
+
+         //Point to first hole descriptor
+         hole = ipv4FindHole(frag, frag->firstHole);
+         //The entry describes the datagram as being completely missing
+         hole->first = 0;
+         hole->last = IPV4_INFINITY;
+         hole->next = IPV4_INFINITY;
+
+         //Dump hole descriptor list
+         ipv4DumpHoleList(frag);
+
+         //Return the matching fragment descriptor
+         return frag;
+      }
+   }
+
+   //The reassembly queue is full
+   return NULL;
+}
+
+
+/**
+ * @brief Flush IPv4 reassembly queue
+ * @param[in] interface Underlying network interface
+ **/
+
+void ipv4FlushFragQueue(NetInterface *interface)
+{
+   uint_t i;
+
+   //Loop through the reassembly queue
+   for(i = 0; i < IPV4_MAX_FRAG_DATAGRAMS; i++)
+   {
+      //Drop any partially reconstructed datagram
+      netBufferSetLength((NetBuffer *) &interface->ipv4Context.fragQueue[i].buffer, 0);
+   }
+}
+
+
+/**
+ * @brief Retrieve hole descriptor
+ * @param[in] frag IPv4 fragment descriptor
+ * @param[in] offset Offset of the hole
+ * @return A pointer to the hole descriptor is returned if the
+ *   specified offset is valid. Otherwise NULL is returned
+ **/
+
+Ipv4HoleDesc *ipv4FindHole(Ipv4FragDesc *frag, uint16_t offset)
+{
+   //Return a pointer to the hole descriptor
+   return netBufferAt((NetBuffer *) &frag->buffer, frag->headerLength + offset);
+}
+
+
+/**
+ * @brief Dump hole descriptor list
+ * @param[in] frag IPv4 fragment descriptor
+ **/
+
+void ipv4DumpHoleList(Ipv4FragDesc *frag)
+{
+//Check debugging level
+#if (TRACE_LEVEL >= TRACE_LEVEL_DEBUG)
+   Ipv4HoleDesc *hole;
+
+   //Debug message
+   TRACE_DEBUG("Hole descriptor list:\r\n");
+   //Select the first hole descriptor from the list
+   hole = ipv4FindHole(frag, frag->firstHole);
+
+   //Loop through the hole descriptor list
+   while(hole != NULL)
+   {
+      //Display current hole
+      TRACE_DEBUG("  %" PRIu16 " - %" PRIu16 "\r\n", hole->first, hole->last);
+      //Select the next hole descriptor from the list
+      hole = ipv4FindHole(frag, hole->next);
+   }
+#endif
+}
+
+#endif
+