Ejemplo n.º 1
0
void ipv6FragTick(NetInterface *interface)
{
   error_t error;
   uint_t i;
   systime_t time;
   Ipv6HoleDesc *hole;

   //Acquire exclusive access to the reassembly queue
   osAcquireMutex(&interface->ipv6FragQueueMutex);

   //Get current time
   time = osGetSystemTime();

   //Loop through the reassembly queue
   for(i = 0; i < IPV6_MAX_FRAG_DATAGRAMS; i++)
   {
      //Point to the current entry in the reassembly queue
      Ipv6FragDesc *frag = &interface->ipv6FragQueue[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 ICMPv6 Time Exceeded message sent to the source host
         if((time - frag->timestamp) >= IPV6_FRAG_TIME_TO_LIVE)
         {
            //Debug message
            TRACE_INFO("IPv6 fragment reassembly timeout...\r\n");
            //Dump IP header contents for debugging purpose
            ipv6DumpHeader(frag->buffer.chunk[0].address);

            //Point to the first hole descriptor
            hole = ipv6FindHole(frag, frag->firstHole);

            //Make sure the fragment zero has been received
            //before sending an ICMPv6 message
            if(hole != NULL && hole->first > 0)
            {
               //Fix the size of the reconstructed datagram
               error = chunkedBufferSetLength((ChunkedBuffer *) &frag->buffer,
                  frag->unfragPartLength + hole->first);

               //Check status code
               if(!error)
               {
                  //Send an ICMPv6 Time Exceeded message
                  icmpv6SendErrorMessage(interface, ICMPV6_TYPE_TIME_EXCEEDED,
                     ICMPV6_CODE_REASSEMBLY_TIME_EXCEEDED, 0, (ChunkedBuffer *) &frag->buffer);
               }
            }

            //Drop the partially reconstructed datagram
            chunkedBufferSetLength((ChunkedBuffer *) &frag->buffer, 0);
         }
      }
   }

   //Release exclusive access to the reassembly queue
   osReleaseMutex(&interface->ipv6FragQueueMutex);
}
Ejemplo n.º 2
0
void chunkedBufferFree(ChunkedBuffer *buffer)
{
   //Properly dispose data chunks
   chunkedBufferSetLength(buffer, 0);
   //Release multi-part buffer
   memPoolFree(buffer);
}
Ejemplo n.º 3
0
ChunkedBuffer *chunkedBufferAlloc(size_t length)
{
   error_t error;
   ChunkedBuffer *buffer;

   //Allocate memory to hold the multi-part buffer
   buffer = memPoolAlloc(MEM_POOL_BUFFER_SIZE);
   //Failed to allocate memory?
   if(!buffer) return NULL;

   //The multi-part buffer consists of a single chunk
   buffer->chunkCount = 1;
   buffer->maxChunkCount = MAX_CHUNK_COUNT;
   buffer->chunk[0].address = (uint8_t *) buffer + MAX_CHUNK_COUNT * sizeof(ChunkDesc);
   buffer->chunk[0].length = MEM_POOL_BUFFER_SIZE - MAX_CHUNK_COUNT * sizeof(ChunkDesc);
   buffer->chunk[0].size = 0;

   //Adjust the length of the buffer
   error = chunkedBufferSetLength(buffer, length);
   //Any error to report?
   if(error)
   {
      //Clean up side effects
      chunkedBufferFree(buffer);
      //Report an failure
      return NULL;
   }

   //Successful memory allocation
   return buffer;
}
Ejemplo n.º 4
0
error_t ipv4FragmentDatagram(NetInterface *interface, Ipv4PseudoHeader *pseudoHeader,
   uint16_t id, const ChunkedBuffer *payload, size_t payloadOffset, uint8_t timeToLive)
{
   error_t error;
   size_t offset;
   size_t length;
   size_t payloadLength;
   size_t fragmentOffset;
   ChunkedBuffer *fragment;

   //Retrieve the length of the payload
   payloadLength = chunkedBufferGetLength(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;

   //Split the payload into multiple IP fragments
   for(offset = 0; offset < payloadLength; offset += length)
   {
      //Flush the contents of the fragment
      error = chunkedBufferSetLength(fragment, fragmentOffset);
      //Sanity check
      if(error) break;

      //Process the last fragment?
      if((payloadLength - offset) <= IPV4_MAX_FRAG_SIZE)
      {
         //Size of the current fragment
         length = payloadLength - offset;
         //Copy fragment data
         chunkedBufferConcat(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 = IPV4_MAX_FRAG_SIZE;
         //Copy fragment data
         chunkedBufferConcat(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;
   }

   //Free previously allocated memory
   chunkedBufferFree(fragment);
   //Return status code
   return error;
}
Ejemplo n.º 5
0
void ipv6FlushFragQueue(NetInterface *interface)
{
   uint_t i;

   //Acquire exclusive access to the reassembly queue
   osAcquireMutex(&interface->ipv6FragQueueMutex);

   //Loop through the reassembly queue
   for(i = 0; i < IPV6_MAX_FRAG_DATAGRAMS; i++)
   {
      //Drop any partially reconstructed datagram
      chunkedBufferSetLength((ChunkedBuffer *) &interface->ipv6FragQueue[i].buffer, 0);
   }

   //Release exclusive access to the reassembly queue
   osReleaseMutex(&interface->ipv6FragQueueMutex);
}
Ejemplo n.º 6
0
error_t ndpSendNeighborAdv(NetInterface *interface,
   const Ipv6Addr *targetIpAddr, const Ipv6Addr *destIpAddr)
{
   error_t error;
   size_t offset;
   size_t length;
   ChunkedBuffer *buffer;
   NdpNeighborAdvMessage *message;
   Ipv6PseudoHeader pseudoHeader;

   //Allocate a memory buffer to hold the Neighbor Advertisement
   //message and the Target Link-Layer Address option
   buffer = ipAllocBuffer(sizeof(NdpNeighborAdvMessage) +
      sizeof(NdpLinkLayerAddrOption), &offset);

   //Failed to allocate memory?
   if(!buffer) return ERROR_OUT_OF_MEMORY;

   //Point to the beginning of the message
   message = chunkedBufferAt(buffer, offset);

   //Format Neighbor Advertisement message
   message->type = ICMPV6_TYPE_NEIGHBOR_ADV;
   message->code = 0;
   message->checksum = 0;
   message->reserved1 = 0;
   message->o = TRUE;
   message->s = FALSE;
   message->r = FALSE;
   message->reserved2 = 0;
   message->targetAddr = *targetIpAddr;

   //Length of the message, excluding any option
   length = sizeof(NdpNeighborAdvMessage);

   //Add Target Link-Layer Address option
   ndpAddOption(message, &length, NDP_OPT_TARGET_LINK_LAYER_ADDR,
      &interface->macAddr, sizeof(MacAddr));

   //Adjust the length of the multi-part buffer
   chunkedBufferSetLength(buffer, offset + length);

   //Format IPv6 pseudo header
   pseudoHeader.srcAddr = *targetIpAddr;
   pseudoHeader.destAddr = *destIpAddr;
   pseudoHeader.length = htonl(length);
   pseudoHeader.reserved = 0;
   pseudoHeader.nextHeader = IPV6_ICMPV6_HEADER;

   //Destination IP address is the unspecified address?
   if(ipv6CompAddr(destIpAddr, &IPV6_UNSPECIFIED_ADDR))
   {
      //If the destination is the unspecified address, the node
      //must set the Solicited flag to zero and multicast the
      //advertisement to the all-nodes address
      pseudoHeader.destAddr = IPV6_LINK_LOCAL_ALL_NODES_ADDR;
   }
   else
   {
      //Otherwise, the node must set the Solicited flag to one and
      //unicast the advertisement to the destination IP address
      message->s = TRUE;
   }

   //Calculate ICMPv6 header checksum
   message->checksum = ipCalcUpperLayerChecksumEx(&pseudoHeader,
      sizeof(Ipv6PseudoHeader), buffer, offset, length);

   //Debug message
   TRACE_INFO("Sending Neighbor Advertisement message (%" PRIuSIZE " bytes)...\r\n", length);
   //Dump message contents for debugging purpose
   ndpDumpNeighborAdvMessage(message);

   //Send Neighbor Advertisement message
   error = ipv6SendDatagram(interface, &pseudoHeader, buffer, offset, NDP_HOP_LIMIT);

   //Free previously allocated memory
   chunkedBufferFree(buffer);
   //Return status code
   return error;
}
Ejemplo n.º 7
0
error_t ndpSendNeighborSol(NetInterface *interface, const Ipv6Addr *targetIpAddr)
{
   error_t error;
   size_t offset;
   size_t length;
   ChunkedBuffer *buffer;
   NdpNeighborSolMessage *message;
   Ipv6PseudoHeader pseudoHeader;

   //Allocate a memory buffer to hold the Neighbor Solicitation
   //message and the Source Link-Layer Address option
   buffer = ipAllocBuffer(sizeof(NdpNeighborSolMessage) +
      sizeof(NdpLinkLayerAddrOption), &offset);

   //Failed to allocate memory?
   if(!buffer) return ERROR_OUT_OF_MEMORY;

   //Point to the beginning of the message
   message = chunkedBufferAt(buffer, offset);

   //Format Neighbor Solicitation message
   message->type = ICMPV6_TYPE_NEIGHBOR_SOL;
   message->code = 0;
   message->checksum = 0;
   message->reserved = 0;
   message->targetAddr = *targetIpAddr;

   //Length of the message, excluding any option
   length = sizeof(NdpNeighborSolMessage);

   //Check whether the target address is a tentative address
   if(ipv6IsTentativeAddr(interface, targetIpAddr))
   {
      //The IPv6 source is set to the unspecified address
      pseudoHeader.srcAddr = IPV6_UNSPECIFIED_ADDR;
   }
   else
   {
      //The Source Link-Layer Address option must not be included
      //when the host IPv6 address is unspecified
      if(!ipv6CompAddr(&interface->ipv6Config.linkLocalAddr, &IPV6_UNSPECIFIED_ADDR))
      {
         //Add Source Link-Layer Address option
         ndpAddOption(message, &length, NDP_OPT_SOURCE_LINK_LAYER_ADDR,
            &interface->macAddr, sizeof(MacAddr));
      }

      //Set the IPv6 source address
      pseudoHeader.srcAddr = interface->ipv6Config.linkLocalAddr;
   }

   //Adjust the length of the multi-part buffer
   chunkedBufferSetLength(buffer, offset + length);

   //Compute the solicited-node multicast address that
   //corresponds to the target IPv6 address
   ipv6ComputeSolicitedNodeAddr(targetIpAddr, &pseudoHeader.destAddr);

   //Format IPv6 pseudo header
   pseudoHeader.length = htonl(length);
   pseudoHeader.reserved = 0;
   pseudoHeader.nextHeader = IPV6_ICMPV6_HEADER;

   //Calculate ICMPv6 header checksum
   message->checksum = ipCalcUpperLayerChecksumEx(&pseudoHeader,
      sizeof(Ipv6PseudoHeader), buffer, offset, length);

   //Debug message
   TRACE_INFO("Sending Neighbor Solicitation message (%" PRIuSIZE " bytes)...\r\n", length);
   //Dump message contents for debugging purpose
   ndpDumpNeighborSolMessage(message);

   //Send Neighbor Solicitation message
   error = ipv6SendDatagram(interface, &pseudoHeader, buffer, offset, NDP_HOP_LIMIT);

   //Free previously allocated memory
   chunkedBufferFree(buffer);
   //Return status code
   return error;
}
Ejemplo n.º 8
0
error_t ipv6FragmentDatagram(NetInterface *interface, Ipv6PseudoHeader *pseudoHeader,
   const ChunkedBuffer *payload, size_t payloadOffset, uint8_t hopLimit)
{
   error_t error;
   uint32_t id;
   size_t offset;
   size_t length;
   size_t payloadLength;
   size_t fragmentOffset;
   ChunkedBuffer *fragment;

   //Identification field is used to identify fragments of an original IP datagram
   id = osAtomicInc32(&interface->ipv6Identification);

   //Retrieve the length of the payload
   payloadLength = chunkedBufferGetLength(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;

   //Split the payload into multiple IP fragments
   for(offset = 0; offset < payloadLength; offset += length)
   {
      //Flush the contents of the fragment
      error = chunkedBufferSetLength(fragment, fragmentOffset);
      //Sanity check
      if(error) break;

      //Process the last fragment?
      if((payloadLength - offset) <= IPV6_MAX_FRAG_SIZE)
      {
         //Size of the current fragment
         length = payloadLength - offset;
         //Copy fragment data
         chunkedBufferConcat(fragment, payload, payloadOffset + offset, length);

         //Do not set the MF flag for the last fragment
         error = ipv6SendPacket(interface, pseudoHeader, id,
            offset, fragment, fragmentOffset, hopLimit);
      }
      else
      {
         //Size of the current fragment (must be a multiple of 8-byte blocks)
         length = IPV6_MAX_FRAG_SIZE;
         //Copy fragment data
         chunkedBufferConcat(fragment, payload, payloadOffset + offset, length);

         //Fragmented packets must have the M flag set
         error = ipv6SendPacket(interface, pseudoHeader, id,
            offset | IPV6_FLAG_M, fragment, fragmentOffset, hopLimit);
      }

      //Failed to send current IP fragment?
      if(error) break;
   }

   //Free previously allocated memory
   chunkedBufferFree(fragment);
   //Return status code
   return error;
}
Ejemplo n.º 9
0
Ipv6FragDesc *ipv6SearchFragQueue(NetInterface *interface,
   Ipv6Header *packet, Ipv6FragmentHeader *header)
{
   error_t error;
   uint_t i;
   Ipv6Header *datagram;
   Ipv6FragDesc *frag;
   Ipv6HoleDesc *hole;

   //Search for a matching IP datagram being reassembled
   for(i = 0; i < IPV6_MAX_FRAG_DATAGRAMS; i++)
   {
      //Point to the current entry in the reassembly queue
      frag = &interface->ipv6FragQueue[i];

      //Check whether the current entry is used?
      if(frag->buffer.chunkCount > 0)
      {
         //Point to the corresponding datagram
         datagram = chunkedBufferAt((ChunkedBuffer *) &frag->buffer, 0);

         //Check source and destination addresses
         if(!ipv6CompAddr(&datagram->srcAddr, &packet->srcAddr))
            continue;
         if(!ipv6CompAddr(&datagram->destAddr, &packet->destAddr))
            continue;
         //Compare fragment identification fields
         if(frag->identification != header->identification)
            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 < IPV6_MAX_FRAG_DATAGRAMS; i++)
   {
      //Point to the current entry in the reassembly queue
      frag = &interface->ipv6FragQueue[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 IPv6 header and
         //the first hole descriptor
         error = chunkedBufferSetLength((ChunkedBuffer *) &frag->buffer,
            MEM_POOL_BUFFER_SIZE + sizeof(Ipv6HoleDesc));

         //Failed to allocate memory?
         if(error)
         {
            //Clean up side effects
            chunkedBufferSetLength((ChunkedBuffer *) &frag->buffer, 0);
            //Exit immediately
            return NULL;
         }

         //Initial length of the reconstructed datagram
         frag->unfragPartLength = sizeof(Ipv6Header);
         frag->fragPartLength = 0;

         //Fix the length of the first chunk
         frag->buffer.chunk[0].length = frag->unfragPartLength;
         //Copy IPv6 header from the incoming fragment
         chunkedBufferWrite((ChunkedBuffer *) &frag->buffer, 0, packet, frag->unfragPartLength);

         //Save current time
         frag->timestamp = osGetSystemTime();
         //Record fragment identification field
         frag->identification = header->identification;
         //Create a new entry in the hole descriptor list
         frag->firstHole = 0;

         //Point to first hole descriptor
         hole = ipv6FindHole(frag, frag->firstHole);
         //The entry describes the datagram  as  being completely missing
         hole->first = 0;
         hole->last = IPV6_INFINITY;
         hole->next = IPV6_INFINITY;

         //Dump hole descriptor list
         ipv6DumpHoleList(frag);

         //Return the matching fragment descriptor
         return frag;
      }
   }

   //The reassembly queue is full
   return NULL;
}
Ejemplo n.º 10
0
void ipv6ParseFragmentHeader(NetInterface *interface, const ChunkedBuffer *buffer,
   size_t fragHeaderOffset, size_t nextHeaderOffset)
{
   error_t error;
   size_t length;
   uint16_t offset;
   uint16_t dataFirst;
   uint16_t dataLast;
   Ipv6FragDesc *frag;
   Ipv6HoleDesc *hole;
   Ipv6HoleDesc *prevHole;
   Ipv6Header *packet;
   Ipv6FragmentHeader *header;

   //Remaining bytes to process in the payload
   length = chunkedBufferGetLength(buffer) - fragHeaderOffset;

   //Ensure the fragment header is valid
   if(length < sizeof(Ipv6FragmentHeader))
      return;

   //Point to the IPv6 header
   packet = chunkedBufferAt(buffer, 0);
   //Sanity check
   if(!packet) return;

   //Point to the IPv6 Fragment header
   header = chunkedBufferAt(buffer, fragHeaderOffset);
   //Sanity check
   if(!header) return;

   //Calculate the length of the fragment
   length -= sizeof(Ipv6FragmentHeader);
   //Convert the fragment offset from network byte order
   offset = ntohs(header->fragmentOffset);

   //Every fragment except the last must contain a multiple of 8 bytes of data
   if((offset & IPV6_FLAG_M) && (length % 8))
   {
      //Compute the offset of the Payload Length field within the packet
      size_t n = (uint8_t *) &packet->payloadLength - (uint8_t *) packet;

      //The fragment must be discarded and an ICMP Parameter Problem
      //message should be sent to the source of the fragment, pointing
      //to the Payload Length field of the fragment packet
      icmpv6SendErrorMessage(interface, ICMPV6_TYPE_PARAM_PROBLEM,
         ICMPV6_CODE_INVALID_HEADER_FIELD, n, buffer);

      //Exit immediately
      return;
   }

   //Calculate the index of the first byte
   dataFirst = offset & IPV6_OFFSET_MASK;
   //Calculate the index immediately following the last byte
   dataLast = dataFirst + length;

   //Search for a matching IP datagram being reassembled
   frag = ipv6SearchFragQueue(interface, packet, header);
   //No matching entry in the reassembly queue?
   if(!frag) return;

   //The very first fragment requires special handling
   if(!(offset & IPV6_OFFSET_MASK))
   {
      uint8_t *p;

      //Calculate the length of the unfragmentable part
      frag->unfragPartLength = fragHeaderOffset;

      //The size of the reconstructed datagram exceeds the maximum value?
      if((frag->unfragPartLength + frag->fragPartLength) > IPV6_MAX_FRAG_DATAGRAM_SIZE)
      {
         //Compute the offset of the Fragment Offset field within the packet
         size_t n = fragHeaderOffset + (uint8_t *) &header->fragmentOffset -
            (uint8_t *) header;

         //The fragment must be discarded and an ICMP Parameter Problem
         //message should be sent to the source of the fragment, pointing
         //to the Fragment Offset field of the fragment packet
         icmpv6SendErrorMessage(interface, ICMPV6_TYPE_PARAM_PROBLEM,
            ICMPV6_CODE_INVALID_HEADER_FIELD, n, buffer);

         //Drop any allocated resources
         chunkedBufferSetLength((ChunkedBuffer *) &frag->buffer, 0);
         //Exit immediately
         return;
      }

      //Make sure the unfragmentable part entirely fits in the first chunk
      if(frag->unfragPartLength > frag->buffer.chunk[0].size)
      {
         //Drop the reconstructed datagram
         chunkedBufferSetLength((ChunkedBuffer *) &frag->buffer, 0);
         //Exit immediately
         return;
      }

      //Fix the length of the first chunk
      frag->buffer.chunk[0].length = frag->unfragPartLength;

      //The unfragmentable part of the reassembled packet consists
      //of all headers up to, but not including, the Fragment header
      //of the first fragment packet
      chunkedBufferCopy((ChunkedBuffer *) &frag->buffer, 0, buffer, 0, frag->unfragPartLength);

      //Point to the Next Header field of the last header
      p = chunkedBufferAt((ChunkedBuffer *) &frag->buffer, nextHeaderOffset);

      //The Next Header field of the last header of the unfragmentable
      //part is obtained from the Next Header field of the first
      //fragment's Fragment header
      *p = header->nextHeader;
   }

   //It may be necessary to increase the size of the buffer...
   if(dataLast > frag->fragPartLength)
   {
      //The size of the reconstructed datagram exceeds the maximum value?
      if((frag->unfragPartLength + dataLast) > IPV6_MAX_FRAG_DATAGRAM_SIZE)
      {
         //Compute the offset of the Fragment Offset field within the packet
         size_t n = fragHeaderOffset + (uint8_t *) &header->fragmentOffset - (uint8_t *) header;

         //The fragment must be discarded and an ICMP Parameter Problem
         //message should be sent to the source of the fragment, pointing
         //to the Fragment Offset field of the fragment packet
         icmpv6SendErrorMessage(interface, ICMPV6_TYPE_PARAM_PROBLEM,
            ICMPV6_CODE_INVALID_HEADER_FIELD, n, buffer);

         //Drop any allocated resources
         chunkedBufferSetLength((ChunkedBuffer *) &frag->buffer, 0);
         //Exit immediately
         return;
      }

      //Adjust the size of the reconstructed datagram
      error = chunkedBufferSetLength((ChunkedBuffer *) &frag->buffer,
         frag->unfragPartLength + dataLast + sizeof(Ipv6HoleDesc));

      //Any error to report?
      if(error)
      {
         //Drop the reconstructed datagram
         chunkedBufferSetLength((ChunkedBuffer *) &frag->buffer, 0);
         //Exit immediately
         return;
      }

      //Actual length of the fragmentable part
      frag->fragPartLength = dataLast;
   }

   //Select the first hole descriptor from the list
   hole = ipv6FindHole(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 = ipv6FindHole(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 & IPV6_FLAG_M))
         {
            //Create a new entry that describes this hole
            hole = ipv6FindHole(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 = ipv6FindHole(frag, prevHole ? prevHole->next : frag->firstHole);
   }

   //Copy data from the fragment to the reassembly buffer
   chunkedBufferCopy((ChunkedBuffer *) &frag->buffer, frag->unfragPartLength + dataFirst,
      buffer, fragHeaderOffset + sizeof(Ipv6FragmentHeader), length);

   //Dump hole descriptor list
   ipv6DumpHoleList(frag);

   //If the hole descriptor list is empty, the reassembly process is now complete
   if(!ipv6FindHole(frag, frag->firstHole))
   {
      //Discard the extra hole descriptor that follows the reconstructed datagram
      error = chunkedBufferSetLength((ChunkedBuffer *) &frag->buffer,
         frag->unfragPartLength + frag->fragPartLength);

      //Check status code
      if(!error)
      {
         //Point to the IPv6 header
         Ipv6Header *datagram = chunkedBufferAt((ChunkedBuffer *) &frag->buffer, 0);

         //Fix the Payload Length field
         datagram->payloadLength = htons(frag->unfragPartLength +
            frag->fragPartLength - sizeof(Ipv6Header));

         //Pass the original IPv6 datagram to the higher protocol layer
         ipv6ProcessPacket(interface, (ChunkedBuffer *) &frag->buffer);
      }

      //Release previously allocated memory
      chunkedBufferSetLength((ChunkedBuffer *) &frag->buffer, 0);
   }
}
Ejemplo n.º 11
0
error_t nbnsSendResponse(NetInterface *interface,
   const IpAddr *destIpAddr, uint16_t destPort, uint16_t id)
{
   error_t error;
   size_t length;
   size_t offset;
   ChunkedBuffer *buffer;
   NbnsHeader *message;
   NbnsAddrEntry *addrEntry;
   DnsResourceRecord *resourceRecord;

   //Allocate a memory buffer to hold the NBNS response message
   buffer = udpAllocBuffer(DNS_MESSAGE_MAX_SIZE, &offset);
   //Failed to allocate buffer?
   if(!buffer) return ERROR_OUT_OF_MEMORY;

   //Point to the NBNS header
   message = chunkedBufferAt(buffer, offset);

   //Take the identifier from the query message
   message->id = id;

   //Format NBNS response header
   message->qr = 1;
   message->opcode = DNS_OPCODE_QUERY;
   message->aa = 1;
   message->tc = 0;
   message->rd = 1;
   message->ra = 1;
   message->z = 0;
   message->b = 0;
   message->rcode = DNS_RCODE_NO_ERROR;

   //The NBNS response contains 1 answer resource record
   message->qdcount = 0;
   message->ancount = HTONS(1);
   message->nscount = 0;
   message->arcount = 0;

   //NBNS response message length
   length = sizeof(DnsHeader);

   //Encode the host name using the NBNS name notation
   length += nbnsEncodeName(interface->hostname, (uint8_t *) message + length);

   //Point to the corresponding resource record
   resourceRecord = DNS_GET_RESOURCE_RECORD(message, length);
   //Fill in resource record
   resourceRecord->rtype = HTONS(DNS_RR_TYPE_NB);
   resourceRecord->rclass = HTONS(DNS_RR_CLASS_IN);
   resourceRecord->ttl = HTONL(NBNS_DEFAULT_RESOURCE_RECORD_TTL);
   resourceRecord->rdlength = HTONS(sizeof(NbnsAddrEntry));

   //Point to the address entry array
   addrEntry = (NbnsAddrEntry *) resourceRecord->rdata;
   //Fill in address entry
   addrEntry->flags = HTONS(NBNS_G_UNIQUE | NBNS_ONT_BNODE);
   addrEntry->addr = interface->ipv4Config.addr;

   //Update the length of the NBNS response message
   length += sizeof(DnsResourceRecord) + sizeof(NbnsAddrEntry);

   //Adjust the length of the multi-part buffer
   chunkedBufferSetLength(buffer, offset + length);

   //Debug message
   TRACE_INFO("Sending NBNS message (%" PRIuSIZE " bytes)...\r\n", length);
   //Dump message
   dnsDumpMessage((DnsHeader *) message, length);

   //A response packet is always sent to the source UDP port and
   //source IP address of the request packet
   error = udpSendDatagramEx(interface, NBNS_PORT, destIpAddr,
      destPort, buffer, offset, IPV4_DEFAULT_TTL);

   //Free previously allocated memory
   chunkedBufferFree(buffer);
   //Return status code
   return error;
}
Ejemplo n.º 12
0
error_t dnsSendQuery(DnsCacheEntry *entry)
{
   error_t error;
   size_t length;
   size_t offset;
   ChunkedBuffer *buffer;
   DnsHeader *message;
   DnsQuestion *dnsQuestion;
   IpAddr destIpAddr;

#if (IPV4_SUPPORT == ENABLED)
   //An IPv4 address is expected?
   if(entry->type == HOST_TYPE_IPV4)
   {
      //Select the relevant DNS server
      destIpAddr.length = sizeof(Ipv4Addr);
      ipv4GetDnsServer(entry->interface, entry->dnsServerNum, &destIpAddr.ipv4Addr);

      //Make sure the IP address is valid
      if(destIpAddr.ipv4Addr == IPV4_UNSPECIFIED_ADDR)
         return ERROR_NO_DNS_SERVER;
   }
   else
#endif
#if (IPV6_SUPPORT == ENABLED)
   //An IPv6 address is expected?
   if(entry->type == HOST_TYPE_IPV6)
   {
      //Select the relevant DNS server
      destIpAddr.length = sizeof(Ipv6Addr);
      ipv6GetDnsServer(entry->interface, entry->dnsServerNum, &destIpAddr.ipv6Addr);

      //Make sure the IP address is valid
      if(ipv6CompAddr(&destIpAddr.ipv6Addr, &IPV6_UNSPECIFIED_ADDR))
         return ERROR_NO_DNS_SERVER;
   }
   else
#endif
   //Invalid host type?
   {
      //Report an error
      return ERROR_INVALID_PARAMETER;
   }

   //Allocate a memory buffer to hold the DNS query message
   buffer = udpAllocBuffer(DNS_MESSAGE_MAX_SIZE, &offset);
   //Failed to allocate buffer?
   if(!buffer) return ERROR_OUT_OF_MEMORY;

   //Point to the DNS header
   message = chunkedBufferAt(buffer, offset);

   //Format DNS query message
   message->id = htons(entry->id);
   message->qr = 0;
   message->opcode = DNS_OPCODE_QUERY;
   message->aa = 0;
   message->tc = 0;
   message->rd = 1;
   message->ra = 0;
   message->z = 0;
   message->rcode = DNS_RCODE_NO_ERROR;

   //The DNS query contains one question
   message->qdcount = HTONS(1);
   message->ancount = 0;
   message->nscount = 0;
   message->arcount = 0;

   //Length of the DNS query message
   length = sizeof(DnsHeader);

   //Encode the host name using the DNS name notation
   length += dnsEncodeName(entry->name, message->questions);

   //Point to the corresponding question structure
   dnsQuestion = DNS_GET_QUESTION(message, length);

#if (IPV4_SUPPORT == ENABLED)
   //An IPv4 address is expected?
   if(entry->type == HOST_TYPE_IPV4)
   {
      //Fill in question structure
      dnsQuestion->qtype = HTONS(DNS_RR_TYPE_A);
      dnsQuestion->qclass = HTONS(DNS_RR_CLASS_IN);
   }
#endif
#if (IPV6_SUPPORT == ENABLED)
   //An IPv6 address is expected?
   if(entry->type == HOST_TYPE_IPV6)
   {
      //Fill in question structure
      dnsQuestion->qtype = HTONS(DNS_RR_TYPE_AAAA);
      dnsQuestion->qclass = HTONS(DNS_RR_CLASS_IN);
   }
#endif

   //Update the length of the DNS query message
   length += sizeof(DnsQuestion);

   //Adjust the length of the multi-part buffer
   chunkedBufferSetLength(buffer, offset + length);

   //Debug message
   TRACE_INFO("Sending DNS message (%" PRIuSIZE " bytes)...\r\n", length);
   //Dump message
   dnsDumpMessage(message, length);

   //Send DNS query message
   error = udpSendDatagramEx(entry->interface, entry->port,
      &destIpAddr, DNS_PORT, buffer, offset, 0);

   //Free previously allocated memory
   chunkedBufferFree(buffer);
   //Return status code
   return error;
}
Ejemplo n.º 13
0
void ipv4ReassembleDatagram(NetInterface *interface,
   const MacAddr *srcMacAddr, const Ipv4Header *packet, size_t length)
{
   error_t error;
   uint16_t offset;
   uint16_t dataFirst;
   uint16_t dataLast;
   Ipv4FragDesc *frag;
   Ipv4HoleDesc *hole;
   Ipv4HoleDesc *prevHole;

   //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))
   {
      //Drop incoming packet
      return;
   }

   //Calculate the index of the first byte
   dataFirst = (offset & IPV4_OFFSET_MASK) * 8;
   //Calculate the index immediately following the last byte
   dataLast = dataFirst + length;

   //Search for a matching IP datagram being reassembled
   frag = ipv4SearchFragQueue(interface, packet);
   //No matching entry in the reassembly queue?
   if(!frag) 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)
      {
         //Drop any allocated resources
         chunkedBufferSetLength((ChunkedBuffer *) &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)
      {
         //Drop the reconstructed datagram
         chunkedBufferSetLength((ChunkedBuffer *) &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
      chunkedBufferWrite((ChunkedBuffer *) &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)
      {
         //Drop any allocated resources
         chunkedBufferSetLength((ChunkedBuffer *) &frag->buffer, 0);
         //Exit immediately
         return;
      }

      //Adjust the size of the reconstructed datagram
      error = chunkedBufferSetLength((ChunkedBuffer *) &frag->buffer,
         frag->headerLength + dataLast + sizeof(Ipv4HoleDesc));

      //Any error to report?
      if(error)
      {
         //Drop the reconstructed datagram
         chunkedBufferSetLength((ChunkedBuffer *) &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
   chunkedBufferWrite((ChunkedBuffer *) &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 = chunkedBufferSetLength((ChunkedBuffer *) &frag->buffer,
         frag->headerLength + frag->dataLength);

      //Check status code
      if(!error)
      {
         //Point to the IP header
         Ipv4Header *datagram = chunkedBufferAt((ChunkedBuffer *) &frag->buffer, 0);

         //Fix IP header
         datagram->totalLength = htons(frag->headerLength + frag->dataLength);
         datagram->fragmentOffset = 0;
         datagram->headerChecksum = 0;

         //Recalculate IP header checksum
         datagram->headerChecksum = ipCalcChecksum(datagram, frag->buffer.chunk[0].length);

         //Pass the original IPv4 datagram to the higher protocol layer
         ipv4ProcessDatagram(interface, srcMacAddr, (ChunkedBuffer *) &frag->buffer);
      }

      //Release previously allocated memory
      chunkedBufferSetLength((ChunkedBuffer *) &frag->buffer, 0);
   }
}
Ejemplo n.º 14
0
error_t tcpConnect(Socket *socket)
{
   error_t error;
   uint_t event;

   //Socket already connected?
   if(socket->state != TCP_STATE_CLOSED)
      return ERROR_ALREADY_CONNECTED;

   //The user owns the socket
   socket->ownedFlag = TRUE;

   //Number of chunks that comprise the TX and the RX buffers
   socket->txBuffer.maxChunkCount = arraysize(socket->txBuffer.chunk);
   socket->rxBuffer.maxChunkCount = arraysize(socket->rxBuffer.chunk);

   //Allocate transmit buffer
   error = chunkedBufferSetLength((ChunkedBuffer *) &socket->txBuffer, socket->txBufferSize);
   //Allocate receive buffer
   if(!error)
      error = chunkedBufferSetLength((ChunkedBuffer *) &socket->rxBuffer, socket->rxBufferSize);

   //Failed to allocate memory?
   if(error)
   {
      //Free any previously allocated memory
      tcpDeleteControlBlock(socket);
      //Report an error to the caller
      return error;
   }

   //Default MSS value
   socket->mss = min(TCP_DEFAULT_MSS, TCP_MAX_MSS);
   //An initial send sequence number is selected
   socket->iss = rand();
   //Initialize TCP control block
   socket->sndUna = socket->iss;
   socket->sndNxt = socket->iss + 1;
   socket->rcvUser = 0;
   socket->rcvWnd = socket->rxBufferSize;
   //Default retransmission timeout
   socket->rto = TCP_INITIAL_RTO;

   //Send a SYN segment
   error = tcpSendSegment(socket, TCP_FLAG_SYN, socket->iss, 0, 0, TRUE);
   //Failed to send TCP segment?
   if(error) return error;

   //Switch to the SYN-SENT state
   tcpChangeState(socket, TCP_STATE_SYN_SENT);
   //Wait for the connection to be established
   event = tcpWaitForEvents(socket, SOCKET_EVENT_CONNECTED |
      SOCKET_EVENT_CLOSED, socket->timeout);

   //Connection successfully established?
   if(event == SOCKET_EVENT_CONNECTED)
      return NO_ERROR;
   //Failed to establish connection?
   else if(event == SOCKET_EVENT_CLOSED)
      return ERROR_CONNECTION_FAILED;
   //Timeout exception?
   else
      return ERROR_TIMEOUT;
}
Ejemplo n.º 15
0
Socket *tcpAccept(Socket *socket, IpAddr *clientIpAddr, uint16_t *clientPort)
{
   error_t error;
   TcpSynQueueItem *queueItem;
   Socket *newSocket;

   //Ensure the socket was previously placed in the listening state
   if(tcpGetState(socket) != TCP_STATE_LISTEN)
      return NULL;

   //Enter critical section
   osMutexAcquire(socketMutex);

   //Wait for an connection attempt
   while(1)
   {
      //The SYN queue is empty?
      if(!socket->synQueue)
      {
         //Set the events the application is interested in
         socket->eventMask = SOCKET_EVENT_RX_READY;
         //Reset the event object
         osEventReset(socket->event);
         //Leave critical section
         osMutexRelease(socketMutex);
         //Wait until a SYN message is received from a client
         osEventWait(socket->event, socket->timeout);
         //Enter critical section
         osMutexAcquire(socketMutex);
      }

      //Check whether the queue is still empty
      if(!socket->synQueue)
      {
         //Leave critical section
         osMutexRelease(socketMutex);
         //Timeout error
         return NULL;
      }

      //Point to the first item in the receive queue
      queueItem = socket->synQueue;
      //Return the client IP address and port number
      if(clientIpAddr)
         *clientIpAddr = queueItem->srcAddr;
      if(clientPort)
         *clientPort = queueItem->srcPort;

      //Leave critical section
      osMutexRelease(socketMutex);

      //Create a new socket to handle the incoming connection request
      newSocket = socketOpen(SOCKET_TYPE_STREAM, SOCKET_PROTOCOL_TCP);
      //Failed to open socket?
      if(!newSocket)
      {
         //Debug message
         TRACE_WARNING("Cannot accept TCP connection!\r\n");

         //Remove the item from the SYN queue
         socket->synQueue = queueItem->next;
         //Deallocate memory buffer
         memPoolFree(queueItem);
         //Wait for the next connection attempt
         continue;
      }

      //Enter critical section
      osMutexAcquire(socketMutex);

      //The user owns the socket
      newSocket->ownedFlag = TRUE;

      //Number of chunks that comprise the TX and the RX buffers
      newSocket->txBuffer.maxChunkCount = arraysize(newSocket->txBuffer.chunk);
      newSocket->rxBuffer.maxChunkCount = arraysize(newSocket->rxBuffer.chunk);

      //Allocate transmit buffer
      error = chunkedBufferSetLength((ChunkedBuffer *) &newSocket->txBuffer, newSocket->txBufferSize);
      //Allocate receive buffer
      if(!error)
         error = chunkedBufferSetLength((ChunkedBuffer *) &newSocket->rxBuffer, newSocket->rxBufferSize);

      //Failed to allocate memory?
      if(error)
      {
         //Debug message
         TRACE_WARNING("Cannot accept TCP connection!\r\n");

         //Properly close the socket
         tcpAbort(newSocket);
         //Remove the item from the SYN queue
         socket->synQueue = queueItem->next;
         //Deallocate memory buffer
         memPoolFree(queueItem);
         //Wait for the next connection attempt
         continue;
      }

      //Bind the newly created socket to the appropriate interface
      newSocket->interface = queueItem->interface;
      //Bind the socket to the specified address
      newSocket->localIpAddr = queueItem->destAddr;
      newSocket->localPort = socket->localPort;
      //Save the port number and the IP address of the remote host
      newSocket->remoteIpAddr = queueItem->srcAddr;
      newSocket->remotePort = queueItem->srcPort;
      //Save the maximum segment size
      newSocket->mss = queueItem->mss;

      //Initialize TCP control block
      newSocket->iss = rand();
      newSocket->irs = queueItem->isn;
      newSocket->sndUna = newSocket->iss;
      newSocket->sndNxt = newSocket->iss + 1;
      newSocket->rcvNxt = newSocket->irs + 1;
      newSocket->rcvUser = 0;
      newSocket->rcvWnd = newSocket->rxBufferSize;

      //Default retransmission timeout
      newSocket->rto = TCP_INITIAL_RTO;
      //Initial congestion window
      newSocket->cwnd = min(TCP_INITIAL_WINDOW * newSocket->mss, newSocket->txBufferSize);
      //Slow start threshold should be set arbitrarily high
      newSocket->ssthresh = UINT16_MAX;

      //Send a SYN ACK control segment
      error = tcpSendSegment(newSocket, TCP_FLAG_SYN | TCP_FLAG_ACK,
         newSocket->iss, newSocket->rcvNxt, 0, TRUE);
      //Failed to send TCP segment?
      if(error)
      {
         //Debug message
         TRACE_WARNING("Cannot accept TCP connection!\r\n");

         //Close previously created socket
         tcpAbort(newSocket);
         //Remove the item from the SYN queue
         socket->synQueue = queueItem->next;
         //Deallocate memory buffer
         memPoolFree(queueItem);
         //Wait for the next connection attempt
         continue;
      }

      //Remove the item from the SYN queue
      socket->synQueue = queueItem->next;
      //Deallocate memory buffer
      memPoolFree(queueItem);
      //Update the state of events
      tcpUpdateEvents(socket);

      //The connection state should be changed to SYN-RECEIVED
      tcpChangeState(newSocket, TCP_STATE_SYN_RECEIVED);

      //Leave critical section
      osMutexRelease(socketMutex);
      //Return a handle to the newly created socket
      return newSocket;
   }
}