Webserver+3d print

Dependents:   Nucleo

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers igmp.c Source File

igmp.c

Go to the documentation of this file.
00001 /**
00002  * @file igmp.c
00003  * @brief IGMP (Internet Group Management Protocol)
00004  *
00005  * @section License
00006  *
00007  * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
00008  *
00009  * This file is part of CycloneTCP Open.
00010  *
00011  * This program is free software; you can redistribute it and/or
00012  * modify it under the terms of the GNU General Public License
00013  * as published by the Free Software Foundation; either version 2
00014  * of the License, or (at your option) any later version.
00015  *
00016  * This program is distributed in the hope that it will be useful,
00017  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019  * GNU General Public License for more details.
00020  *
00021  * You should have received a copy of the GNU General Public License
00022  * along with this program; if not, write to the Free Software Foundation,
00023  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
00024  *
00025  * @section Description
00026  *
00027  * IGMP is used by IP hosts to report their multicast group memberships
00028  * to routers. Refer to the following RFCs for complete details:
00029  * - RFC 1112: Host Extensions for IP Multicasting
00030  * - RFC 2236: Internet Group Management Protocol, Version 2
00031  * - RFC 3376: Internet Group Management Protocol, Version 3
00032  *
00033  * @author Oryx Embedded SARL (www.oryx-embedded.com)
00034  * @version 1.7.6
00035  **/
00036 
00037 //Switch to the appropriate trace level
00038 #define TRACE_LEVEL IGMP_TRACE_LEVEL
00039 
00040 //Dependencies
00041 #include "core/net.h"
00042 #include "core/ip.h"
00043 #include "ipv4/ipv4.h"
00044 #include "ipv4/igmp.h"
00045 #include "debug.h"
00046 
00047 //Check TCP/IP stack configuration
00048 #if (IPV4_SUPPORT == ENABLED && IGMP_SUPPORT == ENABLED)
00049 
00050 //Tick counter to handle periodic operations
00051 systime_t igmpTickCounter;
00052 
00053 
00054 /**
00055  * @brief IGMP initialization
00056  * @param[in] interface Underlying network interface
00057  * @return Error code
00058  **/
00059 
00060 error_t igmpInit(NetInterface *interface)
00061 {
00062    //The default host compatibility mode is IGMPv2
00063    interface->igmpv1RouterPresent = FALSE;
00064 
00065    //Start IGMPv1 router present timer
00066    interface->igmpv1RouterPresentTimer =
00067       osGetSystemTime() + IGMP_V1_ROUTER_PRESENT_TIMEOUT;
00068 
00069    //Successful initialization
00070    return NO_ERROR;
00071 }
00072 
00073 
00074 /**
00075  * @brief Join the specified host group
00076  * @param[in] interface Underlying network interface
00077  * @param[in] entry IPv4 filter entry identifying the host group to join
00078  * @return Error code
00079  **/
00080 
00081 error_t igmpJoinGroup(NetInterface *interface, Ipv4FilterEntry *entry)
00082 {
00083    //The all-systems group (address 224.0.0.1) is handled as a special
00084    //case. The host starts in Idle Member state for that group on every
00085    //interface and never transitions to another state
00086    if(entry->addr == IGMP_ALL_SYSTEMS_ADDR)
00087    {
00088       //Clear flag
00089       entry->flag = FALSE;
00090       //Enter the Idle Member state
00091       entry->state = IGMP_STATE_IDLE_MEMBER;
00092    }
00093    else
00094    {
00095       //Link is up?
00096       if(interface->linkState)
00097       {
00098          //When a host joins a multicast group, it should immediately transmit
00099          //an unsolicited Membership Report for that group
00100          igmpSendReportMessage(interface, entry->addr);
00101 
00102          //Set flag
00103          entry->flag = TRUE;
00104          //Start timer
00105          entry->timer = osGetSystemTime() + IGMP_UNSOLICITED_REPORT_INTERVAL;
00106          //Enter the Delaying Member state
00107          entry->state = IGMP_STATE_DELAYING_MEMBER;
00108       }
00109       //Link is down?
00110       else
00111       {
00112          //Clear flag
00113          entry->flag = FALSE;
00114          //Enter the Idle Member state
00115          entry->state = IGMP_STATE_IDLE_MEMBER;
00116       }
00117    }
00118 
00119    //Successful processing
00120    return NO_ERROR;
00121 }
00122 
00123 
00124 /**
00125  * @brief Leave the specified host group
00126  * @param[in] interface Underlying network interface
00127  * @param[in] entry IPv4 filter entry identifying the host group to leave
00128  * @return Error code
00129  **/
00130 
00131 error_t igmpLeaveGroup(NetInterface *interface, Ipv4FilterEntry *entry)
00132 {
00133    //Check link state
00134    if(interface->linkState)
00135    {
00136       //Send a Leave Group message if the flag is set
00137       if(entry->flag)
00138          igmpSendLeaveGroupMessage(interface, entry->addr);
00139    }
00140 
00141    //Switch to the Non-Member state
00142    entry->state = IGMP_STATE_NON_MEMBER;
00143 
00144    //Successful processing
00145    return NO_ERROR;
00146 }
00147 
00148 
00149 /**
00150  * @brief IGMP timer handler
00151  *
00152  * This routine must be periodically called by the TCP/IP stack to
00153  * handle IGMP related timers
00154  *
00155  * @param[in] interface Underlying network interface
00156  **/
00157 
00158 void igmpTick(NetInterface *interface)
00159 {
00160    uint_t i;
00161    systime_t time;
00162    Ipv4FilterEntry *entry;
00163 
00164    //Get current time
00165    time = osGetSystemTime();
00166 
00167    //Check IGMPv1 router present timer
00168    if(timeCompare(time, interface->igmpv1RouterPresentTimer) >= 0)
00169       interface->igmpv1RouterPresent = FALSE;
00170 
00171    //Go through the multicast filter table
00172    for(i = 0; i < IPV4_MULTICAST_FILTER_SIZE; i++)
00173    {
00174       //Point to the current entry
00175       entry = &interface->ipv4Context.multicastFilter[i];
00176 
00177       //Valid entry?
00178       if(entry->refCount > 0)
00179       {
00180          //Delaying Member state?
00181          if(entry->state == IGMP_STATE_DELAYING_MEMBER)
00182          {
00183             //Timer expired?
00184             if(timeCompare(time, entry->timer) >= 0)
00185             {
00186                //Send a Membership Report message for the group on the interface
00187                igmpSendReportMessage(interface, entry->addr);
00188 
00189                //Set flag
00190                entry->flag = TRUE;
00191                //Switch to the Idle Member state
00192                entry->state = IGMP_STATE_IDLE_MEMBER;
00193             }
00194          }
00195       }
00196    }
00197 }
00198 
00199 
00200 /**
00201  * @brief Callback function for link change event
00202  * @param[in] interface Underlying network interface
00203  **/
00204 
00205 void igmpLinkChangeEvent(NetInterface *interface)
00206 {
00207    uint_t i;
00208    systime_t time;
00209    Ipv4FilterEntry *entry;
00210 
00211    //Get current time
00212    time = osGetSystemTime();
00213 
00214    //Link up event?
00215    if(interface->linkState)
00216    {
00217       //The default host compatibility mode is IGMPv2
00218       interface->igmpv1RouterPresent = FALSE;
00219       //Start IGMPv1 router present timer
00220       interface->igmpv1RouterPresentTimer = time + IGMP_V1_ROUTER_PRESENT_TIMEOUT;
00221 
00222       //Go through the multicast filter table
00223       for(i = 0; i < IPV4_MULTICAST_FILTER_SIZE; i++)
00224       {
00225          //Point to the current entry
00226          entry = &interface->ipv4Context.multicastFilter[i];
00227 
00228          //Valid entry?
00229          if(entry->refCount > 0)
00230          {
00231             //The all-systems group (address 224.0.0.1) is handled as a special
00232             //case. The host starts in Idle Member state for that group on every
00233             //interface and never transitions to another state
00234             if(entry->addr != IGMP_ALL_SYSTEMS_ADDR)
00235             {
00236                //Send an unsolicited Membership Report for that group
00237                igmpSendReportMessage(interface, entry->addr);
00238 
00239                //Set flag
00240                entry->flag = TRUE;
00241                //Start timer
00242                entry->timer = time + IGMP_UNSOLICITED_REPORT_INTERVAL;
00243                //Enter the Delaying Member state
00244                entry->state = IGMP_STATE_DELAYING_MEMBER;
00245             }
00246          }
00247       }
00248    }
00249    //Link down event?
00250    else
00251    {
00252       //Go through the multicast filter table
00253       for(i = 0; i < IPV4_MULTICAST_FILTER_SIZE; i++)
00254       {
00255          //Point to the current entry
00256          entry = &interface->ipv4Context.multicastFilter[i];
00257 
00258          //Valid entry?
00259          if(entry->refCount > 0)
00260          {
00261             //Clear flag
00262             entry->flag = FALSE;
00263             //Enter the Idle Member state
00264             entry->state = IGMP_STATE_IDLE_MEMBER;
00265          }
00266       }
00267    }
00268 }
00269 
00270 
00271 /**
00272  * @brief Process incoming IGMP message
00273  * @param[in] interface Underlying network interface
00274  * @param[in] buffer Multi-part buffer containing the incoming IGMP message
00275  * @param[in] offset Offset to the first byte of the IGMP message
00276  **/
00277 
00278 void igmpProcessMessage(NetInterface *interface,
00279    const NetBuffer *buffer, size_t offset)
00280 {
00281    size_t length;
00282    IgmpMessage *message;
00283 
00284    //Retrieve the length of the IGMP message
00285    length = netBufferGetLength(buffer) - offset;
00286 
00287    //Ensure the message length is correct
00288    if(length < sizeof(IgmpMessage))
00289    {
00290       //Debug message
00291       TRACE_WARNING("IGMP message length is invalid!\r\n");
00292       //Silently discard incoming message
00293       return;
00294    }
00295 
00296    //Point to the beginning of the IGMP message
00297    message = netBufferAt(buffer, offset);
00298    //Sanity check
00299    if(message == NULL)
00300       return;
00301 
00302    //Debug message
00303    TRACE_INFO("IGMP message received (%" PRIuSIZE " bytes)...\r\n", length);
00304    //Dump message contents for debugging purpose
00305    igmpDumpMessage(message);
00306 
00307    //Verify checksum value
00308    if(ipCalcChecksumEx(buffer, offset, length) != 0x0000)
00309    {
00310       //Debug message
00311       TRACE_WARNING("Wrong IGMP header checksum!\r\n");
00312       //Drop incoming message
00313       return;
00314    }
00315 
00316    //Check the type field
00317    switch(message->type)
00318    {
00319    //Membership Query message?
00320    case IGMP_TYPE_MEMBERSHIP_QUERY:
00321       //Process Membership Query message
00322       igmpProcessQueryMessage(interface, message, length);
00323       break;
00324    //Membership Report message?
00325    case IGMP_TYPE_MEMBERSHIP_REPORT_V1:
00326    case IGMP_TYPE_MEMBERSHIP_REPORT_V2:
00327       //Process Membership Query message
00328       igmpProcessReportMessage(interface, message, length);
00329       break;
00330    //Unknown type?
00331    default:
00332       //Debug message
00333       TRACE_WARNING("Unknown IGMP message type!\r\n");
00334       //Discard incoming IGMP message
00335       break;
00336    }
00337 }
00338 
00339 
00340 /**
00341  * @brief Process incoming Membership Query message
00342  * @param[in] interface Underlying network interface
00343  * @param[in] message Incoming Membership Query message
00344  * @param[in] length Message length
00345  **/
00346 
00347 void igmpProcessQueryMessage(NetInterface *interface,
00348    const IgmpMessage *message, size_t length)
00349 {
00350    uint_t i;
00351    systime_t time;
00352    systime_t maxRespTime;
00353    Ipv4FilterEntry *entry;
00354 
00355    //Get current time
00356    time = osGetSystemTime();
00357 
00358    //IGMPv1 Membership Query message?
00359    if(message->maxRespTime == 0)
00360    {
00361       //The host receives a query with the Max Response Time field set to 0
00362       interface->igmpv1RouterPresent = TRUE;
00363       //Restart IGMPv1 router present timer
00364       interface->igmpv1RouterPresentTimer = time + IGMP_V1_ROUTER_PRESENT_TIMEOUT;
00365       //The maximum response time is 10 seconds by default
00366       maxRespTime = IGMP_V1_MAX_RESPONSE_TIME;
00367    }
00368    //IGMPv2 Membership Query message?
00369    else
00370    {
00371       //The Max Resp Time field specifies the maximum time allowed
00372       //before sending a responding report
00373       maxRespTime = message->maxRespTime * 10;
00374    }
00375 
00376    //Go through the multicast filter table
00377    for(i = 0; i < IPV4_MULTICAST_FILTER_SIZE; i++)
00378    {
00379       //Point to the current entry
00380       entry = &interface->ipv4Context.multicastFilter[i];
00381 
00382       //Valid entry?
00383       if(entry->refCount > 0)
00384       {
00385          //The all-systems group (224.0.0.1) is handled as a special case. The
00386          //host starts in Idle Member state for that group on every interface
00387          //and never transitions to another state
00388          if(entry->addr != IGMP_ALL_SYSTEMS_ADDR)
00389          {
00390             //A General Query applies to all memberships on the interface from which
00391             //the Query is received. A Group-Specific Query applies to membership
00392             //in a single group on the interface from which the Query is received
00393             if(message->groupAddr == IPV4_UNSPECIFIED_ADDR ||
00394                message->groupAddr == entry->addr)
00395             {
00396                //Delaying Member state?
00397                if(entry->state == IGMP_STATE_DELAYING_MEMBER)
00398                {
00399                   //The timer has not yet expired?
00400                   if(timeCompare(time, entry->timer) < 0)
00401                   {
00402                      //If a timer for the group is already running, it is reset to
00403                      //the random value only if the requested Max Response Time is
00404                      //less than the remaining value of the running timer
00405                      if(maxRespTime < (entry->timer - time))
00406                      {
00407                         //Restart delay timer
00408                         entry->timer = time + igmpRand(maxRespTime);
00409                      }
00410                   }
00411                }
00412                //Idle Member state?
00413                else if(entry->state == IGMP_STATE_IDLE_MEMBER)
00414                {
00415                   //Switch to the Delaying Member state
00416                   entry->state = IGMP_STATE_DELAYING_MEMBER;
00417                   //Delay the response by a random amount of time
00418                   entry->timer = time + igmpRand(maxRespTime);
00419                }
00420             }
00421          }
00422       }
00423    }
00424 }
00425 
00426 
00427 /**
00428  * @brief Process incoming Membership Report message
00429  * @param[in] interface Underlying network interface
00430  * @param[in] message Incoming Membership Report message
00431  * @param[in] length Message length
00432  **/
00433 
00434 void igmpProcessReportMessage(NetInterface *interface,
00435    const IgmpMessage *message, size_t length)
00436 {
00437    uint_t i;
00438    Ipv4FilterEntry *entry;
00439 
00440    //Go through the multicast filter table
00441    for(i = 0; i < IPV4_MULTICAST_FILTER_SIZE; i++)
00442    {
00443       //Point to the current entry
00444       entry = &interface->ipv4Context.multicastFilter[i];
00445 
00446       //Valid entry?
00447       if(entry->refCount > 0)
00448       {
00449          //Report messages are ignored for memberships in
00450          //the Non-Member or Idle Member state
00451          if(entry->state == IGMP_STATE_DELAYING_MEMBER)
00452          {
00453             //The Membership Report message matches the current entry?
00454             if(message->groupAddr == entry->addr)
00455             {
00456                //Clear flag
00457                entry->flag = FALSE;
00458                //Switch to the Idle Member state
00459                entry->state = IGMP_STATE_IDLE_MEMBER;
00460             }
00461          }
00462       }
00463    }
00464 }
00465 
00466 
00467 /**
00468  * @brief Send Membership Report message
00469  * @param[in] interface Underlying network interface
00470  * @param[in] ipAddr IPv4 address specifying the group address
00471  * @return Error code
00472  **/
00473 
00474 error_t igmpSendReportMessage(NetInterface *interface, Ipv4Addr ipAddr)
00475 {
00476    error_t error;
00477    size_t offset;
00478    IgmpMessage *message;
00479    NetBuffer *buffer;
00480    Ipv4PseudoHeader pseudoHeader;
00481 
00482    //Make sure the specified address is a valid multicast address
00483    if(!ipv4IsMulticastAddr(ipAddr))
00484       return ERROR_INVALID_ADDRESS;
00485 
00486    //The all-systems group (224.0.0.1) is handled as a special case.
00487    //The host never sends a report for that group
00488    if(ipAddr == IGMP_ALL_SYSTEMS_ADDR)
00489       return ERROR_INVALID_ADDRESS;
00490 
00491    //Allocate a memory buffer to hold an IGMP message
00492    buffer = ipAllocBuffer(sizeof(IgmpMessage), &offset);
00493    //Failed to allocate memory?
00494    if(buffer == NULL)
00495       return ERROR_OUT_OF_MEMORY;
00496 
00497    //Point to the beginning of the IGMP message
00498    message = netBufferAt(buffer, offset);
00499 
00500    //The type of report is determined by the state of the interface
00501    if(interface->igmpv1RouterPresent)
00502       message->type = IGMP_TYPE_MEMBERSHIP_REPORT_V1;
00503    else
00504       message->type = IGMP_TYPE_MEMBERSHIP_REPORT_V2;
00505 
00506    //Format the Membership Report message
00507    message->maxRespTime = 0;
00508    message->checksum = 0;
00509    message->groupAddr = ipAddr;
00510 
00511    //Message checksum calculation
00512    message->checksum = ipCalcChecksumEx(buffer, offset, sizeof(IgmpMessage));
00513 
00514    //Format IPv4 pseudo header
00515    pseudoHeader.srcAddr = interface->ipv4Context.addr;
00516    pseudoHeader.destAddr = ipAddr;
00517    pseudoHeader.reserved = 0;
00518    pseudoHeader.protocol = IPV4_PROTOCOL_IGMP;
00519    pseudoHeader.length = HTONS(sizeof(IgmpMessage));
00520 
00521    //Debug message
00522    TRACE_INFO("Sending IGMP message (%" PRIuSIZE " bytes)...\r\n", sizeof(IgmpMessage));
00523    //Dump message contents for debugging purpose
00524    igmpDumpMessage(message);
00525 
00526    //The Membership Report message is sent to the group being reported
00527    error = ipv4SendDatagram(interface, &pseudoHeader, buffer, offset, IGMP_TTL);
00528 
00529    //Free previously allocated memory
00530    netBufferFree(buffer);
00531    //Return status code
00532    return error;
00533 }
00534 
00535 
00536 /**
00537  * @brief Send Leave Group message
00538  * @param[in] interface Underlying network interface
00539  * @param[in] ipAddr IPv4 address specifying the group address being left
00540  * @return Error code
00541  **/
00542 
00543 error_t igmpSendLeaveGroupMessage(NetInterface *interface, Ipv4Addr ipAddr)
00544 {
00545    error_t error;
00546    size_t offset;
00547    NetBuffer *buffer;
00548    IgmpMessage *message;
00549    Ipv4PseudoHeader pseudoHeader;
00550 
00551    //Make sure the specified address is a valid multicast address
00552    if(!ipv4IsMulticastAddr(ipAddr))
00553       return ERROR_INVALID_ADDRESS;
00554 
00555    //The all-systems group (224.0.0.1) is handled as a special case.
00556    //The host never sends a Leave Group message for that group
00557    if(ipAddr == IGMP_ALL_SYSTEMS_ADDR)
00558       return ERROR_INVALID_ADDRESS;
00559 
00560    //If the interface state says the querier is running
00561    //IGMPv1, this action should be skipped
00562    if(interface->igmpv1RouterPresent)
00563       return NO_ERROR;
00564 
00565    //Allocate a memory buffer to hold an IGMP message
00566    buffer = ipAllocBuffer(sizeof(IgmpMessage), &offset);
00567    //Failed to allocate memory?
00568    if(buffer == NULL)
00569       return ERROR_OUT_OF_MEMORY;
00570 
00571    //Point to the beginning of the IGMP message
00572    message = netBufferAt(buffer, offset);
00573 
00574    //Format the Leave Group message
00575    message->type = IGMP_TYPE_LEAVE_GROUP;
00576    message->maxRespTime = 0;
00577    message->checksum = 0;
00578    message->groupAddr = ipAddr;
00579 
00580    //Message checksum calculation
00581    message->checksum = ipCalcChecksumEx(buffer, offset, sizeof(IgmpMessage));
00582 
00583    //Format IPv4 pseudo header
00584    pseudoHeader.srcAddr = interface->ipv4Context.addr;
00585    pseudoHeader.destAddr = IGMP_ALL_ROUTERS_ADDR;
00586    pseudoHeader.reserved = 0;
00587    pseudoHeader.protocol = IPV4_PROTOCOL_IGMP;
00588    pseudoHeader.length = HTONS(sizeof(IgmpMessage));
00589 
00590    //Debug message
00591    TRACE_INFO("Sending IGMP message (%" PRIuSIZE " bytes)...\r\n", sizeof(IgmpMessage));
00592    //Dump message contents for debugging purpose
00593    igmpDumpMessage(message);
00594 
00595    //The Leave Group message is sent to the all-routers multicast group
00596    error = ipv4SendDatagram(interface, &pseudoHeader, buffer, offset, IGMP_TTL);
00597 
00598    //Free previously allocated memory
00599    netBufferFree(buffer);
00600    //Return status code
00601    return error;
00602 }
00603 
00604 
00605 /**
00606  * @brief Get a random value in the specified range
00607  * @param[in] max Upper bound
00608  * @return Random value in the specified range
00609  **/
00610 
00611 uint32_t igmpRand(uint32_t max)
00612 {
00613    //Return a random value in the given range
00614    return netGetRand() % (max + 1);
00615 }
00616 
00617 
00618 /**
00619  * @brief Dump IGMP message for debugging purpose
00620  * @param[in] message Pointer to the IGMP message
00621  **/
00622 
00623 void igmpDumpMessage(const IgmpMessage *message)
00624 {
00625    //Dump IGMP message
00626    TRACE_DEBUG("  Type = 0x%02" PRIX8 "\r\n", message->type);
00627    TRACE_DEBUG("  Max Resp Time = 0x%02" PRIX8 "\r\n", message->maxRespTime);
00628    TRACE_DEBUG("  Checksum = 0x%04" PRIX16 "\r\n", ntohs(message->checksum));
00629    TRACE_DEBUG("  Group Address = %s\r\n", ipv4AddrToString(message->groupAddr, NULL));
00630 }
00631 
00632 #endif
00633