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