Webserver+3d print

Dependents:   Nucleo

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers mld.c Source File

mld.c

Go to the documentation of this file.
00001 /**
00002  * @file mld.c
00003  * @brief MLD (Multicast Listener Discovery for IPv6)
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  * MLD is used by an IPv6 router to discover the presence of multicast
00028  * listeners on its directly attached links, and to discover specifically
00029  * which multicast addresses are of interest to those neighboring nodes.
00030  * Refer to the following RFCs for complete details:
00031  * - RFC 2710: Multicast Listener Discovery (MLD) for IPv6
00032  * - RFC 3810: Multicast Listener Discovery Version 2 (MLDv2) for IPv6
00033  *
00034  * @author Oryx Embedded SARL (www.oryx-embedded.com)
00035  * @version 1.7.6
00036  **/
00037 
00038 //Switch to the appropriate trace level
00039 #define TRACE_LEVEL MLD_TRACE_LEVEL
00040 
00041 //Dependencies
00042 #include "core/net.h"
00043 #include "core/ip.h"
00044 #include "ipv6/ipv6.h"
00045 #include "ipv6/icmpv6.h"
00046 #include "ipv6/mld.h"
00047 #include "debug.h"
00048 
00049 //Check TCP/IP stack configuration
00050 #if (IPV6_SUPPORT == ENABLED && MLD_SUPPORT == ENABLED)
00051 
00052 //Tick counter to handle periodic operations
00053 systime_t mldTickCounter;
00054 
00055 
00056 /**
00057  * @brief MLD initialization
00058  * @param[in] interface Underlying network interface
00059  * @return Error code
00060  **/
00061 
00062 error_t mldInit(NetInterface *interface)
00063 {
00064    //Successful initialization
00065    return NO_ERROR;
00066 }
00067 
00068 
00069 /**
00070  * @brief Start listening to the address on the interface
00071  * @param[in] interface Underlying network interface
00072  * @param[in] entry IPv6 filter entry identifying the address to listen to
00073  * @return Error code
00074  **/
00075 
00076 error_t mldStartListening(NetInterface *interface, Ipv6FilterEntry *entry)
00077 {
00078    //The link-scope all-nodes address (FF02::1) is handled as a special
00079    //case. The host starts in Idle Listener state for that address on
00080    //every interface and never transitions to another state
00081    if(ipv6CompAddr(&entry->addr, &IPV6_LINK_LOCAL_ALL_NODES_ADDR))
00082    {
00083       //Clear flag
00084       entry->flag = FALSE;
00085       //Enter the Idle Listener state
00086       entry->state = MLD_STATE_IDLE_LISTENER;
00087    }
00088    else
00089    {
00090       //Link is up?
00091       if(interface->linkState)
00092       {
00093          //Send a Multicast Listener Report message for the group on the interface
00094          mldSendListenerReport(interface, &entry->addr);
00095 
00096          //Set flag
00097          entry->flag = TRUE;
00098          //Start timer
00099          entry->timer = osGetSystemTime() + MLD_UNSOLICITED_REPORT_INTERVAL;
00100          //Enter the Delaying Listener state
00101          entry->state = MLD_STATE_DELAYING_LISTENER;
00102       }
00103       //Link is down?
00104       else
00105       {
00106          //Clear flag
00107          entry->flag = FALSE;
00108          //Enter the Idle Listener state
00109          entry->state = MLD_STATE_IDLE_LISTENER;
00110       }
00111    }
00112 
00113    //Successful processing
00114    return NO_ERROR;
00115 }
00116 
00117 
00118 /**
00119  * @brief Stop listening to the address on the interface
00120  * @param[in] interface Underlying network interface
00121  * @param[in] entry IPv6 filter entry identifying the multicast address to leave
00122  * @return Error code
00123  **/
00124 
00125 error_t mldStopListening(NetInterface *interface, Ipv6FilterEntry *entry)
00126 {
00127    //Check link state
00128    if(interface->linkState)
00129    {
00130       //Send a Multicast Listener Done message if the flag is set
00131       if(entry->flag)
00132          mldSendListenerDone(interface, &entry->addr);
00133    }
00134 
00135    //Switch to the Non-Listener state
00136    entry->state = MLD_STATE_NON_LISTENER;
00137 
00138    //Successful processing
00139    return NO_ERROR;
00140 }
00141 
00142 
00143 /**
00144  * @brief MLD timer handler
00145  *
00146  * This routine must be periodically called by the TCP/IP stack to
00147  * handle MLD related timers
00148  *
00149  * @param[in] interface Underlying network interface
00150  **/
00151 
00152 void mldTick(NetInterface *interface)
00153 {
00154    uint_t i;
00155    systime_t time;
00156    Ipv6FilterEntry *entry;
00157 
00158    //Get current time
00159    time = osGetSystemTime();
00160 
00161    //Go through the multicast filter table
00162    for(i = 0; i < IPV6_MULTICAST_FILTER_SIZE; i++)
00163    {
00164       //Point to the current entry
00165       entry = &interface->ipv6Context.multicastFilter[i];
00166 
00167       //Valid entry?
00168       if(entry->refCount > 0)
00169       {
00170          //Delaying Listener state?
00171          if(entry->state == MLD_STATE_DELAYING_LISTENER)
00172          {
00173             //Timer expired?
00174             if(timeCompare(time, entry->timer) >= 0)
00175             {
00176                //Send a Multicast Listener Report message
00177                mldSendListenerReport(interface, &entry->addr);
00178 
00179                //Set flag
00180                entry->flag = TRUE;
00181                //Switch to the Idle Listener state
00182                entry->state = MLD_STATE_IDLE_LISTENER;
00183             }
00184          }
00185       }
00186    }
00187 }
00188 
00189 
00190 /**
00191  * @brief Callback function for link change event
00192  * @param[in] interface Underlying network interface
00193  **/
00194 
00195 void mldLinkChangeEvent(NetInterface *interface)
00196 {
00197    uint_t i;
00198    systime_t time;
00199    Ipv6FilterEntry *entry;
00200 
00201    //Get current time
00202    time = osGetSystemTime();
00203 
00204    //Link up event?
00205    if(interface->linkState)
00206    {
00207       //Go through the multicast filter table
00208       for(i = 0; i < IPV6_MULTICAST_FILTER_SIZE; i++)
00209       {
00210          //Point to the current entry
00211          entry = &interface->ipv6Context.multicastFilter[i];
00212 
00213          //Valid entry?
00214          if(entry->refCount > 0)
00215          {
00216             //The link-scope all-nodes address (FF02::1) is handled as a special
00217             //case. The host starts in Idle Listener state for that address on
00218             //every interface and never transitions to another state
00219             if(!ipv6CompAddr(&entry->addr, &IPV6_LINK_LOCAL_ALL_NODES_ADDR))
00220             {
00221                //Send an unsolicited Multicast Listener Report message for that group
00222                mldSendListenerReport(interface, &entry->addr);
00223 
00224                //Set flag
00225                entry->flag = TRUE;
00226                //Start timer
00227                entry->timer = time + MLD_UNSOLICITED_REPORT_INTERVAL;
00228                //Enter the Delaying Listener state
00229                entry->state = MLD_STATE_DELAYING_LISTENER;
00230             }
00231          }
00232       }
00233    }
00234    //Link down event?
00235    else
00236    {
00237       //Go through the multicast filter table
00238       for(i = 0; i < IPV6_MULTICAST_FILTER_SIZE; i++)
00239       {
00240          //Point to the current entry
00241          entry = &interface->ipv6Context.multicastFilter[i];
00242 
00243          //Valid entry?
00244          if(entry->refCount > 0)
00245          {
00246             //Clear flag
00247             entry->flag = FALSE;
00248             //Enter the Idle Listener state
00249             entry->state = MLD_STATE_IDLE_LISTENER;
00250          }
00251       }
00252    }
00253 }
00254 
00255 
00256 /**
00257  * @brief Process incoming Multicast Listener Query message
00258  * @param[in] interface Underlying network interface
00259  * @param[in] pseudoHeader IPv6 pseudo header
00260  * @param[in] buffer Multi-part buffer containing the incoming MLD message
00261  * @param[in] offset Offset to the first byte of the MLD message
00262  * @param[in] hopLimit Hop Limit field from IPv6 header
00263  **/
00264 
00265 void mldProcessListenerQuery(NetInterface *interface, Ipv6PseudoHeader *pseudoHeader,
00266    const NetBuffer *buffer, size_t offset, uint8_t hopLimit)
00267 {
00268    uint_t i;
00269    size_t length;
00270    systime_t time;
00271    systime_t maxRespDelay;
00272    MldMessage *message;
00273    Ipv6FilterEntry *entry;
00274 
00275    //Retrieve the length of the MLD message
00276    length = netBufferGetLength(buffer) - offset;
00277 
00278    //The message must be at least 24 octets long
00279    if(length < sizeof(MldMessage))
00280       return;
00281 
00282    //Point to the beginning of the MLD message
00283    message = netBufferAt(buffer, offset);
00284    //Sanity check
00285    if(message == NULL)
00286       return;
00287 
00288    //Debug message
00289    TRACE_INFO("MLD message received (%" PRIuSIZE " bytes)...\r\n", length);
00290    //Dump message contents for debugging purpose
00291    mldDumpMessage(message);
00292 
00293    //Make sure the source address of the message is a valid link-local address
00294    if(!ipv6IsLinkLocalUnicastAddr(&pseudoHeader->srcAddr))
00295       return;
00296 
00297    //Check the Hop Limit field
00298    if(hopLimit != MLD_HOP_LIMIT)
00299       return;
00300 
00301    //Get current time
00302    time = osGetSystemTime();
00303 
00304    //The Max Resp Delay field specifies the maximum time allowed
00305    //before sending a responding report
00306    maxRespDelay = message->maxRespDelay * 10;
00307 
00308    //Go through the multicast filter table
00309    for(i = 0; i < IPV6_MULTICAST_FILTER_SIZE; i++)
00310    {
00311       //Point to the current entry
00312       entry = &interface->ipv6Context.multicastFilter[i];
00313 
00314       //Valid entry?
00315       if(entry->refCount > 0)
00316       {
00317          //The link-scope all-nodes address (FF02::1) is handled as a special
00318          //case. The host starts in Idle Listener state for that address on
00319          //every interface and never transitions to another state
00320          if(!ipv6CompAddr(&entry->addr, &IPV6_LINK_LOCAL_ALL_NODES_ADDR))
00321          {
00322             //A General Query is used to learn which multicast addresses have listeners
00323             //on an attached link. A Multicast-Address-Specific Query is used to learn
00324             //if a particular multicast address has any listeners on an attached link
00325             if(ipv6CompAddr(&message->multicastAddr, &IPV6_UNSPECIFIED_ADDR) ||
00326                ipv6CompAddr(&message->multicastAddr, &entry->addr))
00327             {
00328                //Delaying Listener state?
00329                if(entry->state == MLD_STATE_DELAYING_LISTENER)
00330                {
00331                   //The timer has not yet expired?
00332                   if(timeCompare(time, entry->timer) < 0)
00333                   {
00334                      //If a timer for the address is already running, it is reset to
00335                      //the new random value only if the requested Max Response Delay
00336                      //is less than the remaining value of the running timer
00337                      if(maxRespDelay < (entry->timer - time))
00338                      {
00339                         //Restart delay timer
00340                         entry->timer = time + mldRand(maxRespDelay);
00341                      }
00342                   }
00343                }
00344                //Idle Listener state?
00345                else if(entry->state == MLD_STATE_IDLE_LISTENER)
00346                {
00347                   //Switch to the Delaying Listener state
00348                   entry->state = MLD_STATE_DELAYING_LISTENER;
00349                   //Delay the response by a random amount of time
00350                   entry->timer = time + mldRand(maxRespDelay);
00351                }
00352             }
00353          }
00354       }
00355    }
00356 }
00357 
00358 
00359 /**
00360  * @brief Process incoming Multicast Listener Report message
00361  * @param[in] interface Underlying network interface
00362  * @param[in] pseudoHeader IPv6 pseudo header
00363  * @param[in] buffer Multi-part buffer containing the incoming MLD message
00364  * @param[in] offset Offset to the first byte of the MLD message
00365  * @param[in] hopLimit Hop Limit field from IPv6 header
00366  **/
00367 
00368 void mldProcessListenerReport(NetInterface *interface, Ipv6PseudoHeader *pseudoHeader,
00369    const NetBuffer *buffer, size_t offset, uint8_t hopLimit)
00370 {
00371    uint_t i;
00372    size_t length;
00373    MldMessage *message;
00374    Ipv6FilterEntry *entry;
00375 
00376    //Retrieve the length of the MLD message
00377    length = netBufferGetLength(buffer) - offset;
00378 
00379    //The message must be at least 24 octets long
00380    if(length < sizeof(MldMessage))
00381       return;
00382 
00383    //Point to the beginning of the MLD message
00384    message = netBufferAt(buffer, offset);
00385    //Sanity check
00386    if(message == NULL)
00387       return;
00388 
00389    //Debug message
00390    TRACE_INFO("MLD message received (%" PRIuSIZE " bytes)...\r\n", length);
00391    //Dump message contents for debugging purpose
00392    mldDumpMessage(message);
00393 
00394    //Make sure the source address of the message is a valid link-local address
00395    if(!ipv6IsLinkLocalUnicastAddr(&pseudoHeader->srcAddr))
00396       return;
00397    //Check the Hop Limit field
00398    if(hopLimit != MLD_HOP_LIMIT)
00399       return;
00400 
00401    //Go through the multicast filter table
00402    for(i = 0; i < IPV6_MULTICAST_FILTER_SIZE; i++)
00403    {
00404       //Point to the current entry
00405       entry = &interface->ipv6Context.multicastFilter[i];
00406 
00407       //Valid entry?
00408       if(entry->refCount > 0)
00409       {
00410          //Report messages are ignored for multicast addresses
00411          //in the Non-Listener or Idle Listener state
00412          if(entry->state == MLD_STATE_DELAYING_LISTENER)
00413          {
00414             //The Multicast Listener Report message matches the current entry?
00415             if(ipv6CompAddr(&message->multicastAddr, &entry->addr))
00416             {
00417                //Clear flag
00418                entry->flag = FALSE;
00419                //Switch to the Idle Listener state
00420                entry->state = MLD_STATE_IDLE_LISTENER;
00421             }
00422          }
00423       }
00424    }
00425 }
00426 
00427 
00428 /**
00429  * @brief Send Multicast Listener Report message
00430  * @param[in] interface Underlying network interface
00431  * @param[in] ipAddr IPv6 address specifying the multicast address
00432  * @return Error code
00433  **/
00434 
00435 error_t mldSendListenerReport(NetInterface *interface, Ipv6Addr *ipAddr)
00436 {
00437    error_t error;
00438    size_t offset;
00439    MldMessage *message;
00440    NetBuffer *buffer;
00441    Ipv6PseudoHeader pseudoHeader;
00442 
00443    //Make sure the specified address is a valid multicast address
00444    if(!ipv6IsMulticastAddr(ipAddr))
00445       return ERROR_INVALID_ADDRESS;
00446 
00447    //The link-scope all-nodes address (FF02::1) is handled as a special
00448    //case. The host never sends a report for that address
00449    if(ipv6CompAddr(ipAddr, &IPV6_LINK_LOCAL_ALL_NODES_ADDR))
00450       return ERROR_INVALID_ADDRESS;
00451 
00452    //Allocate a memory buffer to hold a MLD message
00453    buffer = ipAllocBuffer(sizeof(MldMessage), &offset);
00454    //Failed to allocate memory?
00455    if(buffer == NULL)
00456       return ERROR_OUT_OF_MEMORY;
00457 
00458    //Point to the beginning of the MLD message
00459    message = netBufferAt(buffer, offset);
00460 
00461    //Format the Multicast Listener Report message
00462    message->type = ICMPV6_TYPE_MULTICAST_LISTENER_REPORT_V1;
00463    message->code = 0;
00464    message->checksum = 0;
00465    message->maxRespDelay = 0;
00466    message->reserved = 0;
00467    message->multicastAddr = *ipAddr;
00468 
00469    //Format IPv6 pseudo header
00470    pseudoHeader.srcAddr = interface->ipv6Context.addrList[0].addr;
00471    pseudoHeader.destAddr = *ipAddr;
00472    pseudoHeader.length = HTONS(sizeof(MldMessage));
00473    pseudoHeader.reserved = 0;
00474    pseudoHeader.nextHeader = IPV6_ICMPV6_HEADER;
00475 
00476    //Message checksum calculation
00477    message->checksum = ipCalcUpperLayerChecksumEx(&pseudoHeader,
00478       sizeof(Ipv6PseudoHeader), buffer, offset, sizeof(MldMessage));
00479 
00480    //Debug message
00481    TRACE_INFO("Sending MLD message (%" PRIuSIZE " bytes)...\r\n", sizeof(MldMessage));
00482    //Dump message contents for debugging purpose
00483    mldDumpMessage(message);
00484 
00485    //The Multicast Listener Report message is sent to the multicast address being reported
00486    error = ipv6SendDatagram(interface, &pseudoHeader, buffer, offset, MLD_HOP_LIMIT);
00487 
00488    //Free previously allocated memory
00489    netBufferFree(buffer);
00490    //Return status code
00491    return error;
00492 }
00493 
00494 
00495 /**
00496  * @brief Send Multicast Listener Done message
00497  * @param[in] interface Underlying network interface
00498  * @param[in] ipAddr IPv6 address specifying the multicast address being left
00499  * @return Error code
00500  **/
00501 
00502 error_t mldSendListenerDone(NetInterface *interface, Ipv6Addr *ipAddr)
00503 {
00504    error_t error;
00505    size_t offset;
00506    MldMessage *message;
00507    NetBuffer *buffer;
00508    Ipv6PseudoHeader pseudoHeader;
00509 
00510    //Make sure the specified address is a valid multicast address
00511    if(!ipv6IsMulticastAddr(ipAddr))
00512       return ERROR_INVALID_ADDRESS;
00513 
00514    //The link-scope all-nodes address (FF02::1) is handled as a special
00515    //case. The host never sends a report for that address
00516    if(ipv6CompAddr(ipAddr, &IPV6_LINK_LOCAL_ALL_NODES_ADDR))
00517       return ERROR_INVALID_ADDRESS;
00518 
00519    //Allocate a memory buffer to hold a MLD message
00520    buffer = ipAllocBuffer(sizeof(MldMessage), &offset);
00521    //Failed to allocate memory?
00522    if(buffer == NULL)
00523       return ERROR_OUT_OF_MEMORY;
00524 
00525    //Point to the beginning of the MLD message
00526    message = netBufferAt(buffer, offset);
00527 
00528    //Format the Multicast Listener Done message
00529    message->type = ICMPV6_TYPE_MULTICAST_LISTENER_DONE_V1;
00530    message->code = 0;
00531    message->checksum = 0;
00532    message->maxRespDelay = 0;
00533    message->reserved = 0;
00534    message->multicastAddr = *ipAddr;
00535 
00536    //Format IPv6 pseudo header
00537    pseudoHeader.srcAddr = interface->ipv6Context.addrList[0].addr;
00538    pseudoHeader.destAddr = IPV6_LINK_LOCAL_ALL_ROUTERS_ADDR;
00539    pseudoHeader.length = HTONS(sizeof(MldMessage));
00540    pseudoHeader.reserved = 0;
00541    pseudoHeader.nextHeader = IPV6_ICMPV6_HEADER;
00542 
00543    //Message checksum calculation
00544    message->checksum = ipCalcUpperLayerChecksumEx(&pseudoHeader,
00545       sizeof(Ipv6PseudoHeader), buffer, offset, sizeof(MldMessage));
00546 
00547    //Debug message
00548    TRACE_INFO("Sending MLD message (%" PRIuSIZE " bytes)...\r\n", sizeof(MldMessage));
00549    //Dump message contents for debugging purpose
00550    mldDumpMessage(message);
00551 
00552    //The Multicast Listener Done message is sent to the all-routers multicast address
00553    error = ipv6SendDatagram(interface, &pseudoHeader, buffer, offset, MLD_HOP_LIMIT);
00554 
00555    //Free previously allocated memory
00556    netBufferFree(buffer);
00557    //Return status code
00558    return error;
00559 }
00560 
00561 
00562 /**
00563  * @brief Get a random value in the specified range
00564  * @param[in] max Upper bound
00565  * @return Random value in the specified range
00566  **/
00567 
00568 uint32_t mldRand(uint32_t max)
00569 {
00570    //Return a random value in the given range
00571    return netGetRand() % (max + 1);
00572 }
00573 
00574 
00575 /**
00576  * @brief Dump MLD message for debugging purpose
00577  * @param[in] message Pointer to the MLD message
00578  **/
00579 
00580 void mldDumpMessage(const MldMessage *message)
00581 {
00582    //Dump MLD message
00583    TRACE_DEBUG("  Type = %" PRIu8 "\r\n", message->type);
00584    TRACE_DEBUG("  Code = %" PRIu8 "\r\n", message->code);
00585    TRACE_DEBUG("  Checksum = 0x%04" PRIX16 "\r\n", ntohs(message->checksum));
00586    TRACE_DEBUG("  Max Resp Delay = %" PRIu16 "\r\n", message->maxRespDelay);
00587    TRACE_DEBUG("  Multicast Address = %s\r\n", ipv6AddrToString(&message->multicastAddr, NULL));
00588 }
00589 
00590 #endif
00591