Home | History | Annotate | Download | only in ipv4
      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