1 /** 2 * @file 3 * IGMP - Internet Group Management Protocol 4 * 5 */ 6 7 /* 8 * Copyright (c) 2002 CITEL Technologies Ltd. 9 * All rights reserved. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. Neither the name of CITEL Technologies Ltd nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY CITEL TECHNOLOGIES AND CONTRIBUTORS ``AS IS'' 24 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL CITEL TECHNOLOGIES OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 * 35 * This file is a contribution to the lwIP TCP/IP stack. 36 * The Swedish Institute of Computer Science and Adam Dunkels 37 * are specifically granted permission to redistribute this 38 * source code. 39 */ 40 41 /*------------------------------------------------------------- 42 Note 1) 43 Although the rfc requires V1 AND V2 capability 44 we will only support v2 since now V1 is very old (August 1989) 45 V1 can be added if required 46 47 a debug print and statistic have been implemented to 48 show this up. 49 ------------------------------------------------------------- 50 ------------------------------------------------------------- 51 Note 2) 52 A query for a specific group address (as opposed to ALLHOSTS) 53 has now been implemented as I am unsure if it is required 54 55 a debug print and statistic have been implemented to 56 show this up. 57 ------------------------------------------------------------- 58 ------------------------------------------------------------- 59 Note 3) 60 The router alert rfc 2113 is implemented in outgoing packets 61 but not checked rigorously incoming 62 ------------------------------------------------------------- 63 Steve Reynolds 64 ------------------------------------------------------------*/ 65 66 /*----------------------------------------------------------------------------- 67 * RFC 988 - Host extensions for IP multicasting - V0 68 * RFC 1054 - Host extensions for IP multicasting - 69 * RFC 1112 - Host extensions for IP multicasting - V1 70 * RFC 2236 - Internet Group Management Protocol, Version 2 - V2 <- this code is based on this RFC (it's the "de facto" standard) 71 * RFC 3376 - Internet Group Management Protocol, Version 3 - V3 72 * RFC 4604 - Using Internet Group Management Protocol Version 3... - V3+ 73 * RFC 2113 - IP Router Alert Option - 74 *----------------------------------------------------------------------------*/ 75 76 /*----------------------------------------------------------------------------- 77 * Includes 78 *----------------------------------------------------------------------------*/ 79 80 #include "lwip/opt.h" 81 82 #if LWIP_IGMP /* don't build if not configured for use in lwipopts.h */ 83 84 #include "lwip/igmp.h" 85 #include "lwip/debug.h" 86 #include "lwip/def.h" 87 #include "lwip/mem.h" 88 #include "lwip/ip.h" 89 #include "lwip/inet_chksum.h" 90 #include "lwip/netif.h" 91 #include "lwip/icmp.h" 92 #include "lwip/udp.h" 93 #include "lwip/tcp.h" 94 #include "lwip/stats.h" 95 96 #include "string.h" 97 98 /* 99 * IGMP constants 100 */ 101 #define IGMP_TTL 1 102 #define IGMP_MINLEN 8 103 #define ROUTER_ALERT 0x9404U 104 #define ROUTER_ALERTLEN 4 105 106 /* 107 * IGMP message types, including version number. 108 */ 109 #define IGMP_MEMB_QUERY 0x11 /* Membership query */ 110 #define IGMP_V1_MEMB_REPORT 0x12 /* Ver. 1 membership report */ 111 #define IGMP_V2_MEMB_REPORT 0x16 /* Ver. 2 membership report */ 112 #define IGMP_LEAVE_GROUP 0x17 /* Leave-group message */ 113 114 /* Group membership states */ 115 #define IGMP_GROUP_NON_MEMBER 0 116 #define IGMP_GROUP_DELAYING_MEMBER 1 117 #define IGMP_GROUP_IDLE_MEMBER 2 118 119 /** 120 * IGMP packet format. 121 */ 122 #ifdef PACK_STRUCT_USE_INCLUDES 123 # include "arch/bpstruct.h" 124 #endif 125 PACK_STRUCT_BEGIN 126 struct igmp_msg { 127 PACK_STRUCT_FIELD(u8_t igmp_msgtype); 128 PACK_STRUCT_FIELD(u8_t igmp_maxresp); 129 PACK_STRUCT_FIELD(u16_t igmp_checksum); 130 PACK_STRUCT_FIELD(ip_addr_p_t igmp_group_address); 131 } PACK_STRUCT_STRUCT; 132 PACK_STRUCT_END 133 #ifdef PACK_STRUCT_USE_INCLUDES 134 # include "arch/epstruct.h" 135 #endif 136 137 138 static struct igmp_group *igmp_lookup_group(struct netif *ifp, ip_addr_t *addr); 139 static err_t igmp_remove_group(struct igmp_group *group); 140 static void igmp_timeout( struct igmp_group *group); 141 static void igmp_start_timer(struct igmp_group *group, u8_t max_time); 142 static void igmp_stop_timer(struct igmp_group *group); 143 static void igmp_delaying_member(struct igmp_group *group, u8_t maxresp); 144 static err_t igmp_ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, struct netif *netif); 145 static void igmp_send(struct igmp_group *group, u8_t type); 146 147 148 static struct igmp_group* igmp_group_list; 149 static ip_addr_t allsystems; 150 static ip_addr_t allrouters; 151 152 153 /** 154 * Initialize the IGMP module 155 */ 156 void 157 igmp_init(void) 158 { 159 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_init: initializing\n")); 160 161 IP4_ADDR(&allsystems, 224, 0, 0, 1); 162 IP4_ADDR(&allrouters, 224, 0, 0, 2); 163 } 164 165 #ifdef LWIP_DEBUG 166 /** 167 * Dump global IGMP groups list 168 */ 169 void 170 igmp_dump_group_list() 171 { 172 struct igmp_group *group = igmp_group_list; 173 174 while (group != NULL) { 175 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_dump_group_list: [%"U32_F"] ", (u32_t)(group->group_state))); 176 ip_addr_debug_print(IGMP_DEBUG, &group->group_address); 177 LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", group->netif)); 178 group = group->next; 179 } 180 LWIP_DEBUGF(IGMP_DEBUG, ("\n")); 181 } 182 #else 183 #define igmp_dump_group_list() 184 #endif /* LWIP_DEBUG */ 185 186 /** 187 * Start IGMP processing on interface 188 * 189 * @param netif network interface on which start IGMP processing 190 */ 191 err_t 192 igmp_start(struct netif *netif) 193 { 194 struct igmp_group* group; 195 196 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: starting IGMP processing on if %p\n", netif)); 197 198 group = igmp_lookup_group(netif, &allsystems); 199 200 if (group != NULL) { 201 group->group_state = IGMP_GROUP_IDLE_MEMBER; 202 group->use++; 203 204 /* Allow the igmp messages at the MAC level */ 205 if (netif->igmp_mac_filter != NULL) { 206 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: igmp_mac_filter(ADD ")); 207 ip_addr_debug_print(IGMP_DEBUG, &allsystems); 208 LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif)); 209 netif->igmp_mac_filter(netif, &allsystems, IGMP_ADD_MAC_FILTER); 210 } 211 212 return ERR_OK; 213 } 214 215 return ERR_MEM; 216 } 217 218 /** 219 * Stop IGMP processing on interface 220 * 221 * @param netif network interface on which stop IGMP processing 222 */ 223 err_t 224 igmp_stop(struct netif *netif) 225 { 226 struct igmp_group *group = igmp_group_list; 227 struct igmp_group *prev = NULL; 228 struct igmp_group *next; 229 230 /* look for groups joined on this interface further down the list */ 231 while (group != NULL) { 232 next = group->next; 233 /* is it a group joined on this interface? */ 234 if (group->netif == netif) { 235 /* is it the first group of the list? */ 236 if (group == igmp_group_list) { 237 igmp_group_list = next; 238 } 239 /* is there a "previous" group defined? */ 240 if (prev != NULL) { 241 prev->next = next; 242 } 243 /* disable the group at the MAC level */ 244 if (netif->igmp_mac_filter != NULL) { 245 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_stop: igmp_mac_filter(DEL ")); 246 ip_addr_debug_print(IGMP_DEBUG, &group->group_address); 247 LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif)); 248 netif->igmp_mac_filter(netif, &(group->group_address), IGMP_DEL_MAC_FILTER); 249 } 250 /* free group */ 251 memp_free(MEMP_IGMP_GROUP, group); 252 } else { 253 /* change the "previous" */ 254 prev = group; 255 } 256 /* move to "next" */ 257 group = next; 258 } 259 return ERR_OK; 260 } 261 262 /** 263 * Report IGMP memberships for this interface 264 * 265 * @param netif network interface on which report IGMP memberships 266 */ 267 void 268 igmp_report_groups(struct netif *netif) 269 { 270 struct igmp_group *group = igmp_group_list; 271 272 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_report_groups: sending IGMP reports on if %p\n", netif)); 273 274 while (group != NULL) { 275 if (group->netif == netif) { 276 igmp_delaying_member(group, IGMP_JOIN_DELAYING_MEMBER_TMR); 277 } 278 group = group->next; 279 } 280 } 281 282 /** 283 * Search for a group in the global igmp_group_list 284 * 285 * @param ifp the network interface for which to look 286 * @param addr the group ip address to search for 287 * @return a struct igmp_group* if the group has been found, 288 * NULL if the group wasn't found. 289 */ 290 struct igmp_group * 291 igmp_lookfor_group(struct netif *ifp, ip_addr_t *addr) 292 { 293 struct igmp_group *group = igmp_group_list; 294 295 while (group != NULL) { 296 if ((group->netif == ifp) && (ip_addr_cmp(&(group->group_address), addr))) { 297 return group; 298 } 299 group = group->next; 300 } 301 302 /* to be clearer, we return NULL here instead of 303 * 'group' (which is also NULL at this point). 304 */ 305 return NULL; 306 } 307 308 /** 309 * Search for a specific igmp group and create a new one if not found- 310 * 311 * @param ifp the network interface for which to look 312 * @param addr the group ip address to search 313 * @return a struct igmp_group*, 314 * NULL on memory error. 315 */ 316 struct igmp_group * 317 igmp_lookup_group(struct netif *ifp, ip_addr_t *addr) 318 { 319 struct igmp_group *group = igmp_group_list; 320 321 /* Search if the group already exists */ 322 group = igmp_lookfor_group(ifp, addr); 323 if (group != NULL) { 324 /* Group already exists. */ 325 return group; 326 } 327 328 /* Group doesn't exist yet, create a new one */ 329 group = (struct igmp_group *)memp_malloc(MEMP_IGMP_GROUP); 330 if (group != NULL) { 331 group->netif = ifp; 332 ip_addr_set(&(group->group_address), addr); 333 group->timer = 0; /* Not running */ 334 group->group_state = IGMP_GROUP_NON_MEMBER; 335 group->last_reporter_flag = 0; 336 group->use = 0; 337 group->next = igmp_group_list; 338 339 igmp_group_list = group; 340 } 341 342 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_lookup_group: %sallocated a new group with address ", (group?"":"impossible to "))); 343 ip_addr_debug_print(IGMP_DEBUG, addr); 344 LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", ifp)); 345 346 return group; 347 } 348 349 /** 350 * Remove a group in the global igmp_group_list 351 * 352 * @param group the group to remove from the global igmp_group_list 353 * @return ERR_OK if group was removed from the list, an err_t otherwise 354 */ 355 static err_t 356 igmp_remove_group(struct igmp_group *group) 357 { 358 err_t err = ERR_OK; 359 360 /* Is it the first group? */ 361 if (igmp_group_list == group) { 362 igmp_group_list = group->next; 363 } else { 364 /* look for group further down the list */ 365 struct igmp_group *tmpGroup; 366 for (tmpGroup = igmp_group_list; tmpGroup != NULL; tmpGroup = tmpGroup->next) { 367 if (tmpGroup->next == group) { 368 tmpGroup->next = group->next; 369 break; 370 } 371 } 372 /* Group not found in the global igmp_group_list */ 373 if (tmpGroup == NULL) 374 err = ERR_ARG; 375 } 376 /* free group */ 377 memp_free(MEMP_IGMP_GROUP, group); 378 379 return err; 380 } 381 382 /** 383 * Called from ip_input() if a new IGMP packet is received. 384 * 385 * @param p received igmp packet, p->payload pointing to the ip header 386 * @param inp network interface on which the packet was received 387 * @param dest destination ip address of the igmp packet 388 */ 389 void 390 igmp_input(struct pbuf *p, struct netif *inp, ip_addr_t *dest) 391 { 392 struct ip_hdr * iphdr; 393 struct igmp_msg* igmp; 394 struct igmp_group* group; 395 struct igmp_group* groupref; 396 397 IGMP_STATS_INC(igmp.recv); 398 399 /* Note that the length CAN be greater than 8 but only 8 are used - All are included in the checksum */ 400 iphdr = (struct ip_hdr *)p->payload; 401 if (pbuf_header(p, -(s16_t)(IPH_HL(iphdr) * 4)) || (p->len < IGMP_MINLEN)) { 402 pbuf_free(p); 403 IGMP_STATS_INC(igmp.lenerr); 404 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: length error\n")); 405 return; 406 } 407 408 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: message from ")); 409 ip_addr_debug_print(IGMP_DEBUG, &(iphdr->src)); 410 LWIP_DEBUGF(IGMP_DEBUG, (" to address ")); 411 ip_addr_debug_print(IGMP_DEBUG, &(iphdr->dest)); 412 LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", inp)); 413 414 /* Now calculate and check the checksum */ 415 igmp = (struct igmp_msg *)p->payload; 416 if (inet_chksum(igmp, p->len)) { 417 pbuf_free(p); 418 IGMP_STATS_INC(igmp.chkerr); 419 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: checksum error\n")); 420 return; 421 } 422 423 /* Packet is ok so find an existing group */ 424 group = igmp_lookfor_group(inp, dest); /* use the destination IP address of incoming packet */ 425 426 /* If group can be found or create... */ 427 if (!group) { 428 pbuf_free(p); 429 IGMP_STATS_INC(igmp.drop); 430 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP frame not for us\n")); 431 return; 432 } 433 434 /* NOW ACT ON THE INCOMING MESSAGE TYPE... */ 435 switch (igmp->igmp_msgtype) { 436 case IGMP_MEMB_QUERY: { 437 /* IGMP_MEMB_QUERY to the "all systems" address ? */ 438 if ((ip_addr_cmp(dest, &allsystems)) && ip_addr_isany(&igmp->igmp_group_address)) { 439 /* THIS IS THE GENERAL QUERY */ 440 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: General IGMP_MEMB_QUERY on \"ALL SYSTEMS\" address (224.0.0.1) [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp))); 441 442 if (igmp->igmp_maxresp == 0) { 443 IGMP_STATS_INC(igmp.rx_v1); 444 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: got an all hosts query with time== 0 - this is V1 and not implemented - treat as v2\n")); 445 igmp->igmp_maxresp = IGMP_V1_DELAYING_MEMBER_TMR; 446 } else { 447 IGMP_STATS_INC(igmp.rx_general); 448 } 449 450 groupref = igmp_group_list; 451 while (groupref) { 452 /* Do not send messages on the all systems group address! */ 453 if ((groupref->netif == inp) && (!(ip_addr_cmp(&(groupref->group_address), &allsystems)))) { 454 igmp_delaying_member(groupref, igmp->igmp_maxresp); 455 } 456 groupref = groupref->next; 457 } 458 } else { 459 /* IGMP_MEMB_QUERY to a specific group ? */ 460 if (!ip_addr_isany(&igmp->igmp_group_address)) { 461 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_MEMB_QUERY to a specific group ")); 462 ip_addr_debug_print(IGMP_DEBUG, &igmp->igmp_group_address); 463 if (ip_addr_cmp(dest, &allsystems)) { 464 ip_addr_t groupaddr; 465 LWIP_DEBUGF(IGMP_DEBUG, (" using \"ALL SYSTEMS\" address (224.0.0.1) [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp))); 466 /* we first need to re-look for the group since we used dest last time */ 467 ip_addr_copy(groupaddr, igmp->igmp_group_address); 468 group = igmp_lookfor_group(inp, &groupaddr); 469 } else { 470 LWIP_DEBUGF(IGMP_DEBUG, (" with the group address as destination [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp))); 471 } 472 473 if (group != NULL) { 474 IGMP_STATS_INC(igmp.rx_group); 475 igmp_delaying_member(group, igmp->igmp_maxresp); 476 } else { 477 IGMP_STATS_INC(igmp.drop); 478 } 479 } else { 480 IGMP_STATS_INC(igmp.proterr); 481 } 482 } 483 break; 484 } 485 case IGMP_V2_MEMB_REPORT: { 486 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_V2_MEMB_REPORT\n")); 487 IGMP_STATS_INC(igmp.rx_report); 488 if (group->group_state == IGMP_GROUP_DELAYING_MEMBER) { 489 /* This is on a specific group we have already looked up */ 490 group->timer = 0; /* stopped */ 491 group->group_state = IGMP_GROUP_IDLE_MEMBER; 492 group->last_reporter_flag = 0; 493 } 494 break; 495 } 496 default: { 497 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: unexpected msg %d in state %d on group %p on if %p\n", 498 igmp->igmp_msgtype, group->group_state, &group, group->netif)); 499 IGMP_STATS_INC(igmp.proterr); 500 break; 501 } 502 } 503 504 pbuf_free(p); 505 return; 506 } 507 508 /** 509 * Join a group on one network interface. 510 * 511 * @param ifaddr ip address of the network interface which should join a new group 512 * @param groupaddr the ip address of the group which to join 513 * @return ERR_OK if group was joined on the netif(s), an err_t otherwise 514 */ 515 err_t 516 igmp_joingroup(ip_addr_t *ifaddr, ip_addr_t *groupaddr) 517 { 518 err_t err = ERR_VAL; /* no matching interface */ 519 struct igmp_group *group; 520 struct netif *netif; 521 522 /* make sure it is multicast address */ 523 LWIP_ERROR("igmp_joingroup: attempt to join non-multicast address", ip_addr_ismulticast(groupaddr), return ERR_VAL;); 524 LWIP_ERROR("igmp_joingroup: attempt to join allsystems address", (!ip_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;); 525 526 /* loop through netif's */ 527 netif = netif_list; 528 while (netif != NULL) { 529 /* Should we join this interface ? */ 530 if ((netif->flags & NETIF_FLAG_IGMP) && ((ip_addr_isany(ifaddr) || ip_addr_cmp(&(netif->ip_addr), ifaddr)))) { 531 /* find group or create a new one if not found */ 532 group = igmp_lookup_group(netif, groupaddr); 533 534 if (group != NULL) { 535 /* This should create a new group, check the state to make sure */ 536 if (group->group_state != IGMP_GROUP_NON_MEMBER) { 537 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: join to group not in state IGMP_GROUP_NON_MEMBER\n")); 538 } else { 539 /* OK - it was new group */ 540 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: join to new group: ")); 541 ip_addr_debug_print(IGMP_DEBUG, groupaddr); 542 LWIP_DEBUGF(IGMP_DEBUG, ("\n")); 543 544 /* If first use of the group, allow the group at the MAC level */ 545 if ((group->use==0) && (netif->igmp_mac_filter != NULL)) { 546 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: igmp_mac_filter(ADD ")); 547 ip_addr_debug_print(IGMP_DEBUG, groupaddr); 548 LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif)); 549 netif->igmp_mac_filter(netif, groupaddr, IGMP_ADD_MAC_FILTER); 550 } 551 552 IGMP_STATS_INC(igmp.tx_join); 553 igmp_send(group, IGMP_V2_MEMB_REPORT); 554 555 igmp_start_timer(group, IGMP_JOIN_DELAYING_MEMBER_TMR); 556 557 /* Need to work out where this timer comes from */ 558 group->group_state = IGMP_GROUP_DELAYING_MEMBER; 559 } 560 /* Increment group use */ 561 group->use++; 562 /* Join on this interface */ 563 err = ERR_OK; 564 } else { 565 /* Return an error even if some network interfaces are joined */ 566 /** @todo undo any other netif already joined */ 567 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: Not enought memory to join to group\n")); 568 return ERR_MEM; 569 } 570 } 571 /* proceed to next network interface */ 572 netif = netif->next; 573 } 574 575 return err; 576 } 577 578 /** 579 * Leave a group on one network interface. 580 * 581 * @param ifaddr ip address of the network interface which should leave a group 582 * @param groupaddr the ip address of the group which to leave 583 * @return ERR_OK if group was left on the netif(s), an err_t otherwise 584 */ 585 err_t 586 igmp_leavegroup(ip_addr_t *ifaddr, ip_addr_t *groupaddr) 587 { 588 err_t err = ERR_VAL; /* no matching interface */ 589 struct igmp_group *group; 590 struct netif *netif; 591 592 /* make sure it is multicast address */ 593 LWIP_ERROR("igmp_leavegroup: attempt to leave non-multicast address", ip_addr_ismulticast(groupaddr), return ERR_VAL;); 594 LWIP_ERROR("igmp_leavegroup: attempt to leave allsystems address", (!ip_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;); 595 596 /* loop through netif's */ 597 netif = netif_list; 598 while (netif != NULL) { 599 /* Should we leave this interface ? */ 600 if ((netif->flags & NETIF_FLAG_IGMP) && ((ip_addr_isany(ifaddr) || ip_addr_cmp(&(netif->ip_addr), ifaddr)))) { 601 /* find group */ 602 group = igmp_lookfor_group(netif, groupaddr); 603 604 if (group != NULL) { 605 /* Only send a leave if the flag is set according to the state diagram */ 606 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: Leaving group: ")); 607 ip_addr_debug_print(IGMP_DEBUG, groupaddr); 608 LWIP_DEBUGF(IGMP_DEBUG, ("\n")); 609 610 /* If there is no other use of the group */ 611 if (group->use <= 1) { 612 /* If we are the last reporter for this group */ 613 if (group->last_reporter_flag) { 614 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: sending leaving group\n")); 615 IGMP_STATS_INC(igmp.tx_leave); 616 igmp_send(group, IGMP_LEAVE_GROUP); 617 } 618 619 /* Disable the group at the MAC level */ 620 if (netif->igmp_mac_filter != NULL) { 621 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: igmp_mac_filter(DEL ")); 622 ip_addr_debug_print(IGMP_DEBUG, groupaddr); 623 LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif)); 624 netif->igmp_mac_filter(netif, groupaddr, IGMP_DEL_MAC_FILTER); 625 } 626 627 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: remove group: ")); 628 ip_addr_debug_print(IGMP_DEBUG, groupaddr); 629 LWIP_DEBUGF(IGMP_DEBUG, ("\n")); 630 631 /* Free the group */ 632 igmp_remove_group(group); 633 } else { 634 /* Decrement group use */ 635 group->use--; 636 } 637 /* Leave on this interface */ 638 err = ERR_OK; 639 } else { 640 /* It's not a fatal error on "leavegroup" */ 641 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: not member of group\n")); 642 } 643 } 644 /* proceed to next network interface */ 645 netif = netif->next; 646 } 647 648 return err; 649 } 650 651 /** 652 * The igmp timer function (both for NO_SYS=1 and =0) 653 * Should be called every IGMP_TMR_INTERVAL milliseconds (100 ms is default). 654 */ 655 void 656 igmp_tmr(void) 657 { 658 struct igmp_group *group = igmp_group_list; 659 660 while (group != NULL) { 661 if (group->timer > 0) { 662 group->timer--; 663 if (group->timer == 0) { 664 igmp_timeout(group); 665 } 666 } 667 group = group->next; 668 } 669 } 670 671 /** 672 * Called if a timeout for one group is reached. 673 * Sends a report for this group. 674 * 675 * @param group an igmp_group for which a timeout is reached 676 */ 677 static void 678 igmp_timeout(struct igmp_group *group) 679 { 680 /* If the state is IGMP_GROUP_DELAYING_MEMBER then we send a report for this group */ 681 if (group->group_state == IGMP_GROUP_DELAYING_MEMBER) { 682 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_timeout: report membership for group with address ")); 683 ip_addr_debug_print(IGMP_DEBUG, &(group->group_address)); 684 LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", group->netif)); 685 686 IGMP_STATS_INC(igmp.tx_report); 687 igmp_send(group, IGMP_V2_MEMB_REPORT); 688 } 689 } 690 691 /** 692 * Start a timer for an igmp group 693 * 694 * @param group the igmp_group for which to start a timer 695 * @param max_time the time in multiples of IGMP_TMR_INTERVAL (decrease with 696 * every call to igmp_tmr()) 697 */ 698 static void 699 igmp_start_timer(struct igmp_group *group, u8_t max_time) 700 { 701 /* ensure the input value is > 0 */ 702 if (max_time == 0) { 703 max_time = 1; 704 } 705 /* ensure the random value is > 0 */ 706 group->timer = (LWIP_RAND() % (max_time - 1)) + 1; 707 } 708 709 /** 710 * Stop a timer for an igmp_group 711 * 712 * @param group the igmp_group for which to stop the timer 713 */ 714 static void 715 igmp_stop_timer(struct igmp_group *group) 716 { 717 group->timer = 0; 718 } 719 720 /** 721 * Delaying membership report for a group if necessary 722 * 723 * @param group the igmp_group for which "delaying" membership report 724 * @param maxresp query delay 725 */ 726 static void 727 igmp_delaying_member(struct igmp_group *group, u8_t maxresp) 728 { 729 if ((group->group_state == IGMP_GROUP_IDLE_MEMBER) || 730 ((group->group_state == IGMP_GROUP_DELAYING_MEMBER) && 731 ((group->timer == 0) || (maxresp < group->timer)))) { 732 igmp_start_timer(group, maxresp); 733 group->group_state = IGMP_GROUP_DELAYING_MEMBER; 734 } 735 } 736 737 738 /** 739 * Sends an IP packet on a network interface. This function constructs the IP header 740 * and calculates the IP header checksum. If the source IP address is NULL, 741 * the IP address of the outgoing network interface is filled in as source address. 742 * 743 * @param p the packet to send (p->payload points to the data, e.g. next 744 protocol header; if dest == IP_HDRINCL, p already includes an IP 745 header and p->payload points to that IP header) 746 * @param src the source IP address to send from (if src == IP_ADDR_ANY, the 747 * IP address of the netif used to send is used as source address) 748 * @param dest the destination IP address to send the packet to 749 * @param ttl the TTL value to be set in the IP header 750 * @param proto the PROTOCOL to be set in the IP header 751 * @param netif the netif on which to send this packet 752 * @return ERR_OK if the packet was sent OK 753 * ERR_BUF if p doesn't have enough space for IP/LINK headers 754 * returns errors returned by netif->output 755 */ 756 static err_t 757 igmp_ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, struct netif *netif) 758 { 759 /* This is the "router alert" option */ 760 u16_t ra[2]; 761 ra[0] = PP_HTONS(ROUTER_ALERT); 762 ra[1] = 0x0000; /* Router shall examine packet */ 763 IGMP_STATS_INC(igmp.xmit); 764 return ip_output_if_opt(p, src, dest, IGMP_TTL, 0, IP_PROTO_IGMP, netif, ra, ROUTER_ALERTLEN); 765 } 766 767 /** 768 * Send an igmp packet to a specific group. 769 * 770 * @param group the group to which to send the packet 771 * @param type the type of igmp packet to send 772 */ 773 static void 774 igmp_send(struct igmp_group *group, u8_t type) 775 { 776 struct pbuf* p = NULL; 777 struct igmp_msg* igmp = NULL; 778 ip_addr_t src = *IP_ADDR_ANY; 779 ip_addr_t* dest = NULL; 780 781 /* IP header + "router alert" option + IGMP header */ 782 p = pbuf_alloc(PBUF_TRANSPORT, IGMP_MINLEN, PBUF_RAM); 783 784 if (p) { 785 igmp = (struct igmp_msg *)p->payload; 786 LWIP_ASSERT("igmp_send: check that first pbuf can hold struct igmp_msg", 787 (p->len >= sizeof(struct igmp_msg))); 788 ip_addr_copy(src, group->netif->ip_addr); 789 790 if (type == IGMP_V2_MEMB_REPORT) { 791 dest = &(group->group_address); 792 ip_addr_copy(igmp->igmp_group_address, group->group_address); 793 group->last_reporter_flag = 1; /* Remember we were the last to report */ 794 } else { 795 if (type == IGMP_LEAVE_GROUP) { 796 dest = &allrouters; 797 ip_addr_copy(igmp->igmp_group_address, group->group_address); 798 } 799 } 800 801 if ((type == IGMP_V2_MEMB_REPORT) || (type == IGMP_LEAVE_GROUP)) { 802 igmp->igmp_msgtype = type; 803 igmp->igmp_maxresp = 0; 804 igmp->igmp_checksum = 0; 805 igmp->igmp_checksum = inet_chksum(igmp, IGMP_MINLEN); 806 807 igmp_ip_output_if(p, &src, dest, group->netif); 808 } 809 810 pbuf_free(p); 811 } else { 812 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_send: not enough memory for igmp_send\n")); 813 IGMP_STATS_INC(igmp.memerr); 814 } 815 } 816 817 #endif /* LWIP_IGMP */ 818