Esempio n. 1
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;
}
Esempio n. 2
0
void mldProcessListenerReport(NetInterface *interface, Ipv6PseudoHeader *pseudoHeader,
   const ChunkedBuffer *buffer, size_t offset, uint8_t hopLimit)
{
   uint_t i;
   size_t length;
   MldMessage *message;
   Ipv6FilterEntry *entry;

   //Retrieve the length of the MLD message
   length = chunkedBufferGetLength(buffer) - offset;

   //The message must be at least 24 octets long
   if(length < sizeof(MldMessage))
      return;

   //Point to the beginning of the MLD message
   message = chunkedBufferAt(buffer, offset);
   //Sanity check
   if(!message) return;

   //Debug message
   TRACE_INFO("MLD message received (%" PRIuSIZE " bytes)...\r\n", length);
   //Dump message contents for debugging purpose
   mldDumpMessage(message);

   //Make sure the source address of the message is a valid link-local address
   if(!ipv6IsLinkLocalUnicastAddr(&pseudoHeader->srcAddr))
      return;
   //Check the Hop Limit field
   if(hopLimit != MLD_HOP_LIMIT)
      return;

   //Acquire exclusive access to the IPv6 filter table
   osMutexAcquire(interface->ipv6FilterMutex);

   //Loop through filter table entries
   for(i = 0; i < interface->ipv6FilterSize; i++)
   {
      //Point to the current entry
      entry = &interface->ipv6Filter[i];

      //Report messages are ignored for multicast addresses
      //in the Non-Listener or Idle Listener state
      if(entry->state == MLD_STATE_DELAYING_LISTENER)
      {
         //The Multicast Listener Report message matches the current entry?
         if(ipv6CompAddr(&message->multicastAddr, &entry->addr))
         {
            //Clear flag
            entry->flag = FALSE;
            //Switch to the Idle Listener state
            entry->state = MLD_STATE_IDLE_LISTENER;
         }
      }
   }

   //Release exclusive access to the IPv6 filter table
   osMutexRelease(interface->ipv6FilterMutex);
}
Esempio n. 3
0
error_t dm9000SendPacket(NetInterface *interface,
   const ChunkedBuffer *buffer, size_t offset)
{
   size_t i;
   uint16_t *p;

   //Point to the driver context
   Dm9000Context *context = (Dm9000Context *) interface->nicContext;

   //Retrieve the length of the packet
   size_t length = chunkedBufferGetLength(buffer) - offset;

   //Check the frame length
   if(length > 1536)
   {
      //The transmitter can accept another packet
      osSetEvent(&interface->nicTxEvent);
      //Report an error
      return ERROR_INVALID_LENGTH;
   }

   //Copy user data
   chunkedBufferRead(txBuffer, buffer, offset, length);

   //A dummy write is required before accessing FIFO
   dm9000WriteReg(DM9000_REG_MWCMDX, 0);
   //Select MWCMD register
   *DM9000_INDEX_REG = DM9000_REG_MWCMD;

   //Point to the beginning of the buffer
   p = (uint16_t *) txBuffer;

   //Write data to the FIFO using 16-bit mode
   for(i = length; i > 1; i -= 2)
      *DM9000_DATA_REG = *(p++);

   //Odd number of bytes?
   if(i > 0)
      *DM9000_DATA_REG = *((uint8_t *) p);

   //Write the number of bytes to send
   dm9000WriteReg(DM9000_REG_TXPLL, LSB(length));
   dm9000WriteReg(DM9000_REG_TXPLH, MSB(length));

   //Clear interrupt flag
   dm9000WriteReg(DM9000_REG_ISR, ISR_PT);
   //Start data transfer
   dm9000WriteReg(DM9000_REG_TCR, TCR_TXREQ);

   //The packet was successfully written to FIFO
   context->queuedPackets++;

   //Successful processing
   return NO_ERROR;
}
Esempio n. 4
0
error_t sam9263EthSendPacket(NetInterface *interface,
   const ChunkedBuffer *buffer, size_t offset)
{
   //Retrieve the length of the packet
   size_t length = chunkedBufferGetLength(buffer) - offset;

   //Check the frame length
   if(length > SAM9263_ETH_TX_BUFFER_SIZE)
   {
      //The transmitter can accept another packet
      osSetEvent(&interface->nicTxEvent);
      //Report an error
      return ERROR_INVALID_LENGTH;
   }

   //Make sure the current buffer is available for writing
   if(!(txBufferDesc[txBufferIndex].status & AT91C_EMAC_TX_USED))
      return ERROR_FAILURE;

   //Copy user data to the transmit buffer
   chunkedBufferRead(txBuffer[txBufferIndex], buffer, offset, length);

   //Set the necessary flags in the descriptor entry
   if(txBufferIndex < (SAM9263_ETH_TX_BUFFER_COUNT - 1))
   {
      //Write the status word
      txBufferDesc[txBufferIndex].status =
         AT91C_EMAC_TX_LAST | (length & AT91C_EMAC_TX_LENGTH);

      //Point to the next buffer
      txBufferIndex++;
   }
   else
   {
      //Write the status word
      txBufferDesc[txBufferIndex].status = AT91C_EMAC_TX_WRAP |
         AT91C_EMAC_TX_LAST | (length & AT91C_EMAC_TX_LENGTH);

      //Wrap around
      txBufferIndex = 0;
   }

   //Set the TSTART bit to initiate transmission
   AT91C_BASE_EMAC->EMAC_NCR |= AT91C_EMAC_TSTART;

   //Check whether the next buffer is available for writing
   if(txBufferDesc[txBufferIndex].status & AT91C_EMAC_TX_USED)
   {
      //The transmitter can accept another packet
      osSetEvent(&interface->nicTxEvent);
   }

   //Successful processing
   return NO_ERROR;
}
Esempio n. 5
0
void icmpProcessMessage(NetInterface *interface,
                        Ipv4Addr srcIpAddr, const ChunkedBuffer *buffer, size_t offset)
{
    size_t length;
    IcmpHeader *header;

    //Retrieve the length of the ICMP message
    length = chunkedBufferGetLength(buffer) - offset;

    //Ensure the message length is correct
    if(length < sizeof(IcmpHeader))
    {
        //Debug message
        TRACE_WARNING("ICMP message length is invalid!\r\n");
        //Silently discard incoming message
        return;
    }

    //Point to the ICMP message header
    header = chunkedBufferAt(buffer, offset);
    //Sanity check
    if(!header) return;

    //Debug message
    TRACE_INFO("ICMP message received (%" PRIuSIZE " bytes)...\r\n", length);
    //Dump message contents for debugging purpose
    icmpDumpMessage(header);

    //Verify checksum value
    if(ipCalcChecksumEx(buffer, offset, length) != 0x0000)
    {
        //Debug message
        TRACE_WARNING("Wrong ICMP header checksum!\r\n");
        //Drop incoming message
        return;
    }

    //Check the type of ICMP message
    switch(header->type)
    {
    //Echo request?
    case ICMP_TYPE_ECHO_REQUEST:
        //Process Echo Request message
        icmpProcessEchoRequest(interface, srcIpAddr, buffer, offset);
        break;
    //Unknown type?
    default:
        //Debug message
        TRACE_WARNING("Unknown ICMP message type!\r\n");
        //Discard incoming ICMP message
        break;
    }
}
Esempio n. 6
0
void mdnsProcessMessage(NetInterface *interface, const IpPseudoHeader *pseudoHeader,
   const UdpHeader *udpHeader, const ChunkedBuffer *buffer, size_t offset, void *params)
{
   size_t length;
   DnsHeader *message;

   //Retrieve the length of the mDNS message
   length = chunkedBufferGetLength(buffer) - offset;

   //Ensure the mDNS message is valid
   if(length < sizeof(DnsHeader))
      return;
   if(length > DNS_MESSAGE_MAX_SIZE)
      return;

   //Point to the mDNS message header
   message = chunkedBufferAt(buffer, offset);
   //Sanity check
   if(!message) return;

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

   //mDNS messages received with an opcode other than zero must be silently ignored
   if(message->opcode != DNS_OPCODE_QUERY)
      return;
   //mDNS messages received with non-zero response codes must be silently ignored
   if(message->rcode != DNS_RCODE_NO_ERROR)
      return;

#if (MDNS_RESPONDER_SUPPORT == ENABLED)
   //mDNS query received?
   if(!message->qr)
   {
      //Process incoming mDNS query message
      mdnsProcessQuery(interface, pseudoHeader, udpHeader, message, length);
   }
#endif
#if (MDNS_CLIENT_SUPPORT == ENABLED)
   //mDNS response received?
   if(message->qr)
   {
      //Process incoming mDNS response message
      mdnsProcessResponse(interface, pseudoHeader, udpHeader, message, length);
   }
#endif
}
Esempio n. 7
0
error_t m2sxxxEthSendPacket(NetInterface *interface,
   const ChunkedBuffer *buffer, size_t offset)
{
   //Retrieve the length of the packet
   size_t length = chunkedBufferGetLength(buffer) - offset;

   //Check the frame length
   if(length > M2SXXX_ETH_TX_BUFFER_SIZE)
   {
      //The transmitter can accept another packet
      osSetEvent(&interface->nicTxEvent);
      //Report an error
      return ERROR_INVALID_LENGTH;
   }

   //Make sure the current buffer is available for writing
   if(!(txCurDmaDesc->size & DMA_DESC_EMPTY_FLAG))
      return ERROR_FAILURE;

   //Copy user data to the transmit buffer
   chunkedBufferRead((uint8_t *) txCurDmaDesc->addr, buffer, offset, length);

   //Set the packet length and give the ownership of the descriptor to the DMA
   txCurDmaDesc->size = length & DMA_DESC_SIZE_MASK;

   //Check whether DMA transfers are suspended
   if(!(MAC->DMA_TX_CTRL & DMA_TX_CTRL_TX_EN))
   {
      //Set the start position in the ring buffer
      MAC->DMA_TX_DESC = (uint32_t) txCurDmaDesc;
   }

   //Instruct the DMA controller to transfer the packet
   MAC->DMA_TX_CTRL = DMA_TX_CTRL_TX_EN;

   //Point to the next descriptor in the list
   txCurDmaDesc = (M2sxxxTxDmaDesc *) txCurDmaDesc->next;

   //Check whether the next buffer is available for writing
   if(txCurDmaDesc->size & DMA_DESC_EMPTY_FLAG)
   {
      //The transmitter can accept another packet
      osSetEvent(&interface->nicTxEvent);
   }

   //Data successfully written
   return NO_ERROR;
}
Esempio n. 8
0
void ndpProcessRouterAdv(NetInterface *interface, Ipv6PseudoHeader *pseudoHeader,
   const ChunkedBuffer *buffer, size_t offset, uint8_t hopLimit)
{
   size_t length;
   NdpRouterAdvMessage *message;

   //Retrieve the length of the message
   length = chunkedBufferGetLength(buffer) - offset;

   //Check the length of the Router Advertisement message
   if(length < sizeof(NdpRouterAdvMessage))
      return;

   //Point to the beginning of the message
   message = chunkedBufferAt(buffer, offset);
   //Sanity check
   if(!message) return;

   //Debug message
   TRACE_INFO("Router Advertisement message received (%" PRIuSIZE " bytes)...\r\n", length);
   //Dump message contents for debugging purpose
   ndpDumpRouterAdvMessage(message);

   //Routers must use their link-local address as the source for the
   //Router Advertisement so that hosts can uniquely identify routers
   if(!ipv6IsLinkLocalUnicastAddr(&pseudoHeader->srcAddr))
      return;

   //The IPv6 Hop Limit field must have a value of 255 to ensure
   //that the packet has not been forwarded by a router
   if(hopLimit != NDP_HOP_LIMIT)
      return;

   //ICMPv6 Code must be 0. An advertisement that passes the validity
   //checks is called a valid advertisement
   if(message->code)
      return;

#if (SLAAC_SUPPORT == ENABLED)
   //Stateless Address Autoconfiguration is currently used?
   if(interface->slaacContext != NULL)
   {
      //Process the valid advertisement
      slaacProcessRouterAdv(interface->slaacContext,
         &pseudoHeader->srcAddr, message, length);
   }
#endif
}
Esempio n. 9
0
error_t ipv4SendDatagram(NetInterface *interface, Ipv4PseudoHeader *pseudoHeader,
                         ChunkedBuffer *buffer, size_t offset, uint8_t timeToLive)
{
    error_t error;
    size_t length;
    uint16_t id;

    //Retrieve the length of payload
    length = chunkedBufferGetLength(buffer) - offset;

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

    //If the payload length is smaller than the network
    //interface MTU then no fragmentation is needed
    if(length <= IPV4_MAX_PAYLOAD_SIZE)
    {
        //Send data as is
        error = ipv4SendPacket(interface,
                               pseudoHeader, id, 0, buffer, offset, timeToLive);
    }
    //If the payload length exceeds the network interface MTU
    //then the device must fragment the data
    else
    {
#if (IPV4_FRAG_SUPPORT == ENABLED)
        //Fragment IP datagram into smaller packets
        error = ipv4FragmentDatagram(interface,
                                     pseudoHeader, id, buffer, offset, timeToLive);
#else
        //Fragmentation is not supported
        error = ERROR_MESSAGE_TOO_LONG;
#endif
    }

    //Return status code
    return error;
}
Esempio n. 10
0
void icmpProcessEchoRequest(NetInterface *interface,
                            Ipv4Addr srcIpAddr, const ChunkedBuffer *request, size_t requestOffset)
{
    error_t error;
    size_t requestLength;
    size_t replyOffset;
    size_t replyLength;
    ChunkedBuffer *reply;
    IcmpEchoMessage *requestHeader;
    IcmpEchoMessage *replyHeader;
    Ipv4PseudoHeader pseudoHeader;

    //Retrieve the length of the Echo Request message
    requestLength = chunkedBufferGetLength(request) - requestOffset;

    //Ensure the packet length is correct
    if(requestLength < sizeof(IcmpEchoMessage))
        return;

    //Point to the Echo Request header
    requestHeader = chunkedBufferAt(request, requestOffset);
    //Sanity check
    if(!requestHeader) return;

    //Debug message
    TRACE_INFO("ICMP Echo Request message received (%" PRIuSIZE " bytes)...\r\n", requestLength);
    //Dump message contents for debugging purpose
    icmpDumpEchoMessage(requestHeader);

    //Allocate memory to hold the Echo Reply message
    reply = ipAllocBuffer(sizeof(IcmpEchoMessage), &replyOffset);
    //Failed to allocate memory?
    if(!reply) return;

    //Point to the Echo Reply header
    replyHeader = chunkedBufferAt(reply, replyOffset);

    //Format Echo Reply header
    replyHeader->type = ICMP_TYPE_ECHO_REPLY;
    replyHeader->code = 0;
    replyHeader->checksum = 0;
    replyHeader->identifier = requestHeader->identifier;
    replyHeader->sequenceNumber = requestHeader->sequenceNumber;

    //Point to the first data byte
    requestOffset += sizeof(IcmpEchoMessage);
    requestLength -= sizeof(IcmpEchoMessage);

    //Copy data
    error = chunkedBufferConcat(reply, request, requestOffset, requestLength);
    //Any error to report?
    if(error)
    {
        //Clean up side effects
        chunkedBufferFree(reply);
        //Exit immediately
        return;
    }

    //Get the length of the resulting message
    replyLength = chunkedBufferGetLength(reply) - replyOffset;
    //Calculate ICMP header checksum
    replyHeader->checksum = ipCalcChecksumEx(reply, replyOffset, replyLength);

    //Format IPv4 pseudo header
    pseudoHeader.srcAddr = interface->ipv4Config.addr;
    pseudoHeader.destAddr = srcIpAddr;
    pseudoHeader.reserved = 0;
    pseudoHeader.protocol = IPV4_PROTOCOL_ICMP;
    pseudoHeader.length = htons(replyLength);

    //Debug message
    TRACE_INFO("Sending ICMP Echo Reply message (%" PRIuSIZE " bytes)...\r\n", replyLength);
    //Dump message contents for debugging purpose
    icmpDumpEchoMessage(replyHeader);

    //Send Echo Reply message
    ipv4SendDatagram(interface, &pseudoHeader, reply, replyOffset, IPV4_DEFAULT_TTL);

    //Free previously allocated memory block
    chunkedBufferFree(reply);
}
Esempio n. 11
0
void ndpProcessNeighborAdv(NetInterface *interface, Ipv6PseudoHeader *pseudoHeader,
   const ChunkedBuffer *buffer, size_t offset, uint8_t hopLimit)
{
   size_t length;
   NdpNeighborAdvMessage *message;
   NdpLinkLayerAddrOption *option;
   NdpCacheEntry *entry;

   //Retrieve the length of the message
   length = chunkedBufferGetLength(buffer) - offset;

   //Check the length of the Neighbor Advertisement message
   if(length < sizeof(NdpNeighborAdvMessage))
      return;

   //Point to the beginning of the message
   message = chunkedBufferAt(buffer, offset);
   //Sanity check
   if(!message) return;

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

   //The IPv6 Hop Limit field must have a value of 255 to ensure
   //that the packet has not been forwarded by a router
   if(hopLimit != NDP_HOP_LIMIT)
      return;

   //ICMPv6 Code must be 0
   if(message->code)
      return;

   //Check whether the target address is tentative or matches
   //a unicast address assigned to the interface
   if(ipv6CompAddr(&message->targetAddr, &interface->ipv6Config.linkLocalAddr) &&
      interface->ipv6Config.linkLocalAddrState != IPV6_ADDR_STATE_INVALID)
   {
      //Debug message
      TRACE_WARNING("The address %s is a duplicate!\r\n",
         ipv6AddrToString(&interface->ipv6Config.linkLocalAddr, NULL));

      //The address is a duplicate and should not be used
      interface->ipv6Config.linkLocalAddrDup = TRUE;
      //Exit immediately
      return;
   }
   else if(ipv6CompAddr(&message->targetAddr, &interface->ipv6Config.globalAddr) &&
      interface->ipv6Config.globalAddrState != IPV6_ADDR_STATE_INVALID)
   {
      //Debug message
      TRACE_WARNING("The address %s is a duplicate!\r\n",
         ipv6AddrToString(&interface->ipv6Config.globalAddr, NULL));

      //The address is a duplicate and should not be used
      interface->ipv6Config.globalAddrDup = TRUE;
      //Exit immediately
      return;
   }

   //The target address must not be a multicast address
   if(ipv6IsMulticastAddr(&message->targetAddr))
   {
      //Debug message
      TRACE_WARNING("Target address must not be a multicast address!\r\n");
      //Exit immediately
      return;
   }

   //If the destination address is a multicast address
   //then the Solicited flag must be zero
   if(ipv6IsMulticastAddr(&pseudoHeader->destAddr) && message->s)
   {
      //Debug message
      TRACE_WARNING("Solicited flag must be zero!\r\n");
      //Exit immediately
      return;
   }

   //Calculate the length of the Options field
   length -= sizeof(NdpNeighborSolMessage);
   //Search for the Target Link-Layer Address option
   option = ndpGetOption(message->options, length, NDP_OPT_TARGET_LINK_LAYER_ADDR);

   //Source Link-Layer Address option found?
   if(option && option->length == 1)
   {
      //Debug message
      TRACE_DEBUG("  Target Link-Layer Address = %s\r\n",
         macAddrToString(&option->linkLayerAddr, NULL));

      //Acquire exclusive access to Neighbor cache
      osAcquireMutex(&interface->ndpCacheMutex);

      //Search the Neighbor cache for the specified target address
      entry = ndpFindEntry(interface, &message->targetAddr);

      //If no entry exists, the advertisement should be silently discarded
      if(entry)
      {
         //INCOMPLETE state?
         if(entry->state == NDP_STATE_INCOMPLETE)
         {
            //Record link-layer address
            entry->macAddr = option->linkLayerAddr;
            //Send all the packets that are pending for transmission
            ndpSendQueuedPackets(interface, entry);
            //Save current time
            entry->timestamp = osGetSystemTime();

            //Solicited flag is set?
            if(message->s)
            {
               //Computing the random ReachableTime value
               entry->timeout = NDP_REACHABLE_TIME;
               //Switch to the REACHABLE state
               entry->state = NDP_STATE_REACHABLE;
            }
            //Solicited flag is cleared?
            else
            {
               //Enter the STALE state
               entry->state = NDP_STATE_STALE;
            }
         }
         //REACHABLE, STALE, DELAY or PROBE state?
         else
         {
            //Solicited flag is set and Override flag is cleared?
            if(message->s && !message->o)
            {
               //Same link-layer address than cached?
               if(macCompAddr(&entry->macAddr, &option->linkLayerAddr))
               {
                  //Save current time
                  entry->timestamp = osGetSystemTime();
                  //Computing the random ReachableTime value
                  entry->timeout = NDP_REACHABLE_TIME;
                  //Switch to the REACHABLE state
                  entry->state = NDP_STATE_REACHABLE;
               }
               //Different link-layer address than cached?
               else
               {
                  //REACHABLE state?
                  if(entry->state == NDP_STATE_REACHABLE)
                  {
                     //Save current time
                     entry->timestamp = osGetSystemTime();
                     //Enter the STALE state
                     entry->state = NDP_STATE_STALE;
                  }
               }
            }
            //Both Solicited and Override flags are set?
            else if(message->s && message->o)
            {
               //Record link-layer address (if different)
               entry->macAddr = option->linkLayerAddr;
               //Save current time
               entry->timestamp = osGetSystemTime();
               //Computing the random ReachableTime value
               entry->timeout = NDP_REACHABLE_TIME;
               //Switch to the REACHABLE state
               entry->state = NDP_STATE_REACHABLE;
            }
            //Solicited flag is cleared and Override flag is set?
            else if(!message->s && message->o)
            {
               //Different link-layer address than cached?
               if(!macCompAddr(&entry->macAddr, &option->linkLayerAddr))
               {
                  //Record link-layer address
                  entry->macAddr = option->linkLayerAddr;
                  //Save current time
                  entry->timestamp = osGetSystemTime();
                  //Enter the STALE state
                  entry->state = NDP_STATE_STALE;
               }
            }
         }
      }
   }
   //Source Link-Layer Address option not found?
   else
   {
      //Update content of IsRouter flag
   }

   //Release exclusive access to Neighbor cache
   osReleaseMutex(&interface->ndpCacheMutex);
}
Esempio n. 12
0
void ndpProcessNeighborSol(NetInterface *interface, Ipv6PseudoHeader *pseudoHeader,
   const ChunkedBuffer *buffer, size_t offset, uint8_t hopLimit)
{
   size_t length;
   NdpNeighborSolMessage *message;
   NdpLinkLayerAddrOption *option;
   NdpCacheEntry *entry;

   //Retrieve the length of the message
   length = chunkedBufferGetLength(buffer) - offset;

   //Check the length of the Neighbor Solicitation message
   if(length < sizeof(NdpNeighborSolMessage))
      return;

   //Point to the beginning of the message
   message = chunkedBufferAt(buffer, offset);
   //Sanity check
   if(!message) return;

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

   //The IPv6 Hop Limit field must have a value of 255 to ensure
   //that the packet has not been forwarded by a router
   if(hopLimit != NDP_HOP_LIMIT)
      return;

   //ICMPv6 Code must be 0
   if(message->code)
      return;

   //The target address must a valid unicast address assigned to the interface
   //or a tentative address on which DAD is being performed
   if(ipv6CompAddr(&message->targetAddr, &interface->ipv6Config.linkLocalAddr))
   {
      //Check whether the target address is tentative
      if(interface->ipv6Config.linkLocalAddrState == IPV6_ADDR_STATE_TENTATIVE)
      {
         //If the source address of the Neighbor Solicitation is the unspecified
         //address, the solicitation is from a node performing Duplicate Address
         //Detection
         if(ipv6CompAddr(&pseudoHeader->srcAddr, &IPV6_UNSPECIFIED_ADDR))
         {
            //Debug message
            TRACE_WARNING("The tentative address %s is a duplicate!\r\n",
               ipv6AddrToString(&interface->ipv6Config.linkLocalAddr, NULL));

            //The tentative address is a duplicate and should not be used
            interface->ipv6Config.linkLocalAddrDup = TRUE;
         }

         //In all cases, a node must not respond to a Neighbor Solicitation
         //for a tentative address
         return;
      }
   }
   else if(ipv6CompAddr(&message->targetAddr, &interface->ipv6Config.globalAddr))
   {
      //Check whether the target address is tentative
      if(interface->ipv6Config.globalAddrState == IPV6_ADDR_STATE_TENTATIVE)
      {
         //If the source address of the Neighbor Solicitation is the unspecified
         //address, the solicitation is from a node performing Duplicate Address
         //Detection
         if(ipv6CompAddr(&pseudoHeader->srcAddr, &IPV6_UNSPECIFIED_ADDR))
         {
            //Debug message
            TRACE_WARNING("The tentative address %s is a duplicate!\r\n",
               ipv6AddrToString(&interface->ipv6Config.globalAddr, NULL));

            //The tentative address is a duplicate and should not be used
            interface->ipv6Config.globalAddrDup = TRUE;
         }

         //In all cases, a node must not respond to a Neighbor Solicitation
         //for a tentative address
         return;
      }
   }
   else
   {
      //Debug message
      TRACE_WARNING("Wrong target address!\r\n");
      //Exit immediately
      return;
   }

   //If the IP source address is the unspecified address, the IP
   //destination address must be a solicited-node multicast address
   if(ipv6CompAddr(&pseudoHeader->srcAddr, &IPV6_UNSPECIFIED_ADDR) &&
      !ipv6IsSolicitedNodeAddr(&pseudoHeader->destAddr))
   {
      //Debug message
      TRACE_WARNING("Destination address must be a solicited-node address!\r\n");
      //Exit immediately
      return;
   }

   //Calculate the length of the Options field
   length -= sizeof(NdpNeighborSolMessage);
   //Search for the Source Link-Layer Address option
   option = ndpGetOption(message->options, length, NDP_OPT_SOURCE_LINK_LAYER_ADDR);

   //Source Link-Layer Address option found?
   if(option && option->length == 1)
   {
      //Debug message
      TRACE_DEBUG("  Source Link-Layer Address = %s\r\n",
         macAddrToString(&option->linkLayerAddr, NULL));

      //If the Source Address is not the unspecified address, then the Neighbor
      //cache should be updated for the IP source address of the solicitation
      if(ipv6CompAddr(&pseudoHeader->srcAddr, &IPV6_UNSPECIFIED_ADDR))
         return;

      //Acquire exclusive access to Neighbor cache
      osAcquireMutex(&interface->ndpCacheMutex);

      //Search the Neighbor cache for the source address of the solicitation
      entry = ndpFindEntry(interface, &pseudoHeader->srcAddr);

      //No matching entry has been found?
      if(!entry)
      {
         //Create an entry
         entry = ndpCreateEntry(interface);

         //Neighbor cache entry successfully created?
         if(entry)
         {
            //Record the IPv6 and the corresponding MAC address
            entry->ipAddr = pseudoHeader->srcAddr;
            entry->macAddr = option->linkLayerAddr;
            //Save current time
            entry->timestamp = osGetSystemTime();
            //Enter the STALE state
            entry->state = NDP_STATE_STALE;
         }
      }
      else
      {
         //INCOMPLETE state?
         if(entry->state == NDP_STATE_INCOMPLETE)
         {
            //Record link-layer address
            entry->macAddr = option->linkLayerAddr;
            //Send all the packets that are pending for transmission
            ndpSendQueuedPackets(interface, entry);
            //Save current time
            entry->timestamp = osGetSystemTime();
            //Enter the STALE state
            entry->state = NDP_STATE_STALE;
         }
         //REACHABLE, STALE, DELAY or PROBE state?
         else
         {
            //Different link-layer address than cached?
            if(!macCompAddr(&entry->macAddr, &option->linkLayerAddr))
            {
               //Update link-layer address
               entry->macAddr = option->linkLayerAddr;
               //Save current time
               entry->timestamp = osGetSystemTime();
               //Enter the STALE state
               entry->state = NDP_STATE_STALE;
            }
         }
      }

      //Release exclusive access to Neighbor cache
      osReleaseMutex(&interface->ndpCacheMutex);
   }
   //Source Link-Layer Address option not found?
   else
   {
      //This option must be included in multicast solicitations
      if(ipv6IsMulticastAddr(&pseudoHeader->destAddr))
      {
         //Debug message
         TRACE_WARNING("The Source Link-Layer Address must be included!\r\n");
         //Exit immediately
         return;
      }
   }

   //After any updates to the Neighbor cache, the node sends a Neighbor
   //Advertisement response as described in RFC 4861 7.2.4
   ndpSendNeighborAdv(interface, &message->targetAddr, &pseudoHeader->srcAddr);
}
Esempio n. 13
0
error_t ndpEnqueuePacket(NetInterface *interface,
   const Ipv6Addr *ipAddr, ChunkedBuffer *buffer, size_t offset)
{
   error_t error;
   uint_t i;
   size_t length;
   NdpCacheEntry *entry;

   //Retrieve the length of the multi-part buffer
   length = chunkedBufferGetLength(buffer);

   //Acquire exclusive access to Neighbor cache
   osAcquireMutex(&interface->ndpCacheMutex);

   //Search the Neighbor cache for the specified Ipv6 address
   entry = ndpFindEntry(interface, ipAddr);

   //No matching entry in Neighbor cache?
   if(!entry)
   {
      //Release exclusive access to Neighbor cache
      osReleaseMutex(&interface->ndpCacheMutex);
      //Report an error to the calling function
      return ERROR_FAILURE;
   }

   //Check current state
   if(entry->state == NDP_STATE_INCOMPLETE)
   {
      //Check whether the packet queue is full
      if(entry->queueSize >= NDP_MAX_PENDING_PACKETS)
      {
         //When the queue overflows, the new arrival should replace the oldest entry
         chunkedBufferFree(entry->queue[0].buffer);

         //Make room for the new packet
         for(i = 1; i < NDP_MAX_PENDING_PACKETS; i++)
            entry->queue[i - 1] = entry->queue[i];

         //Adjust the number of pending packets
         entry->queueSize--;
      }

      //Index of the entry to be filled in
      i = entry->queueSize;
      //Allocate a memory buffer to store the packet
      entry->queue[i].buffer = chunkedBufferAlloc(length);

      //Failed to allocate memory?
      if(!entry->queue[i].buffer)
      {
         //Release exclusive access to Neighbor cache
         osReleaseMutex(&interface->ndpCacheMutex);
         //Report an error to the calling function
         return ERROR_OUT_OF_MEMORY;
      }

      //Copy packet contents
      chunkedBufferCopy(entry->queue[i].buffer, 0, buffer, 0, length);
      //Offset to the first byte of the IPv6 header
      entry->queue[i].offset = offset;

      //Increment the number of queued packets
      entry->queueSize++;
      //The packet was successfully enqueued
      error = NO_ERROR;
   }
   else
   {
      //Send immediately the packet since the address is already resolved
      error = ethSendFrame(interface, &entry->macAddr, buffer, offset, ETH_TYPE_IPV6);
   }

   //Release exclusive access to Neighbor cache
   osReleaseMutex(&interface->ndpCacheMutex);
   //Return status code
   return error;
}
Esempio n. 14
0
void icmpv6ProcessMessage(NetInterface *interface, Ipv6PseudoHeader *pseudoHeader,
   const ChunkedBuffer *buffer, size_t offset, uint8_t hopLimit)
{
   size_t length;
   Icmpv6Header *header;

   //Retrieve the length of the ICMPv6 message
   length = chunkedBufferGetLength(buffer) - offset;

   //Ensure the message length is correct
   if(length < sizeof(Icmpv6Header))
   {
      //Debug message
      TRACE_WARNING("ICMPv6 message length is invalid!\r\n");
      //Exit immediately
      return;
   }

   //Point to the ICMPv6 message header
   header = chunkedBufferAt(buffer, offset);
   //Sanity check
   if(!header) return;

   //Debug message
   TRACE_INFO("ICMPv6 message received (%" PRIuSIZE " bytes)...\r\n", length);
   //Dump message contents for debugging purpose
   icmpv6DumpMessage(header);

   //Verify checksum value
   if(ipCalcUpperLayerChecksumEx(pseudoHeader,
      sizeof(Ipv6PseudoHeader), buffer, offset, length) != 0xFFFF)
   {
      //Debug message
      TRACE_WARNING("Wrong ICMPv6 header checksum!\r\n");
      //Exit immediately
      return;
   }

   //Check whether the destination address is the tentative address
   if(ipv6IsTentativeAddr(interface, &pseudoHeader->destAddr))
   {
      //The interface must accept Neighbor Solicitation and
      //Neighbor Advertisement messages
      if(header->type != ICMPV6_TYPE_NEIGHBOR_SOL &&
         header->type != ICMPV6_TYPE_NEIGHBOR_ADV)
      {
         //Other packets addressed to the tentative address
         //should be silently discarded
         return;
      }
   }

   //Check the type of message
   switch(header->type)
   {
   //Echo Request message?
   case ICMPV6_TYPE_ECHO_REQUEST:
      //Process Echo Request message
      icmpv6ProcessEchoRequest(interface, pseudoHeader, buffer, offset);
      break;
#if (MLD_SUPPORT == ENABLED)
   //Multicast Listener Query message?
   case ICMPV6_TYPE_MULTICAST_LISTENER_QUERY:
      //Process Multicast Listener Query message
      mldProcessListenerQuery(interface, pseudoHeader, buffer, offset, hopLimit);
      break;
   //Version 1 Multicast Listener Report message?
   case ICMPV6_TYPE_MULTICAST_LISTENER_REPORT_V1:
      //Process Version 1 Multicast Listener Report message
      mldProcessListenerReport(interface, pseudoHeader, buffer, offset, hopLimit);
      break;
#endif
#if (NDP_SUPPORT == ENABLED)
   //Router Advertisement message?
   case ICMPV6_TYPE_ROUTER_ADV:
      //Process Router Advertisement message
      ndpProcessRouterAdv(interface, pseudoHeader, buffer, offset, hopLimit);
      break;
   //Neighbor Solicitation message?
   case ICMPV6_TYPE_NEIGHBOR_SOL:
      //Process Neighbor Solicitation message
      ndpProcessNeighborSol(interface, pseudoHeader, buffer, offset, hopLimit);
      break;
   //Neighbor Advertisement message?
   case ICMPV6_TYPE_NEIGHBOR_ADV:
      //Process Neighbor Advertisement message
      ndpProcessNeighborAdv(interface, pseudoHeader, buffer, offset, hopLimit);
      break;
#endif
   //Unknown type?
   default:
      //Debug message
      TRACE_WARNING("Unknown ICMPv6 message type!\r\n");
      //Discard incoming ICMPv6 message
      break;
   }
}
Esempio n. 15
0
void mldProcessListenerQuery(NetInterface *interface, Ipv6PseudoHeader *pseudoHeader,
   const ChunkedBuffer *buffer, size_t offset, uint8_t hopLimit)
{
   uint_t i;
   size_t length;
   systime_t time;
   systime_t maxRespDelay;
   MldMessage *message;
   Ipv6FilterEntry *entry;

   //Retrieve the length of the MLD message
   length = chunkedBufferGetLength(buffer) - offset;

   //The message must be at least 24 octets long
   if(length < sizeof(MldMessage))
      return;

   //Point to the beginning of the MLD message
   message = chunkedBufferAt(buffer, offset);
   //Sanity check
   if(!message) return;

   //Debug message
   TRACE_INFO("MLD message received (%" PRIuSIZE " bytes)...\r\n", length);
   //Dump message contents for debugging purpose
   mldDumpMessage(message);

   //Make sure the source address of the message is a valid link-local address
   if(!ipv6IsLinkLocalUnicastAddr(&pseudoHeader->srcAddr))
      return;
   //Check the Hop Limit field
   if(hopLimit != MLD_HOP_LIMIT)
      return;

   //Get current time
   time = osGetTickCount();

   //The Max Resp Delay field specifies the maximum time allowed
   //before sending a responding report
   maxRespDelay = message->maxRespDelay * 10;

   //Acquire exclusive access to the IPv6 filter table
   osMutexAcquire(interface->ipv6FilterMutex);

   //Loop through filter table entries
   for(i = 0; i < interface->ipv6FilterSize; i++)
   {
      //Point to the current entry
      entry = &interface->ipv6Filter[i];

      //The link-scope all-nodes address (FF02::1) is handled as a special
      //case. The host starts in Idle Listener state for that address on
      //every interface and never transitions to another state
      if(ipv6CompAddr(&entry->addr, &IPV6_LINK_LOCAL_ALL_NODES_ADDR))
         continue;

      //A General Query is used to learn which multicast addresses have listeners
      //on an attached link. A Multicast-Address-Specific Query is used to learn
      //if a particular multicast address has any listeners on an attached link
      if(ipv6CompAddr(&message->multicastAddr, &IPV6_UNSPECIFIED_ADDR) ||
         ipv6CompAddr(&message->multicastAddr, &entry->addr))
      {
         //Delaying Listener state?
         if(entry->state == MLD_STATE_DELAYING_LISTENER)
         {
            //The timer has not yet expired?
            if(timeCompare(time, entry->timer) < 0)
            {
               //If a timer for the address is already running, it is reset to
               //the new random value only if the requested Max Response Delay
               //is less than the remaining value of the running timer
               if(maxRespDelay < (entry->timer - time))
               {
                  //Restart delay timer
                  entry->timer = time + mldRand(maxRespDelay);
               }
            }
         }
         //Idle Listener state?
         else if(entry->state == MLD_STATE_IDLE_LISTENER)
         {
            //Switch to the Delaying Listener state
            entry->state = MLD_STATE_DELAYING_LISTENER;
            //Delay the response by a random amount of time
            entry->timer = time + mldRand(maxRespDelay);
         }
      }
   }

   //Release exclusive access to the IPv6 filter table
   osMutexRelease(interface->ipv6FilterMutex);
}
Esempio n. 16
0
void ipv4ProcessDatagram(NetInterface *interface,
                         const MacAddr *srcMacAddr, const ChunkedBuffer *buffer)
{
    error_t error;
    size_t offset;
    size_t length;
    Ipv4Header *header;
    IpPseudoHeader pseudoHeader;

    //Retrieve the length of the IPv4 datagram
    length = chunkedBufferGetLength(buffer);

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

    //Debug message
    TRACE_INFO("IPv4 datagram received (%u bytes)...\r\n", length);
    //Dump IP header contents for debugging purpose
    ipv4DumpHeader(header);

    //Get the offset to the payload
    offset = header->headerLength * 4;
    //Compute the length of the payload
    length -= header->headerLength * 4;

    //Form the IPv4 pseudo header
    pseudoHeader.length = sizeof(Ipv4PseudoHeader);
    pseudoHeader.ipv4Data.srcAddr = header->srcAddr;
    pseudoHeader.ipv4Data.destAddr = header->destAddr;
    pseudoHeader.ipv4Data.reserved = 0;
    pseudoHeader.ipv4Data.protocol = header->protocol;
    pseudoHeader.ipv4Data.length = htons(length);

    //Check the protocol field
    switch(header->protocol)
    {
    //ICMP protocol?
    case IPV4_PROTOCOL_ICMP:
        //Process incoming ICMP message
        icmpProcessMessage(interface, header->srcAddr, buffer, offset);
#if (RAW_SOCKET_SUPPORT == ENABLED)
        //Allow raw sockets to process ICMP messages
        rawSocketProcessDatagram(interface, &pseudoHeader, buffer, offset);
#endif
        //No error to report
        error = NO_ERROR;
        //Continue processing
        break;

#if (IGMP_SUPPORT == ENABLED)
    //IGMP protocol?
    case IPV4_PROTOCOL_IGMP:
        //Process incoming IGMP message
        igmpProcessMessage(interface, buffer, offset);
#if (RAW_SOCKET_SUPPORT == ENABLED)
        //Allow raw sockets to process IGMP messages
        rawSocketProcessDatagram(interface, &pseudoHeader, buffer, offset);
#endif
        //No error to report
        error = NO_ERROR;
        //Continue processing
        break;
#endif

#if (TCP_SUPPORT == ENABLED)
    //TCP protocol?
    case IPV4_PROTOCOL_TCP:
        //Process incoming TCP segment
        tcpProcessSegment(interface, &pseudoHeader, buffer, offset);
        //No error to report
        error = NO_ERROR;
        //Continue processing
        break;
#endif

#if (UDP_SUPPORT == ENABLED)
    //UDP protocol?
    case IPV4_PROTOCOL_UDP:
        //Process incoming UDP datagram
        error = udpProcessDatagram(interface, &pseudoHeader, buffer, offset);
        //Continue processing
        break;
#endif

    //Unknown protocol?
    default:
#if (RAW_SOCKET_SUPPORT == ENABLED)
        //Allow raw sockets to process IPv4 packets
        error = rawSocketProcessDatagram(interface, &pseudoHeader, buffer, offset);
#else
        //Report an error
        error = ERROR_PROTOCOL_UNREACHABLE;
#endif
        //Continue processing
        break;
    }

    //Unreachable protocol?
    if(error == ERROR_PROTOCOL_UNREACHABLE)
    {
        //Send a Destination Unreachable message
        icmpSendErrorMessage(interface, ICMP_TYPE_DEST_UNREACHABLE,
                             ICMP_CODE_PROTOCOL_UNREACHABLE, 0, buffer);
    }
    //Unreachable port?
    else if(error == ERROR_PORT_UNREACHABLE)
    {
        //Send a Destination Unreachable message
        icmpSendErrorMessage(interface, ICMP_TYPE_DEST_UNREACHABLE,
                             ICMP_CODE_PORT_UNREACHABLE, 0, buffer);
    }
}
Esempio n. 17
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;
}
Esempio n. 18
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);
   }
}
Esempio n. 19
0
error_t udpSendDatagramEx(NetInterface *interface, uint16_t srcPort, const IpAddr *destIpAddr,
   uint16_t destPort, ChunkedBuffer *buffer, size_t offset, uint8_t ttl)
{
   error_t error;
   size_t length;
   UdpHeader *header;
   IpPseudoHeader pseudoHeader;

   //Make room for the UDP header
   offset -= sizeof(UdpHeader);
   //Retrieve the length of the datagram
   length = chunkedBufferGetLength(buffer) - offset;

   //Point to the UDP header
   header = chunkedBufferAt(buffer, offset);
   //Sanity check
   if(!header) return ERROR_FAILURE;

   //Format UDP header
   header->srcPort = htons(srcPort);
   header->destPort = htons(destPort);
   header->length = htons(length);
   header->checksum = 0;

#if (IPV4_SUPPORT == ENABLED)
   //Destination address is an IPv4 address?
   if(destIpAddr->length == sizeof(Ipv4Addr))
   {
      Ipv4Addr srcIpAddr;

      //Select the source IPv4 address and the relevant network interface
      //to use when sending data to the specified destination host
      error = ipv4SelectSourceAddr(&interface, destIpAddr->ipv4Addr, &srcIpAddr);
      //Any error to report?
      if(error) return error;

      //Format IPv4 pseudo header
      pseudoHeader.length = sizeof(Ipv4PseudoHeader);
      pseudoHeader.ipv4Data.srcAddr = srcIpAddr;
      pseudoHeader.ipv4Data.destAddr = destIpAddr->ipv4Addr;
      pseudoHeader.ipv4Data.reserved = 0;
      pseudoHeader.ipv4Data.protocol = IPV4_PROTOCOL_UDP;
      pseudoHeader.ipv4Data.length = htons(length);

      //Calculate UDP header checksum
      header->checksum = ipCalcUpperLayerChecksumEx(&pseudoHeader.ipv4Data,
         sizeof(Ipv4PseudoHeader), buffer, offset, length);
   }
   else
#endif
#if (IPV6_SUPPORT == ENABLED)
   //Destination address is an IPv6 address?
   if(destIpAddr->length == sizeof(Ipv6Addr))
   {
      //Select the source IPv6 address and the relevant network interface
      //to use when sending data to the specified destination host
      error = ipv6SelectSourceAddr(&interface,
         &destIpAddr->ipv6Addr, &pseudoHeader.ipv6Data.srcAddr);
      //Any error to report?
      if(error) return error;

      //Format IPv6 pseudo header
      pseudoHeader.length = sizeof(Ipv6PseudoHeader);
      pseudoHeader.ipv6Data.destAddr = destIpAddr->ipv6Addr;
      pseudoHeader.ipv6Data.length = htonl(length);
      pseudoHeader.ipv6Data.reserved = 0;
      pseudoHeader.ipv6Data.nextHeader = IPV6_UDP_HEADER;

      //Calculate UDP header checksum
      header->checksum = ipCalcUpperLayerChecksumEx(&pseudoHeader.ipv6Data,
         sizeof(Ipv6PseudoHeader), buffer, offset, length);
   }
   else
#endif
   //Invalid destination address?
   {
      //An internal error has occurred
      return ERROR_FAILURE;
   }

   //Debug message
   TRACE_INFO("Sending UDP datagram (%" PRIuSIZE " bytes)\r\n", length);
   //Dump UDP header contents for debugging purpose
   udpDumpHeader(header);

   //Send UDP datagram
   return ipSendDatagram(interface, &pseudoHeader, buffer, offset, ttl);
}
Esempio n. 20
0
error_t ipv4SendPacket(NetInterface *interface, Ipv4PseudoHeader *pseudoHeader,
                       uint16_t fragId, uint16_t fragOffset, ChunkedBuffer *buffer, size_t offset, uint8_t timeToLive)
{
    error_t error;
    size_t length;
    Ipv4Addr destIpAddr = 0;
    MacAddr destMacAddr;
    Ipv4Header *packet;

    //Is there enough space for the IPv4 header?
    if(offset < sizeof(Ipv4Header))
        return ERROR_INVALID_PARAMETER;

    //Make room for the header
    offset -= sizeof(Ipv4Header);
    //Calculate the size of the entire packet, including header and data
    length = chunkedBufferGetLength(buffer) - offset;

    //Point to the IPv4 header
    packet = chunkedBufferAt(buffer, offset);

    //Format IPv4 header
    packet->version = IPV4_VERSION;
    packet->headerLength = 5;
    packet->typeOfService = 0;
    packet->totalLength = htons(length);
    packet->identification = htons(fragId);
    packet->fragmentOffset = htons(fragOffset);
    packet->timeToLive = timeToLive;
    packet->protocol = pseudoHeader->protocol;
    packet->headerChecksum = 0;
    packet->srcAddr = pseudoHeader->srcAddr;
    packet->destAddr = pseudoHeader->destAddr;

    //Calculate IP header checksum
    packet->headerChecksum = ipCalcChecksumEx(buffer, offset, packet->headerLength * 4);

    //Ensure the source address is valid
    error = ipv4CheckSourceAddr(interface, pseudoHeader->srcAddr);
    //Invalid source address?
    if(error) return error;

    //Destination address is the unspecified address?
    if(pseudoHeader->destAddr == IPV4_UNSPECIFIED_ADDR)
    {
        //Destination address is not acceptable
        error = ERROR_INVALID_ADDRESS;
    }
    //Destination address is the loopback address?
    else if(pseudoHeader->destAddr == IPV4_LOOPBACK_ADDR)
    {
        //Not yet implemented...
        error = ERROR_NOT_IMPLEMENTED;
    }
    //Destination address is a broadcast address?
    else if(ipv4IsBroadcastAddr(interface, pseudoHeader->destAddr))
    {
        //Destination IPv4 address
        destIpAddr = pseudoHeader->destAddr;
        //Make use of the broadcast MAC address
        destMacAddr = MAC_BROADCAST_ADDR;
        //No error to report
        error = NO_ERROR;
    }
    //Destination address is a multicast address?
    else if(ipv4IsMulticastAddr(pseudoHeader->destAddr))
    {
        //Destination IPv4 address
        destIpAddr = pseudoHeader->destAddr;
        //Map IPv4 multicast address to MAC-layer multicast address
        error = ipv4MapMulticastAddrToMac(pseudoHeader->destAddr, &destMacAddr);
    }
    //Destination host is in the local subnet?
    else if(ipv4IsInLocalSubnet(interface, pseudoHeader->destAddr))
    {
        //Destination IPv4 address
        destIpAddr = pseudoHeader->destAddr;
        //Resolve host address before sending the packet
        error = arpResolve(interface, pseudoHeader->destAddr, &destMacAddr);
    }
    //Destination host is outside the local subnet?
    else
    {
        //Make sure the default gateway is properly set
        if(interface->ipv4Config.defaultGateway != IPV4_UNSPECIFIED_ADDR)
        {
            //Use the default gateway to forward the packet
            destIpAddr = interface->ipv4Config.defaultGateway;
            //Perform address resolution
            error = arpResolve(interface, interface->ipv4Config.defaultGateway, &destMacAddr);
        }
        else
        {
            //There is no route to the outside world...
            error = ERROR_NO_ROUTE;
        }
    }

    //Successful address resolution?
    if(!error)
    {
        //Debug message
        TRACE_INFO("Sending IPv4 packet (%u bytes)...\r\n", length);
        //Dump IP header contents for debugging purpose
        ipv4DumpHeader(packet);

        //Send Ethernet frame
        error = ethSendFrame(interface, &destMacAddr, buffer, offset, ETH_TYPE_IPV4);
    }
    //Address resolution is in progress?
    else if(error == ERROR_IN_PROGRESS)
    {
        //Debug message
        TRACE_INFO("Enqueuing IPv4 packet (%u bytes)...\r\n", length);
        //Dump IP header contents for debugging purpose
        ipv4DumpHeader(packet);

        //Enqueue packets waiting for address resolution
        error = arpEnqueuePacket(interface, destIpAddr, buffer, offset);
    }
    //Address resolution failed?
    else
    {
        //Debug message
        TRACE_WARNING("Cannot map IPv4 address to Ethernet address!\r\n");
    }

    //Return status code
    return error;
}
Esempio n. 21
0
error_t rawSocketProcessIpPacket(NetInterface *interface,
   IpPseudoHeader *pseudoHeader, const ChunkedBuffer *buffer, size_t offset)
{
   uint_t i;
   size_t length;
   Socket *socket;
   SocketQueueItem *queueItem;
   ChunkedBuffer *p;

   //Retrieve the length of the raw IP packet
   length = chunkedBufferGetLength(buffer) - offset;

   //Enter critical section
   osAcquireMutex(&socketMutex);

   //Loop through opened sockets
   for(i = 0; i < SOCKET_MAX_COUNT; i++)
   {
      //Point to the current socket
      socket = socketTable + i;

      //Raw socket found?
      if(socket->type != SOCKET_TYPE_RAW_IP)
         continue;
      //Check whether the socket is bound to a particular interface
      if(socket->interface && socket->interface != interface)
         continue;

#if (IPV4_SUPPORT == ENABLED)
      //An IPv4 packet was received?
      if(pseudoHeader->length == sizeof(Ipv4PseudoHeader))
      {
         //Check protocol field
         if(socket->protocol != pseudoHeader->ipv4Data.protocol)
            continue;
         //Destination IP address filtering
         if(socket->localIpAddr.length)
         {
            //An IPv4 address is expected
            if(socket->localIpAddr.length != sizeof(Ipv4Addr))
               continue;
            //Filter out non-matching addresses
            if(socket->localIpAddr.ipv4Addr != pseudoHeader->ipv4Data.destAddr)
               continue;
         }
         //Source IP address filtering
         if(socket->remoteIpAddr.length)
         {
            //An IPv4 address is expected
            if(socket->remoteIpAddr.length != sizeof(Ipv4Addr))
               continue;
            //Filter out non-matching addresses
            if(socket->remoteIpAddr.ipv4Addr != pseudoHeader->ipv4Data.srcAddr)
               continue;
         }
      }
      else
#endif
#if (IPV6_SUPPORT == ENABLED)
      //An IPv6 packet was received?
      if(pseudoHeader->length == sizeof(Ipv6PseudoHeader))
      {
         //Check protocol field
         if(socket->protocol != pseudoHeader->ipv6Data.nextHeader)
            continue;
         //Destination IP address filtering
         if(socket->localIpAddr.length)
         {
            //An IPv6 address is expected
            if(socket->localIpAddr.length != sizeof(Ipv6Addr))
               continue;
            //Filter out non-matching addresses
            if(!ipv6CompAddr(&socket->localIpAddr.ipv6Addr, &pseudoHeader->ipv6Data.destAddr))
               continue;
         }
         //Source IP address filtering
         if(socket->remoteIpAddr.length)
         {
            //An IPv6 address is expected
            if(socket->remoteIpAddr.length != sizeof(Ipv6Addr))
               continue;
            //Filter out non-matching addresses
            if(!ipv6CompAddr(&socket->remoteIpAddr.ipv6Addr, &pseudoHeader->ipv6Data.srcAddr))
               continue;
         }
      }
      else
#endif
      //An invalid packet was received?
      {
         //This should never occur...
         continue;
      }

      //The current socket meets all the criteria
      break;
   }

   //Drop incoming packet if no matching socket was found
   if(i >= SOCKET_MAX_COUNT)
   {
      //Leave critical section
      osReleaseMutex(&socketMutex);
      //Unreachable protocol...
      return ERROR_PROTOCOL_UNREACHABLE;
   }

   //Empty receive queue?
   if(!socket->receiveQueue)
   {
      //Allocate a memory buffer to hold the data and the associated descriptor
      p = chunkedBufferAlloc(sizeof(SocketQueueItem) + length);

      //Successful memory allocation?
      if(p != NULL)
      {
         //Point to the newly created item
         queueItem = chunkedBufferAt(p, 0);
         queueItem->buffer = p;
         //Add the newly created item to the queue
         socket->receiveQueue = queueItem;
      }
      else
      {
         //Memory allocation failed
         queueItem = NULL;
      }
   }
   else
   {
      //Point to the very first item
      queueItem = socket->receiveQueue;
      //Reach the last item in the receive queue
      for(i = 1; queueItem->next; i++)
         queueItem = queueItem->next;

      //Make sure the receive queue is not full
      if(i >= RAW_SOCKET_RX_QUEUE_SIZE)
      {
         //Leave critical section
         osReleaseMutex(&socketMutex);
         //Notify the calling function that the queue is full
         return ERROR_RECEIVE_QUEUE_FULL;
      }

      //Allocate a memory buffer to hold the data and the associated descriptor
      p = chunkedBufferAlloc(sizeof(SocketQueueItem) + length);

      //Successful memory allocation?
      if(p != NULL)
      {
         //Add the newly created item to the queue
         queueItem->next = chunkedBufferAt(p, 0);
         //Point to the newly created item
         queueItem = queueItem->next;
         queueItem->buffer = p;
      }
      else
      {
         //Memory allocation failed
         queueItem = NULL;
      }
   }

   //Failed to allocate memory?
   if(!queueItem)
   {
      //Leave critical section
      osReleaseMutex(&socketMutex);
      //Return error code
      return ERROR_OUT_OF_MEMORY;
   }

   //Initialize next field
   queueItem->next = NULL;
   //Port number is unused
   queueItem->srcPort = 0;

#if (IPV4_SUPPORT == ENABLED)
   //IPv4 remote address?
   if(pseudoHeader->length == sizeof(Ipv4PseudoHeader))
   {
      //Save the source IPv4 address
      queueItem->srcIpAddr.length = sizeof(Ipv4Addr);
      queueItem->srcIpAddr.ipv4Addr = pseudoHeader->ipv4Data.srcAddr;
      //Save the destination IPv4 address
      queueItem->destIpAddr.length = sizeof(Ipv4Addr);
      queueItem->destIpAddr.ipv4Addr = pseudoHeader->ipv4Data.destAddr;
   }
#endif
#if (IPV6_SUPPORT == ENABLED)
   //IPv6 remote address?
   if(pseudoHeader->length == sizeof(Ipv6PseudoHeader))
   {
      //Save the source IPv6 address
      queueItem->srcIpAddr.length = sizeof(Ipv6Addr);
      queueItem->srcIpAddr.ipv6Addr = pseudoHeader->ipv6Data.srcAddr;
      //Save the destination IPv6 address
      queueItem->destIpAddr.length = sizeof(Ipv6Addr);
      queueItem->destIpAddr.ipv6Addr = pseudoHeader->ipv6Data.destAddr;
   }
#endif

   //Offset to the raw IP packet
   queueItem->offset = sizeof(SocketQueueItem);
   //Copy the raw data
   chunkedBufferCopy(queueItem->buffer, queueItem->offset, buffer, offset, length);

   //Notify user that data is available
   rawSocketUpdateEvents(socket);

   //Leave critical section
   osReleaseMutex(&socketMutex);
   //Successful processing
   return NO_ERROR;
}
Esempio n. 22
0
error_t icmpSendErrorMessage(NetInterface *interface, uint8_t type,
                             uint8_t code, uint8_t parameter, const ChunkedBuffer *ipPacket)
{
    error_t error;
    size_t offset;
    size_t length;
    Ipv4Header *ipHeader;
    ChunkedBuffer *icmpMessage;
    IcmpErrorMessage *icmpHeader;
    Ipv4PseudoHeader pseudoHeader;

    //Retrieve the length of the invoking IPv4 packet
    length = chunkedBufferGetLength(ipPacket);

    //Check the length of the IPv4 packet
    if(length < sizeof(Ipv4Header))
        return ERROR_INVALID_LENGTH;

    //Point to the header of the invoking packet
    ipHeader = chunkedBufferAt(ipPacket, 0);
    //Sanity check
    if(!ipHeader) return ERROR_FAILURE;

    //Never respond to a packet destined to a broadcast or a multicast address
    if(ipv4IsBroadcastAddr(interface, ipHeader->destAddr) ||
            ipv4IsMulticastAddr(ipHeader->destAddr))
    {
        //Report an error
        return ERROR_INVALID_ADDRESS;
    }

    //Length of the data that will be returned along with the ICMP header
    length = MIN(length, ipHeader->headerLength * 4 + 8);

    //Allocate a memory buffer to hold the ICMP message
    icmpMessage = ipAllocBuffer(sizeof(IcmpErrorMessage), &offset);
    //Failed to allocate memory?
    if(!icmpMessage) return ERROR_OUT_OF_MEMORY;

    //Point to the ICMP header
    icmpHeader = chunkedBufferAt(icmpMessage, offset);

    //Format ICMP message
    icmpHeader->type = type;
    icmpHeader->code = code;
    icmpHeader->checksum = 0;
    icmpHeader->parameter = parameter;
    icmpHeader->unused = 0;

    //Copy the IP header and the first 8 bytes of the original datagram data
    error = chunkedBufferConcat(icmpMessage, ipPacket, 0, length);
    //Any error to report?
    if(error)
    {
        //Clean up side effects
        chunkedBufferFree(icmpMessage);
        //Exit immediately
        return error;
    }

    //Get the length of the resulting message
    length = chunkedBufferGetLength(icmpMessage) - offset;
    //Message checksum calculation
    icmpHeader->checksum = ipCalcChecksumEx(icmpMessage, offset, length);

    //Format IPv4 pseudo header
    pseudoHeader.srcAddr = ipHeader->destAddr;
    pseudoHeader.destAddr = ipHeader->srcAddr;
    pseudoHeader.reserved = 0;
    pseudoHeader.protocol = IPV4_PROTOCOL_ICMP;
    pseudoHeader.length = htons(length);

    //Debug message
    TRACE_INFO("Sending ICMP Error message (%" PRIuSIZE " bytes)...\r\n", length);
    //Dump message contents for debugging purpose
    icmpDumpErrorMessage(icmpHeader);

    //Send ICMP Error message
    error = ipv4SendDatagram(interface, &pseudoHeader,
                             icmpMessage, offset, IPV4_DEFAULT_TTL);

    //Free previously allocated memory
    chunkedBufferFree(icmpMessage);
    //Return status code
    return error;
}
Esempio n. 23
0
error_t icmpv6SendErrorMessage(NetInterface *interface, uint8_t type,
   uint8_t code, uint32_t parameter, const ChunkedBuffer *ipPacket)
{
   error_t error;
   size_t offset;
   size_t length;
   Ipv6Header *ipHeader;
   ChunkedBuffer *icmpMessage;
   Icmpv6ErrorMessage *icmpHeader;
   Ipv6PseudoHeader pseudoHeader;

   //Retrieve the length of the invoking IPv6 packet
   length = chunkedBufferGetLength(ipPacket);

   //Check the length of the IPv6 packet
   if(length < sizeof(Ipv6Header))
      return ERROR_INVALID_LENGTH;

   //Point to the header of the invoking packet
   ipHeader = chunkedBufferAt(ipPacket, 0);
   //Sanity check
   if(!ipHeader) return ERROR_FAILURE;

   //Never respond to a packet destined to an IPv6 multicast address
   if(ipv6IsMulticastAddr(&ipHeader->destAddr))
      return ERROR_INVALID_ADDRESS;

   //Return as much of invoking IPv6 packet as possible without
   //the ICMPv6 packet exceeding the minimum IPv6 MTU
   length = MIN(length, IPV6_DEFAULT_MTU -
      sizeof(Ipv6Header) - sizeof(Icmpv6ErrorMessage));

   //Allocate a memory buffer to hold the ICMPv6 message
   icmpMessage = ipAllocBuffer(sizeof(Icmpv6ErrorMessage), &offset);
   //Failed to allocate memory?
   if(!icmpMessage) return ERROR_OUT_OF_MEMORY;

   //Point to the ICMPv6 header
   icmpHeader = chunkedBufferAt(icmpMessage, offset);

   //Format ICMPv6 Error message
   icmpHeader->type = type;
   icmpHeader->code = code;
   icmpHeader->checksum = 0;
   icmpHeader->parameter = htonl(parameter);

   //Copy incoming IPv6 packet contents
   error = chunkedBufferConcat(icmpMessage, ipPacket, 0, length);
   //Any error to report?
   if(error)
   {
      //Clean up side effects
      chunkedBufferFree(icmpMessage);
      //Exit immediately
      return error;
   }

   //Get the length of the resulting message
   length = chunkedBufferGetLength(icmpMessage) - offset;

   //Format IPv6 pseudo header
   pseudoHeader.srcAddr = ipHeader->destAddr;
   pseudoHeader.destAddr = ipHeader->srcAddr;
   pseudoHeader.length = htonl(length);
   pseudoHeader.reserved = 0;
   pseudoHeader.nextHeader = IPV6_ICMPV6_HEADER;

   //Message checksum calculation
   icmpHeader->checksum = ipCalcUpperLayerChecksumEx(&pseudoHeader,
      sizeof(Ipv6PseudoHeader), icmpMessage, offset, length);

   //Debug message
   TRACE_INFO("Sending ICMPv6 Error message (%" PRIuSIZE " bytes)...\r\n", length);
   //Dump message contents for debugging purpose
   icmpv6DumpErrorMessage(icmpHeader);

   //Send ICMPv6 Error message
   error = ipv6SendDatagram(interface, &pseudoHeader,
      icmpMessage, offset, IPV6_DEFAULT_HOP_LIMIT);

   //Free previously allocated memory
   chunkedBufferFree(icmpMessage);
   //Return status code
   return error;
}
Esempio n. 24
0
error_t udpProcessDatagram(NetInterface *interface,
   IpPseudoHeader *pseudoHeader, const ChunkedBuffer *buffer, size_t offset)
{
   error_t error;
   uint_t i;
   size_t length;
   UdpHeader *header;
   Socket *socket;
   SocketQueueItem *queueItem;
   ChunkedBuffer *p;

   //Retrieve the length of the UDP datagram
   length = chunkedBufferGetLength(buffer) - offset;

   //Ensure the UDP header is valid
   if(length < sizeof(UdpHeader))
   {
      //Debug message
      TRACE_WARNING("UDP datagram length is invalid!\r\n");
      //Report an error
      return ERROR_INVALID_HEADER;
   }

   //Point to the UDP header
   header = chunkedBufferAt(buffer, offset);
   //Sanity check
   if(!header) return ERROR_FAILURE;

   //Debug message
   TRACE_INFO("UDP datagram received (%" PRIuSIZE " bytes)...\r\n", length);
   //Dump UDP header contents for debugging purpose
   udpDumpHeader(header);

   //When UDP runs over IPv6, the checksum is mandatory
   if(header->checksum || pseudoHeader->length == sizeof(Ipv6PseudoHeader))
   {
      //Verify UDP checksum
      if(ipCalcUpperLayerChecksumEx(pseudoHeader->data,
         pseudoHeader->length, buffer, offset, length) != 0xFFFF)
      {
         //Debug message
         TRACE_WARNING("Wrong UDP header checksum!\r\n");
         //Report an error
         return ERROR_WRONG_CHECKSUM;
      }
   }

   //Enter critical section
   osMutexAcquire(socketMutex);

   //Loop through opened sockets
   for(i = 0; i < SOCKET_MAX_COUNT; i++)
   {
      //Point to the current socket
      socket = socketTable + i;

      //UDP socket found?
      if(socket->type != SOCKET_TYPE_DGRAM)
         continue;
      //Check whether the socket is bound to a particular interface
      if(socket->interface && socket->interface != interface)
         continue;
      //Check destination port number
      if(socket->localPort != ntohs(header->destPort))
         continue;
      //Source port number filtering
      if(socket->remotePort && socket->remotePort != ntohs(header->srcPort))
         continue;

#if (IPV4_SUPPORT == ENABLED)
      //An IPv4 packet was received?
      if(pseudoHeader->length == sizeof(Ipv4PseudoHeader))
      {
         //Destination IP address filtering
         if(socket->localIpAddr.length)
         {
            //An IPv4 address is expected
            if(socket->localIpAddr.length != sizeof(Ipv4Addr))
               continue;
            //Filter out non-matching addresses
            if(socket->localIpAddr.ipv4Addr != pseudoHeader->ipv4Data.destAddr)
               continue;
         }
         //Source IP address filtering
         if(socket->remoteIpAddr.length)
         {
            //An IPv4 address is expected
            if(socket->remoteIpAddr.length != sizeof(Ipv4Addr))
               continue;
            //Filter out non-matching addresses
            if(socket->remoteIpAddr.ipv4Addr != pseudoHeader->ipv4Data.srcAddr)
               continue;
         }
      }
      else
#endif
#if (IPV6_SUPPORT == ENABLED)
      //An IPv6 packet was received?
      if(pseudoHeader->length == sizeof(Ipv6PseudoHeader))
      {
         //Destination IP address filtering
         if(socket->localIpAddr.length)
         {
            //An IPv6 address is expected
            if(socket->localIpAddr.length != sizeof(Ipv6Addr))
               continue;
            //Filter out non-matching addresses
            if(!ipv6CompAddr(&socket->localIpAddr.ipv6Addr, &pseudoHeader->ipv6Data.destAddr))
               continue;
         }
         //Source IP address filtering
         if(socket->remoteIpAddr.length)
         {
            //An IPv6 address is expected
            if(socket->remoteIpAddr.length != sizeof(Ipv6Addr))
               continue;
            //Filter out non-matching addresses
            if(!ipv6CompAddr(&socket->remoteIpAddr.ipv6Addr, &pseudoHeader->ipv6Data.srcAddr))
               continue;
         }
      }
      else
#endif
      //An invalid packet was received?
      {
         //This should never occur...
         continue;
      }

      //The current socket meets all the criteria
      break;
   }

   //Point to the payload
   offset += sizeof(UdpHeader);
   length -= sizeof(UdpHeader);

   //No matching socket found?
   if(i >= SOCKET_MAX_COUNT)
   {
      //Leave critical section
      osMutexRelease(socketMutex);
      //Invoke user callback, if any
      error = udpInvokeRxCallback(interface, pseudoHeader, header, buffer, offset);
      //Return status code
      return error;
   }

   //Empty receive queue?
   if(!socket->receiveQueue)
   {
      //Allocate a memory buffer to hold the data and the associated descriptor
      p = chunkedBufferAlloc(sizeof(SocketQueueItem) + length);

      //Successful memory allocation?
      if(p != NULL)
      {
         //Point to the newly created item
         queueItem = chunkedBufferAt(p, 0);
         queueItem->buffer = p;
         //Add the newly created item to the queue
         socket->receiveQueue = queueItem;
      }
      else
      {
         //Memory allocation failed
         queueItem = NULL;
      }
   }
   else
   {
      //Point to the very first item
      queueItem = socket->receiveQueue;
      //Reach the last item in the receive queue
      for(i = 1; queueItem->next; i++)
         queueItem = queueItem->next;

      //Make sure the receive queue is not full
      if(i >= UDP_RX_QUEUE_SIZE)
      {
         //Leave critical section
         osMutexRelease(socketMutex);
         //Notify the calling function that the queue is full
         return ERROR_RECEIVE_QUEUE_FULL;
      }

      //Allocate a memory buffer to hold the data and the associated descriptor
      p = chunkedBufferAlloc(sizeof(SocketQueueItem) + length);

      //Successful memory allocation?
      if(p != NULL)
      {
         //Add the newly created item to the queue
         queueItem->next = chunkedBufferAt(p, 0);
         //Point to the newly created item
         queueItem = queueItem->next;
         queueItem->buffer = p;
      }
      else
      {
         //Memory allocation failed
         queueItem = NULL;
      }
   }

   //Failed to allocate memory?
   if(!queueItem)
   {
      //Leave critical section
      osMutexRelease(socketMutex);
      //Return error code
      return ERROR_OUT_OF_MEMORY;
   }

   //Initialize next field
   queueItem->next = NULL;
   //Record the source port number
   queueItem->srcPort = ntohs(header->srcPort);

#if (IPV4_SUPPORT == ENABLED)
   //IPv4 remote address?
   if(pseudoHeader->length == sizeof(Ipv4PseudoHeader))
   {
      //Save the source IPv4 address
      queueItem->srcIpAddr.length = sizeof(Ipv4Addr);
      queueItem->srcIpAddr.ipv4Addr = pseudoHeader->ipv4Data.srcAddr;
      //Save the destination IPv4 address
      queueItem->destIpAddr.length = sizeof(Ipv4Addr);
      queueItem->destIpAddr.ipv4Addr = pseudoHeader->ipv4Data.destAddr;
   }
#endif
#if (IPV6_SUPPORT == ENABLED)
   //IPv6 remote address?
   if(pseudoHeader->length == sizeof(Ipv6PseudoHeader))
   {
      //Save the source IPv6 address
      queueItem->srcIpAddr.length = sizeof(Ipv6Addr);
      queueItem->srcIpAddr.ipv6Addr = pseudoHeader->ipv6Data.srcAddr;
      //Save the destination IPv6 address
      queueItem->destIpAddr.length = sizeof(Ipv6Addr);
      queueItem->destIpAddr.ipv6Addr = pseudoHeader->ipv6Data.destAddr;
   }
#endif

   //Offset to the payload
   queueItem->offset = sizeof(SocketQueueItem);
   //Copy the payload
   chunkedBufferCopy(queueItem->buffer, queueItem->offset, buffer, offset, length);

   //Notify user that data is available
   udpUpdateEvents(socket);

   //Leave critical section
   osMutexRelease(socketMutex);
   //Successful processing
   return NO_ERROR;
}
Esempio n. 25
0
void dnsProcessResponse(NetInterface *interface, const IpPseudoHeader *pseudoHeader,
   const UdpHeader *udpHeader, const ChunkedBuffer *buffer, size_t offset, void *params)
{
   uint_t i;
   uint_t j;
   size_t n;
   size_t pos;
   size_t length;
   DnsHeader *message;
   DnsQuestion *question;
   DnsResourceRecord *resourceRecord;
   DnsCacheEntry *entry;

   //Retrieve the length of the DNS message
   length = chunkedBufferGetLength(buffer) - offset;

   //Ensure the DNS message is valid
   if(length < sizeof(DnsHeader))
      return;
   if(length > DNS_MESSAGE_MAX_SIZE)
      return;

   //Point to the DNS message header
   message = chunkedBufferAt(buffer, offset);
   //Sanity check
   if(!message) return;

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

   //Acquire exclusive access to the DNS cache
   osMutexAcquire(dnsCacheMutex);

   //Loop through DNS cache entries
   for(i = 0; i < DNS_CACHE_SIZE; i++)
   {
      //Point to the current entry
      entry = &dnsCache[i];

      //DNS name resolution in progress?
      if(entry->state == DNS_STATE_IN_PROGRESS &&
         entry->protocol == HOST_NAME_RESOLVER_DNS)
      {
         //Check destination port number
         if(entry->port == ntohs(udpHeader->destPort))
         {
            //Compare identifier against expected one
            if(ntohs(message->id) != entry->id)
               break;
            //Check message type
            if(!message->qr)
               break;
            //The DNS message shall contain one question
            if(ntohs(message->qdcount) != 1)
               break;

            //Point to the first question
            pos = sizeof(DnsHeader);

            //Parse domain name
            n = dnsParseName(message, length, pos, NULL, 0);

            //Invalid name?
            if(!n)
               break;
            //Malformed mDNS message?
            if((n + sizeof(DnsQuestion)) > length)
               break;

            //Compare domain name
            if(!dnsCompareName(message, length, pos, entry->name, 0))
               break;

            //Point to the corresponding entry
            question = DNS_GET_QUESTION(message, n);

            //Check the class of the query
            if(ntohs(question->qclass) != DNS_RR_CLASS_IN)
               break;
            //Check the type of the query
            if(entry->type == HOST_TYPE_IPV4 && ntohs(question->qtype) != DNS_RR_TYPE_A)
               break;
            if(entry->type == HOST_TYPE_IPV6 && ntohs(question->qtype) != DNS_RR_TYPE_AAAA)
               break;

            //Make sure recursion is available
            if(!message->ra)
            {
               //The entry should be deleted since name resolution has failed
               dnsDeleteEntry(entry);
               //Exit immediately
               break;
            }

            //Check return code
            if(message->rcode != DNS_RCODE_NO_ERROR)
            {
               //The entry should be deleted since name resolution has failed
               dnsDeleteEntry(entry);
               //Exit immediately
               break;
            }

            //Point to the first answer
            pos = n + sizeof(DnsQuestion);

            //Parse answer resource records
            for(j = 0; j < ntohs(message->ancount); j++)
            {
               //Parse domain name
               pos = dnsParseName(message, length, pos, NULL, 0);
               //Invalid name?
               if(!pos) break;

               //Point to the associated resource record
               resourceRecord = DNS_GET_RESOURCE_RECORD(message, pos);
               //Point to the resource data
               pos += sizeof(DnsResourceRecord);

               //Make sure the resource record is valid
               if(pos >= length)
                  break;
               if((pos + ntohs(resourceRecord->rdlength)) > length)
                  break;

#if (IPV4_SUPPORT == ENABLED)
               //IPv4 address expected?
               if(entry->type == HOST_TYPE_IPV4)
               {
                  //A resource record found?
                  if(ntohs(resourceRecord->rtype) == DNS_RR_TYPE_A)
                  {
                     //Verify the length of the data field
                     if(ntohs(resourceRecord->rdlength) == sizeof(Ipv4Addr))
                     {
                        //Copy the IPv4 address
                        entry->ipAddr.length = sizeof(Ipv4Addr);
                        ipv4CopyAddr(&entry->ipAddr.ipv4Addr, resourceRecord->rdata);

                        //Save current time
                        entry->timestamp = osGetTickCount();
                        //Save TTL value
                        entry->timeout = ntohl(resourceRecord->ttl) * 1000;
                        //Limit the lifetime of the DNS cache entries
                        entry->timeout = min(entry->timeout, DNS_MAX_LIFETIME);

                        //Unregister UDP callback function
                        udpDetachRxCallback(interface, entry->port);
                        //Host name successfully resolved
                        entry->state = DNS_STATE_RESOLVED;
                        //Exit immediately
                        break;
                     }
                  }
               }
#endif
#if (IPV6_SUPPORT == ENABLED)
               //IPv6 address expected?
               if(entry->type == HOST_TYPE_IPV6)
               {
                  //AAAA resource record found?
                  if(ntohs(resourceRecord->rtype) == DNS_RR_TYPE_AAAA)
                  {
                     //Verify the length of the data field
                     if(ntohs(resourceRecord->rdlength) == sizeof(Ipv6Addr))
                     {
                        //Copy the IPv6 address
                        entry->ipAddr.length = sizeof(Ipv6Addr);
                        ipv6CopyAddr(&entry->ipAddr.ipv6Addr, resourceRecord->rdata);

                        //Save current time
                        entry->timestamp = osGetTickCount();
                        //Save TTL value
                        entry->timeout = ntohl(resourceRecord->ttl) * 1000;
                        //Limit the lifetime of the DNS cache entries
                        entry->timeout = min(entry->timeout, DNS_MAX_LIFETIME);

                        //Unregister UDP callback function
                        udpDetachRxCallback(interface, entry->port);
                        //Host name successfully resolved
                        entry->state = DNS_STATE_RESOLVED;
                        //Exit immediately
                        break;
                     }
                  }
               }
#endif
               //Point to the next resource record
               pos += ntohs(resourceRecord->rdlength);
            }

            //We are done
            break;
         }
      }
   }

   //Release exclusive access to the DNS cache
   osMutexRelease(dnsCacheMutex);
}