Webserver+3d print
Embed:
(wiki syntax)
Show/hide line numbers
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
Generated on Tue Jul 12 2022 17:10:14 by
