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.
igmp.c
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
Generated on Tue Jul 12 2022 17:10:13 by
1.7.2