Webserver+3d print

Dependents:   Nucleo

Revision:
0:8918a71cdbe9
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ipv4/igmp.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,633 @@
+/**
+ * @file igmp.c
+ * @brief IGMP (Internet Group Management Protocol)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ * @section Description
+ *
+ * IGMP is used by IP hosts to report their multicast group memberships
+ * to routers. Refer to the following RFCs for complete details:
+ * - RFC 1112: Host Extensions for IP Multicasting
+ * - RFC 2236: Internet Group Management Protocol, Version 2
+ * - RFC 3376: Internet Group Management Protocol, Version 3
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL IGMP_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "core/ip.h"
+#include "ipv4/ipv4.h"
+#include "ipv4/igmp.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (IPV4_SUPPORT == ENABLED && IGMP_SUPPORT == ENABLED)
+
+//Tick counter to handle periodic operations
+systime_t igmpTickCounter;
+
+
+/**
+ * @brief IGMP initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t igmpInit(NetInterface *interface)
+{
+   //The default host compatibility mode is IGMPv2
+   interface->igmpv1RouterPresent = FALSE;
+
+   //Start IGMPv1 router present timer
+   interface->igmpv1RouterPresentTimer =
+      osGetSystemTime() + IGMP_V1_ROUTER_PRESENT_TIMEOUT;
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Join the specified host group
+ * @param[in] interface Underlying network interface
+ * @param[in] entry IPv4 filter entry identifying the host group to join
+ * @return Error code
+ **/
+
+error_t igmpJoinGroup(NetInterface *interface, Ipv4FilterEntry *entry)
+{
+   //The all-systems group (address 224.0.0.1) is handled as a special
+   //case. The host starts in Idle Member state for that group on every
+   //interface and never transitions to another state
+   if(entry->addr == IGMP_ALL_SYSTEMS_ADDR)
+   {
+      //Clear flag
+      entry->flag = FALSE;
+      //Enter the Idle Member state
+      entry->state = IGMP_STATE_IDLE_MEMBER;
+   }
+   else
+   {
+      //Link is up?
+      if(interface->linkState)
+      {
+         //When a host joins a multicast group, it should immediately transmit
+         //an unsolicited Membership Report for that group
+         igmpSendReportMessage(interface, entry->addr);
+
+         //Set flag
+         entry->flag = TRUE;
+         //Start timer
+         entry->timer = osGetSystemTime() + IGMP_UNSOLICITED_REPORT_INTERVAL;
+         //Enter the Delaying Member state
+         entry->state = IGMP_STATE_DELAYING_MEMBER;
+      }
+      //Link is down?
+      else
+      {
+         //Clear flag
+         entry->flag = FALSE;
+         //Enter the Idle Member state
+         entry->state = IGMP_STATE_IDLE_MEMBER;
+      }
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Leave the specified host group
+ * @param[in] interface Underlying network interface
+ * @param[in] entry IPv4 filter entry identifying the host group to leave
+ * @return Error code
+ **/
+
+error_t igmpLeaveGroup(NetInterface *interface, Ipv4FilterEntry *entry)
+{
+   //Check link state
+   if(interface->linkState)
+   {
+      //Send a Leave Group message if the flag is set
+      if(entry->flag)
+         igmpSendLeaveGroupMessage(interface, entry->addr);
+   }
+
+   //Switch to the Non-Member state
+   entry->state = IGMP_STATE_NON_MEMBER;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief IGMP timer handler
+ *
+ * This routine must be periodically called by the TCP/IP stack to
+ * handle IGMP related timers
+ *
+ * @param[in] interface Underlying network interface
+ **/
+
+void igmpTick(NetInterface *interface)
+{
+   uint_t i;
+   systime_t time;
+   Ipv4FilterEntry *entry;
+
+   //Get current time
+   time = osGetSystemTime();
+
+   //Check IGMPv1 router present timer
+   if(timeCompare(time, interface->igmpv1RouterPresentTimer) >= 0)
+      interface->igmpv1RouterPresent = FALSE;
+
+   //Go through the multicast filter table
+   for(i = 0; i < IPV4_MULTICAST_FILTER_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->ipv4Context.multicastFilter[i];
+
+      //Valid entry?
+      if(entry->refCount > 0)
+      {
+         //Delaying Member state?
+         if(entry->state == IGMP_STATE_DELAYING_MEMBER)
+         {
+            //Timer expired?
+            if(timeCompare(time, entry->timer) >= 0)
+            {
+               //Send a Membership Report message for the group on the interface
+               igmpSendReportMessage(interface, entry->addr);
+
+               //Set flag
+               entry->flag = TRUE;
+               //Switch to the Idle Member state
+               entry->state = IGMP_STATE_IDLE_MEMBER;
+            }
+         }
+      }
+   }
+}
+
+
+/**
+ * @brief Callback function for link change event
+ * @param[in] interface Underlying network interface
+ **/
+
+void igmpLinkChangeEvent(NetInterface *interface)
+{
+   uint_t i;
+   systime_t time;
+   Ipv4FilterEntry *entry;
+
+   //Get current time
+   time = osGetSystemTime();
+
+   //Link up event?
+   if(interface->linkState)
+   {
+      //The default host compatibility mode is IGMPv2
+      interface->igmpv1RouterPresent = FALSE;
+      //Start IGMPv1 router present timer
+      interface->igmpv1RouterPresentTimer = time + IGMP_V1_ROUTER_PRESENT_TIMEOUT;
+
+      //Go through the multicast filter table
+      for(i = 0; i < IPV4_MULTICAST_FILTER_SIZE; i++)
+      {
+         //Point to the current entry
+         entry = &interface->ipv4Context.multicastFilter[i];
+
+         //Valid entry?
+         if(entry->refCount > 0)
+         {
+            //The all-systems group (address 224.0.0.1) is handled as a special
+            //case. The host starts in Idle Member state for that group on every
+            //interface and never transitions to another state
+            if(entry->addr != IGMP_ALL_SYSTEMS_ADDR)
+            {
+               //Send an unsolicited Membership Report for that group
+               igmpSendReportMessage(interface, entry->addr);
+
+               //Set flag
+               entry->flag = TRUE;
+               //Start timer
+               entry->timer = time + IGMP_UNSOLICITED_REPORT_INTERVAL;
+               //Enter the Delaying Member state
+               entry->state = IGMP_STATE_DELAYING_MEMBER;
+            }
+         }
+      }
+   }
+   //Link down event?
+   else
+   {
+      //Go through the multicast filter table
+      for(i = 0; i < IPV4_MULTICAST_FILTER_SIZE; i++)
+      {
+         //Point to the current entry
+         entry = &interface->ipv4Context.multicastFilter[i];
+
+         //Valid entry?
+         if(entry->refCount > 0)
+         {
+            //Clear flag
+            entry->flag = FALSE;
+            //Enter the Idle Member state
+            entry->state = IGMP_STATE_IDLE_MEMBER;
+         }
+      }
+   }
+}
+
+
+/**
+ * @brief Process incoming IGMP message
+ * @param[in] interface Underlying network interface
+ * @param[in] buffer Multi-part buffer containing the incoming IGMP message
+ * @param[in] offset Offset to the first byte of the IGMP message
+ **/
+
+void igmpProcessMessage(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset)
+{
+   size_t length;
+   IgmpMessage *message;
+
+   //Retrieve the length of the IGMP message
+   length = netBufferGetLength(buffer) - offset;
+
+   //Ensure the message length is correct
+   if(length < sizeof(IgmpMessage))
+   {
+      //Debug message
+      TRACE_WARNING("IGMP message length is invalid!\r\n");
+      //Silently discard incoming message
+      return;
+   }
+
+   //Point to the beginning of the IGMP message
+   message = netBufferAt(buffer, offset);
+   //Sanity check
+   if(message == NULL)
+      return;
+
+   //Debug message
+   TRACE_INFO("IGMP message received (%" PRIuSIZE " bytes)...\r\n", length);
+   //Dump message contents for debugging purpose
+   igmpDumpMessage(message);
+
+   //Verify checksum value
+   if(ipCalcChecksumEx(buffer, offset, length) != 0x0000)
+   {
+      //Debug message
+      TRACE_WARNING("Wrong IGMP header checksum!\r\n");
+      //Drop incoming message
+      return;
+   }
+
+   //Check the type field
+   switch(message->type)
+   {
+   //Membership Query message?
+   case IGMP_TYPE_MEMBERSHIP_QUERY:
+      //Process Membership Query message
+      igmpProcessQueryMessage(interface, message, length);
+      break;
+   //Membership Report message?
+   case IGMP_TYPE_MEMBERSHIP_REPORT_V1:
+   case IGMP_TYPE_MEMBERSHIP_REPORT_V2:
+      //Process Membership Query message
+      igmpProcessReportMessage(interface, message, length);
+      break;
+   //Unknown type?
+   default:
+      //Debug message
+      TRACE_WARNING("Unknown IGMP message type!\r\n");
+      //Discard incoming IGMP message
+      break;
+   }
+}
+
+
+/**
+ * @brief Process incoming Membership Query message
+ * @param[in] interface Underlying network interface
+ * @param[in] message Incoming Membership Query message
+ * @param[in] length Message length
+ **/
+
+void igmpProcessQueryMessage(NetInterface *interface,
+   const IgmpMessage *message, size_t length)
+{
+   uint_t i;
+   systime_t time;
+   systime_t maxRespTime;
+   Ipv4FilterEntry *entry;
+
+   //Get current time
+   time = osGetSystemTime();
+
+   //IGMPv1 Membership Query message?
+   if(message->maxRespTime == 0)
+   {
+      //The host receives a query with the Max Response Time field set to 0
+      interface->igmpv1RouterPresent = TRUE;
+      //Restart IGMPv1 router present timer
+      interface->igmpv1RouterPresentTimer = time + IGMP_V1_ROUTER_PRESENT_TIMEOUT;
+      //The maximum response time is 10 seconds by default
+      maxRespTime = IGMP_V1_MAX_RESPONSE_TIME;
+   }
+   //IGMPv2 Membership Query message?
+   else
+   {
+      //The Max Resp Time field specifies the maximum time allowed
+      //before sending a responding report
+      maxRespTime = message->maxRespTime * 10;
+   }
+
+   //Go through the multicast filter table
+   for(i = 0; i < IPV4_MULTICAST_FILTER_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->ipv4Context.multicastFilter[i];
+
+      //Valid entry?
+      if(entry->refCount > 0)
+      {
+         //The all-systems group (224.0.0.1) is handled as a special case. The
+         //host starts in Idle Member state for that group on every interface
+         //and never transitions to another state
+         if(entry->addr != IGMP_ALL_SYSTEMS_ADDR)
+         {
+            //A General Query applies to all memberships on the interface from which
+            //the Query is received. A Group-Specific Query applies to membership
+            //in a single group on the interface from which the Query is received
+            if(message->groupAddr == IPV4_UNSPECIFIED_ADDR ||
+               message->groupAddr == entry->addr)
+            {
+               //Delaying Member state?
+               if(entry->state == IGMP_STATE_DELAYING_MEMBER)
+               {
+                  //The timer has not yet expired?
+                  if(timeCompare(time, entry->timer) < 0)
+                  {
+                     //If a timer for the group is already running, it is reset to
+                     //the random value only if the requested Max Response Time is
+                     //less than the remaining value of the running timer
+                     if(maxRespTime < (entry->timer - time))
+                     {
+                        //Restart delay timer
+                        entry->timer = time + igmpRand(maxRespTime);
+                     }
+                  }
+               }
+               //Idle Member state?
+               else if(entry->state == IGMP_STATE_IDLE_MEMBER)
+               {
+                  //Switch to the Delaying Member state
+                  entry->state = IGMP_STATE_DELAYING_MEMBER;
+                  //Delay the response by a random amount of time
+                  entry->timer = time + igmpRand(maxRespTime);
+               }
+            }
+         }
+      }
+   }
+}
+
+
+/**
+ * @brief Process incoming Membership Report message
+ * @param[in] interface Underlying network interface
+ * @param[in] message Incoming Membership Report message
+ * @param[in] length Message length
+ **/
+
+void igmpProcessReportMessage(NetInterface *interface,
+   const IgmpMessage *message, size_t length)
+{
+   uint_t i;
+   Ipv4FilterEntry *entry;
+
+   //Go through the multicast filter table
+   for(i = 0; i < IPV4_MULTICAST_FILTER_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->ipv4Context.multicastFilter[i];
+
+      //Valid entry?
+      if(entry->refCount > 0)
+      {
+         //Report messages are ignored for memberships in
+         //the Non-Member or Idle Member state
+         if(entry->state == IGMP_STATE_DELAYING_MEMBER)
+         {
+            //The Membership Report message matches the current entry?
+            if(message->groupAddr == entry->addr)
+            {
+               //Clear flag
+               entry->flag = FALSE;
+               //Switch to the Idle Member state
+               entry->state = IGMP_STATE_IDLE_MEMBER;
+            }
+         }
+      }
+   }
+}
+
+
+/**
+ * @brief Send Membership Report message
+ * @param[in] interface Underlying network interface
+ * @param[in] ipAddr IPv4 address specifying the group address
+ * @return Error code
+ **/
+
+error_t igmpSendReportMessage(NetInterface *interface, Ipv4Addr ipAddr)
+{
+   error_t error;
+   size_t offset;
+   IgmpMessage *message;
+   NetBuffer *buffer;
+   Ipv4PseudoHeader pseudoHeader;
+
+   //Make sure the specified address is a valid multicast address
+   if(!ipv4IsMulticastAddr(ipAddr))
+      return ERROR_INVALID_ADDRESS;
+
+   //The all-systems group (224.0.0.1) is handled as a special case.
+   //The host never sends a report for that group
+   if(ipAddr == IGMP_ALL_SYSTEMS_ADDR)
+      return ERROR_INVALID_ADDRESS;
+
+   //Allocate a memory buffer to hold an IGMP message
+   buffer = ipAllocBuffer(sizeof(IgmpMessage), &offset);
+   //Failed to allocate memory?
+   if(buffer == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Point to the beginning of the IGMP message
+   message = netBufferAt(buffer, offset);
+
+   //The type of report is determined by the state of the interface
+   if(interface->igmpv1RouterPresent)
+      message->type = IGMP_TYPE_MEMBERSHIP_REPORT_V1;
+   else
+      message->type = IGMP_TYPE_MEMBERSHIP_REPORT_V2;
+
+   //Format the Membership Report message
+   message->maxRespTime = 0;
+   message->checksum = 0;
+   message->groupAddr = ipAddr;
+
+   //Message checksum calculation
+   message->checksum = ipCalcChecksumEx(buffer, offset, sizeof(IgmpMessage));
+
+   //Format IPv4 pseudo header
+   pseudoHeader.srcAddr = interface->ipv4Context.addr;
+   pseudoHeader.destAddr = ipAddr;
+   pseudoHeader.reserved = 0;
+   pseudoHeader.protocol = IPV4_PROTOCOL_IGMP;
+   pseudoHeader.length = HTONS(sizeof(IgmpMessage));
+
+   //Debug message
+   TRACE_INFO("Sending IGMP message (%" PRIuSIZE " bytes)...\r\n", sizeof(IgmpMessage));
+   //Dump message contents for debugging purpose
+   igmpDumpMessage(message);
+
+   //The Membership Report message is sent to the group being reported
+   error = ipv4SendDatagram(interface, &pseudoHeader, buffer, offset, IGMP_TTL);
+
+   //Free previously allocated memory
+   netBufferFree(buffer);
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Send Leave Group message
+ * @param[in] interface Underlying network interface
+ * @param[in] ipAddr IPv4 address specifying the group address being left
+ * @return Error code
+ **/
+
+error_t igmpSendLeaveGroupMessage(NetInterface *interface, Ipv4Addr ipAddr)
+{
+   error_t error;
+   size_t offset;
+   NetBuffer *buffer;
+   IgmpMessage *message;
+   Ipv4PseudoHeader pseudoHeader;
+
+   //Make sure the specified address is a valid multicast address
+   if(!ipv4IsMulticastAddr(ipAddr))
+      return ERROR_INVALID_ADDRESS;
+
+   //The all-systems group (224.0.0.1) is handled as a special case.
+   //The host never sends a Leave Group message for that group
+   if(ipAddr == IGMP_ALL_SYSTEMS_ADDR)
+      return ERROR_INVALID_ADDRESS;
+
+   //If the interface state says the querier is running
+   //IGMPv1, this action should be skipped
+   if(interface->igmpv1RouterPresent)
+      return NO_ERROR;
+
+   //Allocate a memory buffer to hold an IGMP message
+   buffer = ipAllocBuffer(sizeof(IgmpMessage), &offset);
+   //Failed to allocate memory?
+   if(buffer == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Point to the beginning of the IGMP message
+   message = netBufferAt(buffer, offset);
+
+   //Format the Leave Group message
+   message->type = IGMP_TYPE_LEAVE_GROUP;
+   message->maxRespTime = 0;
+   message->checksum = 0;
+   message->groupAddr = ipAddr;
+
+   //Message checksum calculation
+   message->checksum = ipCalcChecksumEx(buffer, offset, sizeof(IgmpMessage));
+
+   //Format IPv4 pseudo header
+   pseudoHeader.srcAddr = interface->ipv4Context.addr;
+   pseudoHeader.destAddr = IGMP_ALL_ROUTERS_ADDR;
+   pseudoHeader.reserved = 0;
+   pseudoHeader.protocol = IPV4_PROTOCOL_IGMP;
+   pseudoHeader.length = HTONS(sizeof(IgmpMessage));
+
+   //Debug message
+   TRACE_INFO("Sending IGMP message (%" PRIuSIZE " bytes)...\r\n", sizeof(IgmpMessage));
+   //Dump message contents for debugging purpose
+   igmpDumpMessage(message);
+
+   //The Leave Group message is sent to the all-routers multicast group
+   error = ipv4SendDatagram(interface, &pseudoHeader, buffer, offset, IGMP_TTL);
+
+   //Free previously allocated memory
+   netBufferFree(buffer);
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Get a random value in the specified range
+ * @param[in] max Upper bound
+ * @return Random value in the specified range
+ **/
+
+uint32_t igmpRand(uint32_t max)
+{
+   //Return a random value in the given range
+   return netGetRand() % (max + 1);
+}
+
+
+/**
+ * @brief Dump IGMP message for debugging purpose
+ * @param[in] message Pointer to the IGMP message
+ **/
+
+void igmpDumpMessage(const IgmpMessage *message)
+{
+   //Dump IGMP message
+   TRACE_DEBUG("  Type = 0x%02" PRIX8 "\r\n", message->type);
+   TRACE_DEBUG("  Max Resp Time = 0x%02" PRIX8 "\r\n", message->maxRespTime);
+   TRACE_DEBUG("  Checksum = 0x%04" PRIX16 "\r\n", ntohs(message->checksum));
+   TRACE_DEBUG("  Group Address = %s\r\n", ipv4AddrToString(message->groupAddr, NULL));
+}
+
+#endif
+