3  * IGMP - Internet Group Management Protocol
 
   8  * Copyright (c) 2002 CITEL Technologies Ltd.
 
  11  * Redistribution and use in source and binary forms, with or without 
 
  12  * modification, are permitted provided that the following conditions 
 
  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. 
 
  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 
 
  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
 
  41 /*-------------------------------------------------------------
 
  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
 
  47 a debug print and statistic have been implemented to
 
  49 -------------------------------------------------------------
 
  50 -------------------------------------------------------------
 
  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
 
  55 a debug print and statistic have been implemented to
 
  57 -------------------------------------------------------------
 
  58 -------------------------------------------------------------
 
  60 The router alert rfc 2113 is implemented in outgoing packets
 
  61 but not checked rigorously incoming
 
  62 -------------------------------------------------------------
 
  64 ------------------------------------------------------------*/
 
  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  *----------------------------------------------------------------------------*/
 
  76 /*-----------------------------------------------------------------------------
 
  78  *----------------------------------------------------------------------------*/
 
  82 #if LWIP_IGMP /* don't build if not configured for use in lwipopts.h */
 
  84 #include "lwip/igmp.h"
 
  85 #include "lwip/debug.h"
 
  89 #include "lwip/inet.h"
 
  90 #include "lwip/inet_chksum.h"
 
  91 #include "lwip/netif.h"
 
  92 #include "lwip/icmp.h"
 
  95 #include "lwip/stats.h"
 
  99 /*-----------------------------------------------------------------------------
 
 101  *----------------------------------------------------------------------------*/
 
 103 static struct igmp_group* igmp_group_list;
 
 104 static struct ip_addr     allsystems;
 
 105 static struct ip_addr     allrouters;
 
 108  * Initialize the IGMP module
 
 113   LWIP_DEBUGF(IGMP_DEBUG, ("igmp_init: initializing\n"));
 
 115   IP4_ADDR(&allsystems, 224, 0, 0, 1);
 
 116   IP4_ADDR(&allrouters, 224, 0, 0, 2);
 
 121  * Dump global IGMP groups list
 
 124 igmp_dump_group_list()
 
 126   struct igmp_group *group = igmp_group_list;
 
 128   while (group != NULL) {
 
 129     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_dump_group_list: [%"U32_F"] ", (u32_t)(group->group_state)));
 
 130     ip_addr_debug_print(IGMP_DEBUG, &group->group_address);
 
 131     LWIP_DEBUGF(IGMP_DEBUG, (" on if %x\n", (int) group->interface));
 
 134   LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
 
 137 #define igmp_dump_group_list()
 
 138 #endif /* LWIP_DEBUG */
 
 141  * Start IGMP processing on interface
 
 143  * @param netif network interface on which start IGMP processing
 
 146 igmp_start(struct netif *netif)
 
 148   struct igmp_group* group;
 
 150   LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: starting IGMP processing on if %x\n", (int) netif));
 
 152   group = igmp_lookup_group(netif, &allsystems);
 
 155     group->group_state = IGMP_GROUP_IDLE_MEMBER;
 
 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 %x\n", (int) netif));
 
 163       netif->igmp_mac_filter( netif, &allsystems, IGMP_ADD_MAC_FILTER);
 
 173  * Stop IGMP processing on interface
 
 175  * @param netif network interface on which stop IGMP processing
 
 178 igmp_stop(struct netif *netif)
 
 180   struct igmp_group *group = igmp_group_list;
 
 181   struct igmp_group *prev  = NULL;
 
 182   struct igmp_group *next;
 
 184   /* look for groups joined on this interface further down the list */
 
 185   while (group != NULL) {
 
 187     /* is it a group joined on this interface? */
 
 188     if (group->interface == netif) {
 
 189       /* is it the first group of the list? */
 
 190       if (group == igmp_group_list) {
 
 191         igmp_group_list = next;
 
 193       /* is there a "previous" group defined? */
 
 197       /* disable the group at the MAC level */
 
 198       if (netif->igmp_mac_filter != NULL) {
 
 199         LWIP_DEBUGF(IGMP_DEBUG, ("igmp_stop: igmp_mac_filter(DEL "));
 
 200         ip_addr_debug_print(IGMP_DEBUG, &group->group_address);
 
 201         LWIP_DEBUGF(IGMP_DEBUG, (") on if %x\n", (int) netif));
 
 202         netif->igmp_mac_filter(netif, &(group->group_address), IGMP_DEL_MAC_FILTER);
 
 205       memp_free(MEMP_IGMP_GROUP, group);
 
 207       /* change the "previous" */
 
 217  * Report IGMP memberships for this interface
 
 219  * @param netif network interface on which report IGMP memberships
 
 222 igmp_report_groups( struct netif *netif)
 
 224   struct igmp_group *group = igmp_group_list;
 
 226   LWIP_DEBUGF(IGMP_DEBUG, ("igmp_report_groups: sending IGMP reports on if %x\n", (int) netif));
 
 228   while (group != NULL) {
 
 229     if (group->interface == netif) {
 
 230       igmp_delaying_member( group, IGMP_JOIN_DELAYING_MEMBER_TMR);
 
 237  * Search for a group in the global igmp_group_list
 
 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.
 
 245 igmp_lookfor_group(struct netif *ifp, struct ip_addr *addr)
 
 247   struct igmp_group *group = igmp_group_list;
 
 249   while (group != NULL) {
 
 250     if ((group->interface == ifp) && (ip_addr_cmp(&(group->group_address), addr))) {
 
 256   /* to be clearer, we return NULL here instead of
 
 257    * 'group' (which is also NULL at this point).
 
 263  * Search for a specific igmp group and create a new one if not found-
 
 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.
 
 271 igmp_lookup_group(struct netif *ifp, struct ip_addr *addr)
 
 273   struct igmp_group *group = igmp_group_list;
 
 275   /* Search if the group already exists */
 
 276   group = igmp_lookfor_group(ifp, addr);
 
 278     /* Group already exists. */
 
 282   /* Group doesn't exist yet, create a new one */
 
 283   group = memp_malloc(MEMP_IGMP_GROUP);
 
 285     group->interface          = ifp;
 
 286     ip_addr_set(&(group->group_address), addr);
 
 287     group->timer              = 0; /* Not running */
 
 288     group->group_state        = IGMP_GROUP_NON_MEMBER;
 
 289     group->last_reporter_flag = 0;
 
 291     group->next               = igmp_group_list;
 
 293     igmp_group_list = group;
 
 296   LWIP_DEBUGF(IGMP_DEBUG, ("igmp_lookup_group: %sallocated a new group with address ", (group?"":"impossible to ")));
 
 297   ip_addr_debug_print(IGMP_DEBUG, addr);
 
 298   LWIP_DEBUGF(IGMP_DEBUG, (" on if %x\n", (int) ifp));
 
 304  * Remove a group in the global igmp_group_list
 
 306  * @param group the group to remove from the global igmp_group_list
 
 307  * @return ERR_OK if group was removed from the list, an err_t otherwise
 
 310 igmp_remove_group(struct igmp_group *group)
 
 314   /* Is it the first group? */
 
 315   if (igmp_group_list == group) {
 
 316     igmp_group_list = group->next;
 
 318     /* look for group further down the list */
 
 319     struct igmp_group *tmpGroup;
 
 320     for (tmpGroup = igmp_group_list; tmpGroup != NULL; tmpGroup = tmpGroup->next) {
 
 321       if (tmpGroup->next == group) {
 
 322         tmpGroup->next = group->next;
 
 326     /* Group not found in the global igmp_group_list */
 
 327     if (tmpGroup == NULL)
 
 331   memp_free(MEMP_IGMP_GROUP, group);
 
 337  * Called from ip_input() if a new IGMP packet is received.
 
 339  * @param p received igmp packet, p->payload pointing to the ip header
 
 340  * @param inp network interface on which the packet was received
 
 341  * @param dest destination ip address of the igmp packet
 
 344 igmp_input(struct pbuf *p, struct netif *inp, struct ip_addr *dest)
 
 346   struct ip_hdr *    iphdr;
 
 347   struct igmp_msg*   igmp;
 
 348   struct igmp_group* group;
 
 349   struct igmp_group* groupref;
 
 351   /* Note that the length CAN be greater than 8 but only 8 are used - All are included in the checksum */    
 
 353   if (pbuf_header(p, -(s16_t)(IPH_HL(iphdr) * 4)) || (p->len < IGMP_MINLEN)) {
 
 355     IGMP_STATS_INC(igmp.lenerr);
 
 356     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: length error\n"));
 
 360   LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: message from "));
 
 361   ip_addr_debug_print(IGMP_DEBUG, &(iphdr->src));
 
 362   LWIP_DEBUGF(IGMP_DEBUG, (" to address "));
 
 363   ip_addr_debug_print(IGMP_DEBUG, &(iphdr->dest));
 
 364   LWIP_DEBUGF(IGMP_DEBUG, (" on if %x\n", (int) inp));
 
 366   /* Now calculate and check the checksum */
 
 367   igmp = (struct igmp_msg *)p->payload;
 
 368   if (inet_chksum(igmp, p->len)) {
 
 370     IGMP_STATS_INC(igmp.chkerr);
 
 371     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: checksum error\n"));
 
 375   /* Packet is ok so find an existing group */
 
 376   group = igmp_lookfor_group(inp, dest); /* use the incoming IP address! */
 
 378   /* If group can be found or create... */
 
 381     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP frame not for us\n"));
 
 385   /* NOW ACT ON THE INCOMING MESSAGE TYPE... */
 
 386   switch (igmp->igmp_msgtype) {
 
 387    case IGMP_MEMB_QUERY: {
 
 388      /* IGMP_MEMB_QUERY to the "all systems" address ? */
 
 389      if ((ip_addr_cmp(dest, &allsystems)) && (igmp->igmp_group_address.addr == 0)) {
 
 390        /* THIS IS THE GENERAL QUERY */
 
 391        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)));
 
 393        if (igmp->igmp_maxresp == 0) {
 
 394          IGMP_STATS_INC(igmp.v1_rxed);
 
 395          LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: got an all hosts query with time== 0 - this is V1 and not implemented - treat as v2\n"));
 
 396          igmp->igmp_maxresp = IGMP_V1_DELAYING_MEMBER_TMR;
 
 399        IGMP_STATS_INC(igmp.group_query_rxed);
 
 400        groupref = igmp_group_list;
 
 402          /* Do not send messages on the all systems group address! */
 
 403          if ((groupref->interface == inp) && (!(ip_addr_cmp(&(groupref->group_address), &allsystems)))) {
 
 404            igmp_delaying_member( groupref, igmp->igmp_maxresp);
 
 406          groupref = groupref->next;
 
 409        /* IGMP_MEMB_QUERY to a specific group ? */
 
 410        if (group->group_address.addr != 0) {
 
 411          LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_MEMB_QUERY to a specific group "));
 
 412          ip_addr_debug_print(IGMP_DEBUG, &group->group_address);
 
 413          if (ip_addr_cmp (dest, &allsystems)) {
 
 414            LWIP_DEBUGF(IGMP_DEBUG, (" using \"ALL SYSTEMS\" address (224.0.0.1) [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp)));
 
 415            /* we first need to re-lookfor the group since we used dest last time */
 
 416            group = igmp_lookfor_group(inp, &igmp->igmp_group_address);
 
 418            LWIP_DEBUGF(IGMP_DEBUG, (" with the group address as destination [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp)));
 
 422            IGMP_STATS_INC(igmp.unicast_query);
 
 423            igmp_delaying_member( group, igmp->igmp_maxresp);
 
 429    case IGMP_V2_MEMB_REPORT: {
 
 430      LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_V2_MEMB_REPORT\n"));
 
 432      IGMP_STATS_INC(igmp.report_rxed);
 
 433      if (group->group_state == IGMP_GROUP_DELAYING_MEMBER) {
 
 434        /* This is on a specific group we have already looked up */
 
 435        group->timer = 0; /* stopped */
 
 436        group->group_state = IGMP_GROUP_IDLE_MEMBER;
 
 437        group->last_reporter_flag = 0;
 
 442      LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: unexpected msg %x in state %x on group %x on if %x\n", (int) igmp->igmp_msgtype, (int) group->group_state, (int) &group, (int) group->interface));
 
 452  * Join a group on one network interface.
 
 454  * @param ifaddr ip address of the network interface which should join a new group
 
 455  * @param groupaddr the ip address of the group which to join
 
 456  * @return ERR_OK if group was joined on the netif(s), an err_t otherwise
 
 459 igmp_joingroup(struct ip_addr *ifaddr, struct ip_addr *groupaddr)
 
 461   err_t              err = ERR_VAL; /* no matching interface */
 
 462   struct igmp_group *group;
 
 465   /* make sure it is multicast address */
 
 466   LWIP_ERROR("igmp_joingroup: attempt to join non-multicast address", ip_addr_ismulticast(groupaddr), return ERR_VAL;);
 
 467   LWIP_ERROR("igmp_joingroup: attempt to join allsystems address", (!ip_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;);
 
 469   /* loop through netif's */
 
 471   while (netif != NULL) {
 
 472     /* Should we join this interface ? */
 
 473     if ((netif->flags & NETIF_FLAG_IGMP) && ((ip_addr_isany(ifaddr) || ip_addr_cmp(&(netif->ip_addr), ifaddr)))) {
 
 474       /* find group or create a new one if not found */
 
 475       group = igmp_lookup_group(netif, groupaddr);
 
 478         /* This should create a new group, check the state to make sure */
 
 479         if (group->group_state != IGMP_GROUP_NON_MEMBER) {
 
 480           LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: join to group not in state IGMP_GROUP_NON_MEMBER\n"));
 
 482           /* OK - it was new group */
 
 483           LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: join to new group: "));
 
 484           ip_addr_debug_print(IGMP_DEBUG, groupaddr);
 
 485           LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
 
 487           /* If first use of the group, allow the group at the MAC level */
 
 488           if ((group->use==0) && (netif->igmp_mac_filter != NULL)) {
 
 489             LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: igmp_mac_filter(ADD "));
 
 490             ip_addr_debug_print(IGMP_DEBUG, groupaddr);
 
 491             LWIP_DEBUGF(IGMP_DEBUG, (") on if %x\n", (int) netif));
 
 492             netif->igmp_mac_filter(netif, groupaddr, IGMP_ADD_MAC_FILTER);
 
 495           IGMP_STATS_INC(igmp.join_sent);
 
 496           igmp_send(group, IGMP_V2_MEMB_REPORT);
 
 498           igmp_start_timer(group, IGMP_JOIN_DELAYING_MEMBER_TMR);
 
 500           /* Need to work out where this timer comes from */
 
 501           group->group_state = IGMP_GROUP_DELAYING_MEMBER;
 
 503         /* Increment group use */
 
 505         /* Join on this interface */
 
 508         /* Return an error even if some network interfaces are joined */
 
 509         /** @todo undo any other netif already joined */
 
 510         LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: Not enought memory to join to group\n"));
 
 514     /* proceed to next network interface */
 
 522  * Leave a group on one network interface.
 
 524  * @param ifaddr ip address of the network interface which should leave a group
 
 525  * @param groupaddr the ip address of the group which to leave
 
 526  * @return ERR_OK if group was left on the netif(s), an err_t otherwise
 
 529 igmp_leavegroup(struct ip_addr *ifaddr, struct ip_addr *groupaddr)
 
 531   err_t              err = ERR_VAL; /* no matching interface */
 
 532   struct igmp_group *group;
 
 535   /* make sure it is multicast address */
 
 536   LWIP_ERROR("igmp_leavegroup: attempt to leave non-multicast address", ip_addr_ismulticast(groupaddr), return ERR_VAL;);
 
 537   LWIP_ERROR("igmp_leavegroup: attempt to leave allsystems address", (!ip_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;);
 
 539   /* loop through netif's */
 
 541   while (netif != NULL) {
 
 542     /* Should we leave this interface ? */
 
 543     if ((netif->flags & NETIF_FLAG_IGMP) && ((ip_addr_isany(ifaddr) || ip_addr_cmp(&(netif->ip_addr), ifaddr)))) {
 
 545       group = igmp_lookfor_group(netif, groupaddr);
 
 548         /* Only send a leave if the flag is set according to the state diagram */
 
 549         LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: Leaving group: "));
 
 550         ip_addr_debug_print(IGMP_DEBUG, groupaddr);
 
 551         LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
 
 553         /* If there is no other use of the group */
 
 554         if (group->use <= 1) {
 
 555           /* If we are the last reporter for this group */
 
 556           if (group->last_reporter_flag) {
 
 557             LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: sending leaving group\n"));
 
 558             IGMP_STATS_INC(igmp.leave_sent);
 
 559             igmp_send(group, IGMP_LEAVE_GROUP);
 
 562           /* Disable the group at the MAC level */
 
 563           if (netif->igmp_mac_filter != NULL) {
 
 564             LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: igmp_mac_filter(DEL "));
 
 565             ip_addr_debug_print(IGMP_DEBUG, groupaddr);
 
 566             LWIP_DEBUGF(IGMP_DEBUG, (") on if %x\n", (int) netif));
 
 567             netif->igmp_mac_filter(netif, groupaddr, IGMP_DEL_MAC_FILTER);
 
 570           LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: remove group: "));
 
 571           ip_addr_debug_print(IGMP_DEBUG, groupaddr);
 
 572           LWIP_DEBUGF(IGMP_DEBUG, ("\n"));          
 
 575           igmp_remove_group(group);
 
 577           /* Decrement group use */
 
 580         /* Leave on this interface */
 
 583         /* It's not a fatal error on "leavegroup" */
 
 584         LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: not member of group\n"));
 
 587     /* proceed to next network interface */
 
 595  * The igmp timer function (both for NO_SYS=1 and =0)
 
 596  * Should be called every IGMP_TMR_INTERVAL milliseconds (100 ms is default).
 
 601   struct igmp_group *group = igmp_group_list;
 
 603   while (group != NULL) {
 
 604     if (group->timer != 0) {
 
 606       if (group->timer == 0) {
 
 615  * Called if a timeout for one group is reached.
 
 616  * Sends a report for this group.
 
 618  * @param group an igmp_group for which a timeout is reached
 
 621 igmp_timeout(struct igmp_group *group)
 
 623   /* If the state is IGMP_GROUP_DELAYING_MEMBER then we send a report for this group */
 
 624   if (group->group_state == IGMP_GROUP_DELAYING_MEMBER) {
 
 625     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_timeout: report membership for group with address "));
 
 626     ip_addr_debug_print(IGMP_DEBUG, &(group->group_address));
 
 627     LWIP_DEBUGF(IGMP_DEBUG, (" on if %x\n", (int) group->interface));
 
 629     igmp_send(group, IGMP_V2_MEMB_REPORT);
 
 634  * Start a timer for an igmp group
 
 636  * @param group the igmp_group for which to start a timer
 
 637  * @param max_time the time in multiples of IGMP_TMR_INTERVAL (decrease with
 
 638  *        every call to igmp_tmr())
 
 641 igmp_start_timer(struct igmp_group *group, u8_t max_time)
 
 644    * @todo Important !! this should be random 0 -> max_time. Find out how to do this
 
 646   group->timer = max_time;
 
 650  * Stop a timer for an igmp_group
 
 652  * @param group the igmp_group for which to stop the timer
 
 655 igmp_stop_timer(struct igmp_group *group)
 
 661  * Delaying membership report for a group if necessary
 
 663  * @param group the igmp_group for which "delaying" membership report
 
 664  * @param maxresp query delay
 
 667 igmp_delaying_member( struct igmp_group *group, u8_t maxresp)
 
 669   if ((group->group_state == IGMP_GROUP_IDLE_MEMBER) || ((group->group_state == IGMP_GROUP_DELAYING_MEMBER) && (maxresp > group->timer))) {
 
 670     igmp_start_timer(group, (maxresp)/2);
 
 671     group->group_state = IGMP_GROUP_DELAYING_MEMBER;
 
 677  * Sends an IP packet on a network interface. This function constructs the IP header
 
 678  * and calculates the IP header checksum. If the source IP address is NULL,
 
 679  * the IP address of the outgoing network interface is filled in as source address.
 
 681  * @param p the packet to send (p->payload points to the data, e.g. next
 
 682             protocol header; if dest == IP_HDRINCL, p already includes an IP
 
 683             header and p->payload points to that IP header)
 
 684  * @param src the source IP address to send from (if src == IP_ADDR_ANY, the
 
 685  *         IP  address of the netif used to send is used as source address)
 
 686  * @param dest the destination IP address to send the packet to
 
 687  * @param ttl the TTL value to be set in the IP header
 
 688  * @param proto the PROTOCOL to be set in the IP header
 
 689  * @param netif the netif on which to send this packet
 
 690  * @return ERR_OK if the packet was sent OK
 
 691  *         ERR_BUF if p doesn't have enough space for IP/LINK headers
 
 692  *         returns errors returned by netif->output
 
 695 igmp_ip_output_if(struct pbuf *p, struct ip_addr *src, struct ip_addr *dest,
 
 696                   u8_t ttl, u8_t proto, struct netif *netif)
 
 698   static u16_t    ip_id = 0;
 
 699   struct ip_hdr * iphdr = NULL;
 
 702   /* First write in the "router alert" */
 
 703   if (pbuf_header(p, ROUTER_ALERTLEN)) {
 
 704     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_ip_output_if: not enough room for IP header in pbuf\n"));
 
 708   /* This is the "router alert" option */
 
 710   ra[0] = htons (ROUTER_ALERT);
 
 711   ra[1] = 0x0000; /* Router shall examine packet */
 
 713   /* now the normal ip header */
 
 714   if (pbuf_header(p, IP_HLEN)) {
 
 715     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_ip_output_if: not enough room for IP header in pbuf\n"));
 
 721   /* Should the IP header be generated or is it already included in p? */
 
 722   if (dest != IP_HDRINCL) {
 
 723     /** @todo should be shared with ip.c - ip_output_if */
 
 724     IPH_TTL_SET(iphdr, ttl);
 
 725     IPH_PROTO_SET(iphdr, proto);
 
 727     ip_addr_set(&(iphdr->dest), dest);
 
 729     IPH_VHLTOS_SET(iphdr, 4, ((IP_HLEN + ROUTER_ALERTLEN) / 4), 0/*tos*/);
 
 730     IPH_LEN_SET(iphdr, htons(p->tot_len));
 
 731     IPH_OFFSET_SET(iphdr, 0);
 
 732     IPH_ID_SET(iphdr, htons(ip_id));
 
 735     if (ip_addr_isany(src)) {
 
 736       ip_addr_set(&(iphdr->src), &(netif->ip_addr));
 
 738       ip_addr_set(&(iphdr->src), src);
 
 741     IPH_CHKSUM_SET(iphdr, 0);
 
 743     IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, (IP_HLEN + ROUTER_ALERTLEN)));
 
 746     dest = &(iphdr->dest);
 
 753   LWIP_DEBUGF(IGMP_DEBUG, ("igmp_ip_output_if: sending to if %x\n", (int) netif));
 
 755   return netif->output(netif, p, dest);
 
 759  * Send an igmp packet to a specific group.
 
 761  * @param group the group to which to send the packet
 
 762  * @param type the type of igmp packet to send
 
 765 igmp_send(struct igmp_group *group, u8_t type)
 
 767   struct pbuf*     p    = NULL;
 
 768   struct igmp_msg* igmp = NULL;
 
 769   struct ip_addr   src  = {0};
 
 770   struct ip_addr*  dest = NULL;
 
 772   /* IP header + "router alert" option + IGMP header */
 
 773   p = pbuf_alloc(PBUF_TRANSPORT, IGMP_MINLEN, PBUF_RAM);
 
 777     LWIP_ASSERT("igmp_send: check that first pbuf can hold struct igmp_msg",
 
 778                (p->len >= sizeof(struct igmp_msg)));
 
 779     ip_addr_set(&src, &((group->interface)->ip_addr));
 
 781     if (type == IGMP_V2_MEMB_REPORT) {
 
 782       dest = &(group->group_address);
 
 783       IGMP_STATS_INC(igmp.report_sent);
 
 784       ip_addr_set(&(igmp->igmp_group_address), &(group->group_address));
 
 785       group->last_reporter_flag = 1; /* Remember we were the last to report */
 
 787       if (type == IGMP_LEAVE_GROUP) {
 
 789         ip_addr_set(&(igmp->igmp_group_address), &(group->group_address));
 
 793     if ((type == IGMP_V2_MEMB_REPORT) || (type == IGMP_LEAVE_GROUP)) {
 
 794       igmp->igmp_msgtype  = type;
 
 795       igmp->igmp_maxresp  = 0;
 
 796       igmp->igmp_checksum = 0;
 
 797       igmp->igmp_checksum = inet_chksum( igmp, IGMP_MINLEN);
 
 799       igmp_ip_output_if( p, &src, dest, IGMP_TTL, IP_PROTO_IGMP, group->interface);
 
 804     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_send: not enough memory for igmp_send\n"));
 
 808 #endif /* LWIP_IGMP */