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