Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
mld.c
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
Generated on Tue Jul 12 2022 17:10:14 by
1.7.2