Back to home page

Quest Cross Reference

 
 

    


Warning, cross-references for /kernel/lwip/core/ipv4/igmp.c need to be fixed.

0001 /**
0002  * @file
0003  * IGMP - Internet Group Management Protocol
0004  *
0005  */
0006 
0007 /*
0008  * Copyright (c) 2002 CITEL Technologies Ltd.
0009  * All rights reserved.
0010  *
0011  * Redistribution and use in source and binary forms, with or without 
0012  * modification, are permitted provided that the following conditions 
0013  * are met: 
0014  * 1. Redistributions of source code must retain the above copyright 
0015  *    notice, this list of conditions and the following disclaimer. 
0016  * 2. Redistributions in binary form must reproduce the above copyright 
0017  *    notice, this list of conditions and the following disclaimer in the 
0018  *    documentation and/or other materials provided with the distribution. 
0019  * 3. Neither the name of CITEL Technologies Ltd nor the names of its contributors 
0020  *    may be used to endorse or promote products derived from this software 
0021  *    without specific prior written permission. 
0022  *
0023  * THIS SOFTWARE IS PROVIDED BY CITEL TECHNOLOGIES AND CONTRIBUTORS ``AS IS''
0024  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
0025  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
0026  * ARE DISCLAIMED.  IN NO EVENT SHALL CITEL TECHNOLOGIES OR CONTRIBUTORS BE LIABLE 
0027  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
0028  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 
0029  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
0030  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
0031  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 
0032  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
0033  * SUCH DAMAGE. 
0034  *
0035  * This file is a contribution to the lwIP TCP/IP stack.
0036  * The Swedish Institute of Computer Science and Adam Dunkels
0037  * are specifically granted permission to redistribute this
0038  * source code.
0039 */
0040 
0041 /*-------------------------------------------------------------
0042 Note 1)
0043 Although the rfc requires V1 AND V2 capability
0044 we will only support v2 since now V1 is very old (August 1989)
0045 V1 can be added if required
0046 
0047 a debug print and statistic have been implemented to
0048 show this up.
0049 -------------------------------------------------------------
0050 -------------------------------------------------------------
0051 Note 2)
0052 A query for a specific group address (as opposed to ALLHOSTS)
0053 has now been implemented as I am unsure if it is required
0054 
0055 a debug print and statistic have been implemented to
0056 show this up.
0057 -------------------------------------------------------------
0058 -------------------------------------------------------------
0059 Note 3)
0060 The router alert rfc 2113 is implemented in outgoing packets
0061 but not checked rigorously incoming
0062 -------------------------------------------------------------
0063 Steve Reynolds
0064 ------------------------------------------------------------*/
0065 
0066 /*-----------------------------------------------------------------------------
0067  * RFC 988  - Host extensions for IP multicasting                         - V0
0068  * RFC 1054 - Host extensions for IP multicasting                         -
0069  * RFC 1112 - Host extensions for IP multicasting                         - V1
0070  * RFC 2236 - Internet Group Management Protocol, Version 2               - V2  <- this code is based on this RFC (it's the "de facto" standard)
0071  * RFC 3376 - Internet Group Management Protocol, Version 3               - V3
0072  * RFC 4604 - Using Internet Group Management Protocol Version 3...       - V3+
0073  * RFC 2113 - IP Router Alert Option                                      - 
0074  *----------------------------------------------------------------------------*/
0075 
0076 /*-----------------------------------------------------------------------------
0077  * Includes
0078  *----------------------------------------------------------------------------*/
0079 
0080 #include "lwip/opt.h"
0081 
0082 #if LWIP_IGMP /* don't build if not configured for use in lwipopts.h */
0083 
0084 #include "lwip/igmp.h"
0085 #include "lwip/debug.h"
0086 #include "lwip/def.h"
0087 #include "lwip/mem.h"
0088 #include "lwip/ip.h"
0089 #include "lwip/inet.h"
0090 #include "lwip/inet_chksum.h"
0091 #include "lwip/netif.h"
0092 #include "lwip/icmp.h"
0093 #include "lwip/udp.h"
0094 #include "lwip/tcp.h"
0095 #include "lwip/stats.h"
0096 
0097 #include "string.h"
0098 
0099 /*-----------------------------------------------------------------------------
0100  * Globales
0101  *----------------------------------------------------------------------------*/
0102 
0103 static struct igmp_group* igmp_group_list;
0104 static struct ip_addr     allsystems;
0105 static struct ip_addr     allrouters;
0106 
0107 /**
0108  * Initialize the IGMP module
0109  */
0110 void
0111 igmp_init(void)
0112 {
0113   LWIP_DEBUGF(IGMP_DEBUG, ("igmp_init: initializing\n"));
0114 
0115   IP4_ADDR(&allsystems, 224, 0, 0, 1);
0116   IP4_ADDR(&allrouters, 224, 0, 0, 2);
0117 }
0118 
0119 #ifdef LWIP_DEBUG
0120 /**
0121  * Dump global IGMP groups list
0122  */
0123 void
0124 igmp_dump_group_list()
0125 { 
0126   struct igmp_group *group = igmp_group_list;
0127 
0128   while (group != NULL) {
0129     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_dump_group_list: [%"U32_F"] ", (u32_t)(group->group_state)));
0130     ip_addr_debug_print(IGMP_DEBUG, &group->group_address);
0131     LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", group->interface));
0132     group = group->next;
0133   }
0134   LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
0135 }
0136 #else
0137 #define igmp_dump_group_list()
0138 #endif /* LWIP_DEBUG */
0139 
0140 /**
0141  * Start IGMP processing on interface
0142  *
0143  * @param netif network interface on which start IGMP processing
0144  */
0145 err_t
0146 igmp_start(struct netif *netif)
0147 {
0148   struct igmp_group* group;
0149 
0150   LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: starting IGMP processing on if %p\n", netif));
0151 
0152   group = igmp_lookup_group(netif, &allsystems);
0153 
0154   if (group != NULL) {
0155     group->group_state = IGMP_GROUP_IDLE_MEMBER;
0156     group->use++;
0157 
0158     /* Allow the igmp messages at the MAC level */
0159     if (netif->igmp_mac_filter != NULL) {
0160       LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: igmp_mac_filter(ADD "));
0161       ip_addr_debug_print(IGMP_DEBUG, &allsystems);
0162       LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif));
0163       netif->igmp_mac_filter( netif, &allsystems, IGMP_ADD_MAC_FILTER);
0164     }
0165 
0166     return ERR_OK;
0167   }
0168 
0169   return ERR_MEM;
0170 }
0171 
0172 /**
0173  * Stop IGMP processing on interface
0174  *
0175  * @param netif network interface on which stop IGMP processing
0176  */
0177 err_t
0178 igmp_stop(struct netif *netif)
0179 {
0180   struct igmp_group *group = igmp_group_list;
0181   struct igmp_group *prev  = NULL;
0182   struct igmp_group *next;
0183 
0184   /* look for groups joined on this interface further down the list */
0185   while (group != NULL) {
0186     next = group->next;
0187     /* is it a group joined on this interface? */
0188     if (group->interface == netif) {
0189       /* is it the first group of the list? */
0190       if (group == igmp_group_list) {
0191         igmp_group_list = next;
0192       }
0193       /* is there a "previous" group defined? */
0194       if (prev != NULL) {
0195         prev->next = next;
0196       }
0197       /* disable the group at the MAC level */
0198       if (netif->igmp_mac_filter != NULL) {
0199         LWIP_DEBUGF(IGMP_DEBUG, ("igmp_stop: igmp_mac_filter(DEL "));
0200         ip_addr_debug_print(IGMP_DEBUG, &group->group_address);
0201         LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif));
0202         netif->igmp_mac_filter(netif, &(group->group_address), IGMP_DEL_MAC_FILTER);
0203       }
0204       /* free group */
0205       memp_free(MEMP_IGMP_GROUP, group);
0206     } else {
0207       /* change the "previous" */
0208       prev = group;
0209     }
0210     /* move to "next" */
0211     group = next;
0212   }
0213   return ERR_OK;
0214 }
0215 
0216 /**
0217  * Report IGMP memberships for this interface
0218  *
0219  * @param netif network interface on which report IGMP memberships
0220  */
0221 void
0222 igmp_report_groups( struct netif *netif)
0223 {
0224   struct igmp_group *group = igmp_group_list;
0225 
0226   LWIP_DEBUGF(IGMP_DEBUG, ("igmp_report_groups: sending IGMP reports on if %p\n", netif));
0227 
0228   while (group != NULL) {
0229     if (group->interface == netif) {
0230       igmp_delaying_member( group, IGMP_JOIN_DELAYING_MEMBER_TMR);
0231     }
0232     group = group->next;
0233   }
0234 }
0235 
0236 /**
0237  * Search for a group in the global igmp_group_list
0238  *
0239  * @param ifp the network interface for which to look
0240  * @param addr the group ip address to search for
0241  * @return a struct igmp_group* if the group has been found,
0242  *         NULL if the group wasn't found.
0243  */
0244 struct igmp_group *
0245 igmp_lookfor_group(struct netif *ifp, struct ip_addr *addr)
0246 {
0247   struct igmp_group *group = igmp_group_list;
0248 
0249   while (group != NULL) {
0250     if ((group->interface == ifp) && (ip_addr_cmp(&(group->group_address), addr))) {
0251       return group;
0252     }
0253     group = group->next;
0254   }
0255 
0256   /* to be clearer, we return NULL here instead of
0257    * 'group' (which is also NULL at this point).
0258    */
0259   return NULL;
0260 }
0261 
0262 /**
0263  * Search for a specific igmp group and create a new one if not found-
0264  *
0265  * @param ifp the network interface for which to look
0266  * @param addr the group ip address to search
0267  * @return a struct igmp_group*,
0268  *         NULL on memory error.
0269  */
0270 struct igmp_group *
0271 igmp_lookup_group(struct netif *ifp, struct ip_addr *addr)
0272 {
0273   struct igmp_group *group = igmp_group_list;
0274   
0275   /* Search if the group already exists */
0276   group = igmp_lookfor_group(ifp, addr);
0277   if (group != NULL) {
0278     /* Group already exists. */
0279     return group;
0280   }
0281 
0282   /* Group doesn't exist yet, create a new one */
0283   group = memp_malloc(MEMP_IGMP_GROUP);
0284   if (group != NULL) {
0285     group->interface          = ifp;
0286     ip_addr_set(&(group->group_address), addr);
0287     group->timer              = 0; /* Not running */
0288     group->group_state        = IGMP_GROUP_NON_MEMBER;
0289     group->last_reporter_flag = 0;
0290     group->use                = 0;
0291     group->next               = igmp_group_list;
0292     
0293     igmp_group_list = group;
0294   }
0295 
0296   LWIP_DEBUGF(IGMP_DEBUG, ("igmp_lookup_group: %sallocated a new group with address ", (group?"":"impossible to ")));
0297   ip_addr_debug_print(IGMP_DEBUG, addr);
0298   LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", ifp));
0299 
0300   return group;
0301 }
0302 
0303 /**
0304  * Remove a group in the global igmp_group_list
0305  *
0306  * @param group the group to remove from the global igmp_group_list
0307  * @return ERR_OK if group was removed from the list, an err_t otherwise
0308  */
0309 err_t
0310 igmp_remove_group(struct igmp_group *group)
0311 {
0312   err_t err = ERR_OK;
0313 
0314   /* Is it the first group? */
0315   if (igmp_group_list == group) {
0316     igmp_group_list = group->next;
0317   } else {
0318     /* look for group further down the list */
0319     struct igmp_group *tmpGroup;
0320     for (tmpGroup = igmp_group_list; tmpGroup != NULL; tmpGroup = tmpGroup->next) {
0321       if (tmpGroup->next == group) {
0322         tmpGroup->next = group->next;
0323         break;
0324       }
0325     }
0326     /* Group not found in the global igmp_group_list */
0327     if (tmpGroup == NULL)
0328       err = ERR_ARG;
0329   }
0330   /* free group */
0331   memp_free(MEMP_IGMP_GROUP, group);
0332 
0333   return err;
0334 }
0335 
0336 /**
0337  * Called from ip_input() if a new IGMP packet is received.
0338  *
0339  * @param p received igmp packet, p->payload pointing to the ip header
0340  * @param inp network interface on which the packet was received
0341  * @param dest destination ip address of the igmp packet
0342  */
0343 void
0344 igmp_input(struct pbuf *p, struct netif *inp, struct ip_addr *dest)
0345 {
0346   struct ip_hdr *    iphdr;
0347   struct igmp_msg*   igmp;
0348   struct igmp_group* group;
0349   struct igmp_group* groupref;
0350 
0351   /* Note that the length CAN be greater than 8 but only 8 are used - All are included in the checksum */    
0352   iphdr = p->payload;
0353   if (pbuf_header(p, -(s16_t)(IPH_HL(iphdr) * 4)) || (p->len < IGMP_MINLEN)) {
0354     pbuf_free(p);
0355     IGMP_STATS_INC(igmp.lenerr);
0356     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: length error\n"));
0357     return;
0358   }
0359 
0360   LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: message from "));
0361   ip_addr_debug_print(IGMP_DEBUG, &(iphdr->src));
0362   LWIP_DEBUGF(IGMP_DEBUG, (" to address "));
0363   ip_addr_debug_print(IGMP_DEBUG, &(iphdr->dest));
0364   LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", inp));
0365 
0366   /* Now calculate and check the checksum */
0367   igmp = (struct igmp_msg *)p->payload;
0368   if (inet_chksum(igmp, p->len)) {
0369     pbuf_free(p);
0370     IGMP_STATS_INC(igmp.chkerr);
0371     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: checksum error\n"));
0372     return;
0373   }
0374 
0375   /* Packet is ok so find an existing group */
0376   group = igmp_lookfor_group(inp, dest); /* use the incoming IP address! */
0377   
0378   /* If group can be found or create... */
0379   if (!group) {
0380     pbuf_free(p);
0381     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP frame not for us\n"));
0382     return;
0383   }
0384 
0385   /* NOW ACT ON THE INCOMING MESSAGE TYPE... */
0386   switch (igmp->igmp_msgtype) {
0387    case IGMP_MEMB_QUERY: {
0388      /* IGMP_MEMB_QUERY to the "all systems" address ? */
0389      if ((ip_addr_cmp(dest, &allsystems)) && (igmp->igmp_group_address.addr == 0)) {
0390        /* THIS IS THE GENERAL QUERY */
0391        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)));
0392 
0393        if (igmp->igmp_maxresp == 0) {
0394          IGMP_STATS_INC(igmp.v1_rxed);
0395          LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: got an all hosts query with time== 0 - this is V1 and not implemented - treat as v2\n"));
0396          igmp->igmp_maxresp = IGMP_V1_DELAYING_MEMBER_TMR;
0397        }
0398 
0399        IGMP_STATS_INC(igmp.group_query_rxed);
0400        groupref = igmp_group_list;
0401        while (groupref) {
0402          /* Do not send messages on the all systems group address! */
0403          if ((groupref->interface == inp) && (!(ip_addr_cmp(&(groupref->group_address), &allsystems)))) {
0404            igmp_delaying_member( groupref, igmp->igmp_maxresp);
0405          }
0406          groupref = groupref->next;
0407        }
0408      } else {
0409        /* IGMP_MEMB_QUERY to a specific group ? */
0410        if (group->group_address.addr != 0) {
0411          LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_MEMB_QUERY to a specific group "));
0412          ip_addr_debug_print(IGMP_DEBUG, &group->group_address);
0413          if (ip_addr_cmp (dest, &allsystems)) {
0414            LWIP_DEBUGF(IGMP_DEBUG, (" using \"ALL SYSTEMS\" address (224.0.0.1) [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp)));
0415            /* we first need to re-lookfor the group since we used dest last time */
0416            group = igmp_lookfor_group(inp, &igmp->igmp_group_address);
0417          } else {
0418            LWIP_DEBUGF(IGMP_DEBUG, (" with the group address as destination [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp)));
0419          }
0420 
0421          if (group != NULL) {
0422            IGMP_STATS_INC(igmp.unicast_query);
0423            igmp_delaying_member( group, igmp->igmp_maxresp);
0424          }
0425        }
0426      }
0427      break;
0428    }
0429    case IGMP_V2_MEMB_REPORT: {
0430      LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_V2_MEMB_REPORT\n"));
0431 
0432      IGMP_STATS_INC(igmp.report_rxed);
0433      if (group->group_state == IGMP_GROUP_DELAYING_MEMBER) {
0434        /* This is on a specific group we have already looked up */
0435        group->timer = 0; /* stopped */
0436        group->group_state = IGMP_GROUP_IDLE_MEMBER;
0437        group->last_reporter_flag = 0;
0438      }
0439      break;
0440    }
0441    default: {
0442      LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: unexpected msg %d in state %d on group %p on if %p\n",
0443        igmp->igmp_msgtype, group->group_state, &group, group->interface));
0444      break;
0445    }
0446   }
0447 
0448   pbuf_free(p);
0449   return;
0450 }
0451 
0452 /**
0453  * Join a group on one network interface.
0454  *
0455  * @param ifaddr ip address of the network interface which should join a new group
0456  * @param groupaddr the ip address of the group which to join
0457  * @return ERR_OK if group was joined on the netif(s), an err_t otherwise
0458  */
0459 err_t
0460 igmp_joingroup(struct ip_addr *ifaddr, struct ip_addr *groupaddr)
0461 {
0462   err_t              err = ERR_VAL; /* no matching interface */
0463   struct igmp_group *group;
0464   struct netif      *netif;
0465 
0466   /* make sure it is multicast address */
0467   LWIP_ERROR("igmp_joingroup: attempt to join non-multicast address", ip_addr_ismulticast(groupaddr), return ERR_VAL;);
0468   LWIP_ERROR("igmp_joingroup: attempt to join allsystems address", (!ip_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;);
0469 
0470   /* loop through netif's */
0471   netif = netif_list;
0472   while (netif != NULL) {
0473     /* Should we join this interface ? */
0474     if ((netif->flags & NETIF_FLAG_IGMP) && ((ip_addr_isany(ifaddr) || ip_addr_cmp(&(netif->ip_addr), ifaddr)))) {
0475       /* find group or create a new one if not found */
0476       group = igmp_lookup_group(netif, groupaddr);
0477 
0478       if (group != NULL) {
0479         /* This should create a new group, check the state to make sure */
0480         if (group->group_state != IGMP_GROUP_NON_MEMBER) {
0481           LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: join to group not in state IGMP_GROUP_NON_MEMBER\n"));
0482         } else {
0483           /* OK - it was new group */
0484           LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: join to new group: "));
0485           ip_addr_debug_print(IGMP_DEBUG, groupaddr);
0486           LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
0487 
0488           /* If first use of the group, allow the group at the MAC level */
0489           if ((group->use==0) && (netif->igmp_mac_filter != NULL)) {
0490             LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: igmp_mac_filter(ADD "));
0491             ip_addr_debug_print(IGMP_DEBUG, groupaddr);
0492             LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif));
0493             netif->igmp_mac_filter(netif, groupaddr, IGMP_ADD_MAC_FILTER);
0494           }
0495 
0496           IGMP_STATS_INC(igmp.join_sent);
0497           igmp_send(group, IGMP_V2_MEMB_REPORT);
0498 
0499           igmp_start_timer(group, IGMP_JOIN_DELAYING_MEMBER_TMR);
0500 
0501           /* Need to work out where this timer comes from */
0502           group->group_state = IGMP_GROUP_DELAYING_MEMBER;
0503         }
0504         /* Increment group use */
0505         group->use++;
0506         /* Join on this interface */
0507         err = ERR_OK;
0508       } else {
0509         /* Return an error even if some network interfaces are joined */
0510         /** @todo undo any other netif already joined */
0511         LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: Not enought memory to join to group\n"));
0512         return ERR_MEM;
0513       }
0514     }
0515     /* proceed to next network interface */
0516     netif = netif->next;
0517   }
0518 
0519   return err;
0520 }
0521 
0522 /**
0523  * Leave a group on one network interface.
0524  *
0525  * @param ifaddr ip address of the network interface which should leave a group
0526  * @param groupaddr the ip address of the group which to leave
0527  * @return ERR_OK if group was left on the netif(s), an err_t otherwise
0528  */
0529 err_t
0530 igmp_leavegroup(struct ip_addr *ifaddr, struct ip_addr *groupaddr)
0531 {
0532   err_t              err = ERR_VAL; /* no matching interface */
0533   struct igmp_group *group;
0534   struct netif      *netif;
0535 
0536   /* make sure it is multicast address */
0537   LWIP_ERROR("igmp_leavegroup: attempt to leave non-multicast address", ip_addr_ismulticast(groupaddr), return ERR_VAL;);
0538   LWIP_ERROR("igmp_leavegroup: attempt to leave allsystems address", (!ip_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;);
0539 
0540   /* loop through netif's */
0541   netif = netif_list;
0542   while (netif != NULL) {
0543     /* Should we leave this interface ? */
0544     if ((netif->flags & NETIF_FLAG_IGMP) && ((ip_addr_isany(ifaddr) || ip_addr_cmp(&(netif->ip_addr), ifaddr)))) {
0545       /* find group */
0546       group = igmp_lookfor_group(netif, groupaddr);
0547 
0548       if (group != NULL) {
0549         /* Only send a leave if the flag is set according to the state diagram */
0550         LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: Leaving group: "));
0551         ip_addr_debug_print(IGMP_DEBUG, groupaddr);
0552         LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
0553 
0554         /* If there is no other use of the group */
0555         if (group->use <= 1) {
0556           /* If we are the last reporter for this group */
0557           if (group->last_reporter_flag) {
0558             LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: sending leaving group\n"));
0559             IGMP_STATS_INC(igmp.leave_sent);
0560             igmp_send(group, IGMP_LEAVE_GROUP);
0561           }
0562           
0563           /* Disable the group at the MAC level */
0564           if (netif->igmp_mac_filter != NULL) {
0565             LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: igmp_mac_filter(DEL "));
0566             ip_addr_debug_print(IGMP_DEBUG, groupaddr);
0567             LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif));
0568             netif->igmp_mac_filter(netif, groupaddr, IGMP_DEL_MAC_FILTER);
0569           }
0570           
0571           LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: remove group: "));
0572           ip_addr_debug_print(IGMP_DEBUG, groupaddr);
0573           LWIP_DEBUGF(IGMP_DEBUG, ("\n"));          
0574           
0575           /* Free the group */
0576           igmp_remove_group(group);
0577         } else {
0578           /* Decrement group use */
0579           group->use--;
0580         }
0581         /* Leave on this interface */
0582         err = ERR_OK;
0583       } else {
0584         /* It's not a fatal error on "leavegroup" */
0585         LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: not member of group\n"));
0586       }
0587     }
0588     /* proceed to next network interface */
0589     netif = netif->next;
0590   }
0591 
0592   return err;
0593 }
0594 
0595 /**
0596  * The igmp timer function (both for NO_SYS=1 and =0)
0597  * Should be called every IGMP_TMR_INTERVAL milliseconds (100 ms is default).
0598  */
0599 void
0600 igmp_tmr(void)
0601 {
0602   struct igmp_group *group = igmp_group_list;
0603 
0604   while (group != NULL) {
0605     if (group->timer != 0) {
0606       group->timer -= 1;
0607       if (group->timer == 0) {
0608         igmp_timeout(group);
0609       }
0610     }
0611     group = group->next;
0612   }
0613 }
0614 
0615 /**
0616  * Called if a timeout for one group is reached.
0617  * Sends a report for this group.
0618  *
0619  * @param group an igmp_group for which a timeout is reached
0620  */
0621 void
0622 igmp_timeout(struct igmp_group *group)
0623 {
0624   /* If the state is IGMP_GROUP_DELAYING_MEMBER then we send a report for this group */
0625   if (group->group_state == IGMP_GROUP_DELAYING_MEMBER) {
0626     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_timeout: report membership for group with address "));
0627     ip_addr_debug_print(IGMP_DEBUG, &(group->group_address));
0628     LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", group->interface));
0629 
0630     igmp_send(group, IGMP_V2_MEMB_REPORT);
0631   }
0632 }
0633 
0634 /**
0635  * Start a timer for an igmp group
0636  *
0637  * @param group the igmp_group for which to start a timer
0638  * @param max_time the time in multiples of IGMP_TMR_INTERVAL (decrease with
0639  *        every call to igmp_tmr())
0640  */
0641 void
0642 igmp_start_timer(struct igmp_group *group, u8_t max_time)
0643 {
0644   /**
0645    * @todo Important !! this should be random 0 -> max_time. Find out how to do this
0646    */
0647   group->timer = max_time;
0648 }
0649 
0650 /**
0651  * Stop a timer for an igmp_group
0652  *
0653  * @param group the igmp_group for which to stop the timer
0654  */
0655 void
0656 igmp_stop_timer(struct igmp_group *group)
0657 {
0658   group->timer = 0;
0659 }
0660 
0661 /**
0662  * Delaying membership report for a group if necessary
0663  *
0664  * @param group the igmp_group for which "delaying" membership report
0665  * @param maxresp query delay
0666  */
0667 void
0668 igmp_delaying_member( struct igmp_group *group, u8_t maxresp)
0669 {
0670   if ((group->group_state == IGMP_GROUP_IDLE_MEMBER) ||
0671      ((group->group_state == IGMP_GROUP_DELAYING_MEMBER) && (maxresp > group->timer))) {
0672     igmp_start_timer(group, (maxresp)/2);
0673     group->group_state = IGMP_GROUP_DELAYING_MEMBER;
0674   }
0675 }
0676 
0677 
0678 /**
0679  * Sends an IP packet on a network interface. This function constructs the IP header
0680  * and calculates the IP header checksum. If the source IP address is NULL,
0681  * the IP address of the outgoing network interface is filled in as source address.
0682  *
0683  * @param p the packet to send (p->payload points to the data, e.g. next
0684             protocol header; if dest == IP_HDRINCL, p already includes an IP
0685             header and p->payload points to that IP header)
0686  * @param src the source IP address to send from (if src == IP_ADDR_ANY, the
0687  *         IP  address of the netif used to send is used as source address)
0688  * @param dest the destination IP address to send the packet to
0689  * @param ttl the TTL value to be set in the IP header
0690  * @param proto the PROTOCOL to be set in the IP header
0691  * @param netif the netif on which to send this packet
0692  * @return ERR_OK if the packet was sent OK
0693  *         ERR_BUF if p doesn't have enough space for IP/LINK headers
0694  *         returns errors returned by netif->output
0695  */
0696 err_t
0697 igmp_ip_output_if(struct pbuf *p, struct ip_addr *src, struct ip_addr *dest,
0698                   u8_t ttl, u8_t proto, struct netif *netif)
0699 {
0700   /* This is the "router alert" option */
0701   u16_t ra[2];
0702   ra[0] = htons (ROUTER_ALERT);
0703   ra[1] = 0x0000; /* Router shall examine packet */
0704   return ip_output_if_opt(p, src, dest, ttl, 0, proto, netif, ra, ROUTER_ALERTLEN);
0705 }
0706 
0707 /**
0708  * Send an igmp packet to a specific group.
0709  *
0710  * @param group the group to which to send the packet
0711  * @param type the type of igmp packet to send
0712  */
0713 void
0714 igmp_send(struct igmp_group *group, u8_t type)
0715 {
0716   struct pbuf*     p    = NULL;
0717   struct igmp_msg* igmp = NULL;
0718   struct ip_addr   src  = {0};
0719   struct ip_addr*  dest = NULL;
0720 
0721   /* IP header + "router alert" option + IGMP header */
0722   p = pbuf_alloc(PBUF_TRANSPORT, IGMP_MINLEN, PBUF_RAM);
0723   
0724   if (p) {
0725     igmp = p->payload;
0726     LWIP_ASSERT("igmp_send: check that first pbuf can hold struct igmp_msg",
0727                (p->len >= sizeof(struct igmp_msg)));
0728     ip_addr_set(&src, &((group->interface)->ip_addr));
0729      
0730     if (type == IGMP_V2_MEMB_REPORT) {
0731       dest = &(group->group_address);
0732       IGMP_STATS_INC(igmp.report_sent);
0733       ip_addr_set(&(igmp->igmp_group_address), &(group->group_address));
0734       group->last_reporter_flag = 1; /* Remember we were the last to report */
0735     } else {
0736       if (type == IGMP_LEAVE_GROUP) {
0737         dest = &allrouters;
0738         ip_addr_set(&(igmp->igmp_group_address), &(group->group_address));
0739       }
0740     }
0741 
0742     if ((type == IGMP_V2_MEMB_REPORT) || (type == IGMP_LEAVE_GROUP)) {
0743       igmp->igmp_msgtype  = type;
0744       igmp->igmp_maxresp  = 0;
0745       igmp->igmp_checksum = 0;
0746       igmp->igmp_checksum = inet_chksum( igmp, IGMP_MINLEN);
0747 
0748       igmp_ip_output_if(p, &src, dest, IGMP_TTL, IP_PROTO_IGMP, group->interface);
0749     }
0750 
0751     pbuf_free(p);
0752   } else {
0753     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_send: not enough memory for igmp_send\n"));
0754   }
0755 }
0756 
0757 #endif /* LWIP_IGMP */