Port of LwIP performed by Ralf in 2010. Not recommended for use with recent mbed libraries, but good demos of raw LwIP possible

Dependents:   LwIP_raw_API_serverExample tiny-dtls

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers igmp.c Source File

igmp.c

Go to the documentation of this file.
00001 /**
00002  * @file
00003  * IGMP - Internet Group Management Protocol
00004  *
00005  */
00006 
00007 /*
00008  * Copyright (c) 2002 CITEL Technologies Ltd.
00009  * All rights reserved.
00010  *
00011  * Redistribution and use in source and binary forms, with or without 
00012  * modification, are permitted provided that the following conditions 
00013  * are met: 
00014  * 1. Redistributions of source code must retain the above copyright 
00015  *    notice, this list of conditions and the following disclaimer. 
00016  * 2. Redistributions in binary form must reproduce the above copyright 
00017  *    notice, this list of conditions and the following disclaimer in the 
00018  *    documentation and/or other materials provided with the distribution. 
00019  * 3. Neither the name of CITEL Technologies Ltd nor the names of its contributors 
00020  *    may be used to endorse or promote products derived from this software 
00021  *    without specific prior written permission. 
00022  *
00023  * THIS SOFTWARE IS PROVIDED BY CITEL TECHNOLOGIES AND CONTRIBUTORS ``AS IS''
00024  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
00025  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
00026  * ARE DISCLAIMED.  IN NO EVENT SHALL CITEL TECHNOLOGIES OR CONTRIBUTORS BE LIABLE 
00027  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
00028  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 
00029  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
00030  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
00031  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 
00032  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
00033  * SUCH DAMAGE. 
00034  *
00035  * This file is a contribution to the lwIP TCP/IP stack.
00036  * The Swedish Institute of Computer Science and Adam Dunkels
00037  * are specifically granted permission to redistribute this
00038  * source code.
00039 */
00040 
00041 /*-------------------------------------------------------------
00042 Note 1)
00043 Although the rfc requires V1 AND V2 capability
00044 we will only support v2 since now V1 is very old (August 1989)
00045 V1 can be added if required
00046 
00047 a debug print and statistic have been implemented to
00048 show this up.
00049 -------------------------------------------------------------
00050 -------------------------------------------------------------
00051 Note 2)
00052 A query for a specific group address (as opposed to ALLHOSTS)
00053 has now been implemented as I am unsure if it is required
00054 
00055 a debug print and statistic have been implemented to
00056 show this up.
00057 -------------------------------------------------------------
00058 -------------------------------------------------------------
00059 Note 3)
00060 The router alert rfc 2113 is implemented in outgoing packets
00061 but not checked rigorously incoming
00062 -------------------------------------------------------------
00063 Steve Reynolds
00064 ------------------------------------------------------------*/
00065 
00066 /*-----------------------------------------------------------------------------
00067  * RFC 988  - Host extensions for IP multicasting                         - V0
00068  * RFC 1054 - Host extensions for IP multicasting                         -
00069  * RFC 1112 - Host extensions for IP multicasting                         - V1
00070  * RFC 2236 - Internet Group Management Protocol, Version 2               - V2  <- this code is based on this RFC (it's the "de facto" standard)
00071  * RFC 3376 - Internet Group Management Protocol, Version 3               - V3
00072  * RFC 4604 - Using Internet Group Management Protocol Version 3...       - V3+
00073  * RFC 2113 - IP Router Alert Option                                      - 
00074  *----------------------------------------------------------------------------*/
00075 
00076 /*-----------------------------------------------------------------------------
00077  * Includes
00078  *----------------------------------------------------------------------------*/
00079 
00080 #include "lwip/opt.h"
00081 
00082 #if LWIP_IGMP /* don't build if not configured for use in lwipopts.h */
00083 
00084 #include "lwip/igmp.h"
00085 #include "lwip/debug.h"
00086 #include "lwip/def.h"
00087 #include "lwip/mem.h"
00088 #include "lwip/ip.h"
00089 #include "lwip/inet.h"
00090 #include "lwip/inet_chksum.h"
00091 #include "lwip/netif.h"
00092 #include "lwip/icmp.h"
00093 #include "lwip/udp.h"
00094 #include "lwip/tcp.h"
00095 #include "lwip/stats.h"
00096 
00097 #include "string.h"
00098 
00099 /*-----------------------------------------------------------------------------
00100  * Globales
00101  *----------------------------------------------------------------------------*/
00102 
00103 static struct igmp_group* igmp_group_list;
00104 static struct ip_addr     allsystems;
00105 static struct ip_addr     allrouters;
00106 
00107 /**
00108  * Initialize the IGMP module
00109  */
00110 void
00111 igmp_init(void)
00112 {
00113   LWIP_DEBUGF(IGMP_DEBUG, ("igmp_init: initializing\n"));
00114 
00115   IP4_ADDR(&allsystems, 224, 0, 0, 1);
00116   IP4_ADDR(&allrouters, 224, 0, 0, 2);
00117 }
00118 
00119 #ifdef LWIP_DEBUG
00120 /**
00121  * Dump global IGMP groups list
00122  */
00123 void
00124 igmp_dump_group_list()
00125 { 
00126   struct igmp_group *group = igmp_group_list;
00127 
00128   while (group != NULL) {
00129     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_dump_group_list: [%"U32_F"] ", (u32_t)(group->group_state)));
00130     ip_addr_debug_print(IGMP_DEBUG, &group->group_address);
00131     LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", group->interface));
00132     group = group->next;
00133   }
00134   LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
00135 }
00136 #else
00137 #define igmp_dump_group_list()
00138 #endif /* LWIP_DEBUG */
00139 
00140 /**
00141  * Start IGMP processing on interface
00142  *
00143  * @param netif network interface on which start IGMP processing
00144  */
00145 err_t
00146 igmp_start(struct netif *netif)
00147 {
00148   struct igmp_group* group;
00149 
00150   LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: starting IGMP processing on if %p\n", netif));
00151 
00152   group = igmp_lookup_group(netif, &allsystems);
00153 
00154   if (group != NULL) {
00155     group->group_state = IGMP_GROUP_IDLE_MEMBER;
00156     group->use++;
00157 
00158     /* Allow the igmp messages at the MAC level */
00159     if (netif->igmp_mac_filter != NULL) {
00160       LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: igmp_mac_filter(ADD "));
00161       ip_addr_debug_print(IGMP_DEBUG, &allsystems);
00162       LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif));
00163       netif->igmp_mac_filter( netif, &allsystems, IGMP_ADD_MAC_FILTER);
00164     }
00165 
00166     return ERR_OK;
00167   }
00168 
00169   return ERR_MEM;
00170 }
00171 
00172 /**
00173  * Stop IGMP processing on interface
00174  *
00175  * @param netif network interface on which stop IGMP processing
00176  */
00177 err_t
00178 igmp_stop(struct netif *netif)
00179 {
00180   struct igmp_group *group = igmp_group_list;
00181   struct igmp_group *prev  = NULL;
00182   struct igmp_group *next;
00183 
00184   /* look for groups joined on this interface further down the list */
00185   while (group != NULL) {
00186     next = group->next;
00187     /* is it a group joined on this interface? */
00188     if (group->interface == netif) {
00189       /* is it the first group of the list? */
00190       if (group == igmp_group_list) {
00191         igmp_group_list = next;
00192       }
00193       /* is there a "previous" group defined? */
00194       if (prev != NULL) {
00195         prev->next = next;
00196       }
00197       /* disable the group at the MAC level */
00198       if (netif->igmp_mac_filter != NULL) {
00199         LWIP_DEBUGF(IGMP_DEBUG, ("igmp_stop: igmp_mac_filter(DEL "));
00200         ip_addr_debug_print(IGMP_DEBUG, &group->group_address);
00201         LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif));
00202         netif->igmp_mac_filter(netif, &(group->group_address), IGMP_DEL_MAC_FILTER);
00203       }
00204       /* free group */
00205       memp_free(MEMP_IGMP_GROUP, group);
00206     } else {
00207       /* change the "previous" */
00208       prev = group;
00209     }
00210     /* move to "next" */
00211     group = next;
00212   }
00213   return ERR_OK;
00214 }
00215 
00216 /**
00217  * Report IGMP memberships for this interface
00218  *
00219  * @param netif network interface on which report IGMP memberships
00220  */
00221 void
00222 igmp_report_groups( struct netif *netif)
00223 {
00224   struct igmp_group *group = igmp_group_list;
00225 
00226   LWIP_DEBUGF(IGMP_DEBUG, ("igmp_report_groups: sending IGMP reports on if %p\n", netif));
00227 
00228   while (group != NULL) {
00229     if (group->interface == netif) {
00230       igmp_delaying_member( group, IGMP_JOIN_DELAYING_MEMBER_TMR);
00231     }
00232     group = group->next;
00233   }
00234 }
00235 
00236 /**
00237  * Search for a group in the global igmp_group_list
00238  *
00239  * @param ifp the network interface for which to look
00240  * @param addr the group ip address to search for
00241  * @return a struct igmp_group* if the group has been found,
00242  *         NULL if the group wasn't found.
00243  */
00244 struct igmp_group *
00245 igmp_lookfor_group(struct netif *ifp, struct ip_addr *addr)
00246 {
00247   struct igmp_group *group = igmp_group_list;
00248 
00249   while (group != NULL) {
00250     if ((group->interface == ifp) && (ip_addr_cmp(&(group->group_address), addr))) {
00251       return group;
00252     }
00253     group = group->next;
00254   }
00255 
00256   /* to be clearer, we return NULL here instead of
00257    * 'group' (which is also NULL at this point).
00258    */
00259   return NULL;
00260 }
00261 
00262 /**
00263  * Search for a specific igmp group and create a new one if not found-
00264  *
00265  * @param ifp the network interface for which to look
00266  * @param addr the group ip address to search
00267  * @return a struct igmp_group*,
00268  *         NULL on memory error.
00269  */
00270 struct igmp_group *
00271 igmp_lookup_group(struct netif *ifp, struct ip_addr *addr)
00272 {
00273   struct igmp_group *group = igmp_group_list;
00274   
00275   /* Search if the group already exists */
00276   group = igmp_lookfor_group(ifp, addr);
00277   if (group != NULL) {
00278     /* Group already exists. */
00279     return group;
00280   }
00281 
00282   /* Group doesn't exist yet, create a new one */
00283   group = memp_malloc(MEMP_IGMP_GROUP);
00284   if (group != NULL) {
00285     group->interface          = ifp;
00286     ip_addr_set(&(group->group_address), addr);
00287     group->timer              = 0; /* Not running */
00288     group->group_state        = IGMP_GROUP_NON_MEMBER;
00289     group->last_reporter_flag = 0;
00290     group->use                = 0;
00291     group->next               = igmp_group_list;
00292     
00293     igmp_group_list = group;
00294   }
00295 
00296   LWIP_DEBUGF(IGMP_DEBUG, ("igmp_lookup_group: %sallocated a new group with address ", (group?"":"impossible to ")));
00297   ip_addr_debug_print(IGMP_DEBUG, addr);
00298   LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", ifp));
00299 
00300   return group;
00301 }
00302 
00303 /**
00304  * Remove a group in the global igmp_group_list
00305  *
00306  * @param group the group to remove from the global igmp_group_list
00307  * @return ERR_OK if group was removed from the list, an err_t otherwise
00308  */
00309 err_t
00310 igmp_remove_group(struct igmp_group *group)
00311 {
00312   err_t err = ERR_OK;
00313 
00314   /* Is it the first group? */
00315   if (igmp_group_list == group) {
00316     igmp_group_list = group->next;
00317   } else {
00318     /* look for group further down the list */
00319     struct igmp_group *tmpGroup;
00320     for (tmpGroup = igmp_group_list; tmpGroup != NULL; tmpGroup = tmpGroup->next) {
00321       if (tmpGroup->next == group) {
00322         tmpGroup->next = group->next;
00323         break;
00324       }
00325     }
00326     /* Group not found in the global igmp_group_list */
00327     if (tmpGroup == NULL)
00328       err = ERR_ARG;
00329   }
00330   /* free group */
00331   memp_free(MEMP_IGMP_GROUP, group);
00332 
00333   return err;
00334 }
00335 
00336 /**
00337  * Called from ip_input() if a new IGMP packet is received.
00338  *
00339  * @param p received igmp packet, p->payload pointing to the ip header
00340  * @param inp network interface on which the packet was received
00341  * @param dest destination ip address of the igmp packet
00342  */
00343 void
00344 igmp_input(struct pbuf *p, struct netif *inp, struct ip_addr *dest)
00345 {
00346   struct ip_hdr *    iphdr;
00347   struct igmp_msg*   igmp;
00348   struct igmp_group* group;
00349   struct igmp_group* groupref;
00350 
00351   /* Note that the length CAN be greater than 8 but only 8 are used - All are included in the checksum */    
00352   iphdr = p->payload;
00353   if (pbuf_header(p, -(s16_t)(IPH_HL(iphdr) * 4)) || (p->len < IGMP_MINLEN)) {
00354     pbuf_free(p);
00355     IGMP_STATS_INC(igmp.lenerr);
00356     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: length error\n"));
00357     return;
00358   }
00359 
00360   LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: message from "));
00361   ip_addr_debug_print(IGMP_DEBUG, &(iphdr->src));
00362   LWIP_DEBUGF(IGMP_DEBUG, (" to address "));
00363   ip_addr_debug_print(IGMP_DEBUG, &(iphdr->dest));
00364   LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", inp));
00365 
00366   /* Now calculate and check the checksum */
00367   igmp = (struct igmp_msg *)p->payload;
00368   if (inet_chksum(igmp, p->len)) {
00369     pbuf_free(p);
00370     IGMP_STATS_INC(igmp.chkerr);
00371     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: checksum error\n"));
00372     return;
00373   }
00374 
00375   /* Packet is ok so find an existing group */
00376   group = igmp_lookfor_group(inp, dest); /* use the incoming IP address! */
00377   
00378   /* If group can be found or create... */
00379   if (!group) {
00380     pbuf_free(p);
00381     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP frame not for us\n"));
00382     return;
00383   }
00384 
00385   /* NOW ACT ON THE INCOMING MESSAGE TYPE... */
00386   switch (igmp->igmp_msgtype) {
00387    case IGMP_MEMB_QUERY: {
00388      /* IGMP_MEMB_QUERY to the "all systems" address ? */
00389      if ((ip_addr_cmp(dest, &allsystems)) && (igmp->igmp_group_address.addr == 0)) {
00390        /* THIS IS THE GENERAL QUERY */
00391        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)));
00392 
00393        if (igmp->igmp_maxresp == 0) {
00394          IGMP_STATS_INC(igmp.v1_rxed);
00395          LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: got an all hosts query with time== 0 - this is V1 and not implemented - treat as v2\n"));
00396          igmp->igmp_maxresp = IGMP_V1_DELAYING_MEMBER_TMR;
00397        }
00398 
00399        IGMP_STATS_INC(igmp.group_query_rxed);
00400        groupref = igmp_group_list;
00401        while (groupref) {
00402          /* Do not send messages on the all systems group address! */
00403          if ((groupref->interface == inp) && (!(ip_addr_cmp(&(groupref->group_address), &allsystems)))) {
00404            igmp_delaying_member( groupref, igmp->igmp_maxresp);
00405          }
00406          groupref = groupref->next;
00407        }
00408      } else {
00409        /* IGMP_MEMB_QUERY to a specific group ? */
00410        if (group->group_address.addr != 0) {
00411          LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_MEMB_QUERY to a specific group "));
00412          ip_addr_debug_print(IGMP_DEBUG, &group->group_address);
00413          if (ip_addr_cmp (dest, &allsystems)) {
00414            LWIP_DEBUGF(IGMP_DEBUG, (" using \"ALL SYSTEMS\" address (224.0.0.1) [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp)));
00415            /* we first need to re-lookfor the group since we used dest last time */
00416            group = igmp_lookfor_group(inp, &igmp->igmp_group_address);
00417          } else {
00418            LWIP_DEBUGF(IGMP_DEBUG, (" with the group address as destination [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp)));
00419          }
00420 
00421          if (group != NULL) {
00422            IGMP_STATS_INC(igmp.unicast_query);
00423            igmp_delaying_member( group, igmp->igmp_maxresp);
00424          }
00425        }
00426      }
00427      break;
00428    }
00429    case IGMP_V2_MEMB_REPORT: {
00430      LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_V2_MEMB_REPORT\n"));
00431 
00432      IGMP_STATS_INC(igmp.report_rxed);
00433      if (group->group_state == IGMP_GROUP_DELAYING_MEMBER) {
00434        /* This is on a specific group we have already looked up */
00435        group->timer = 0; /* stopped */
00436        group->group_state = IGMP_GROUP_IDLE_MEMBER;
00437        group->last_reporter_flag = 0;
00438      }
00439      break;
00440    }
00441    default: {
00442      LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: unexpected msg %d in state %d on group %p on if %p\n",
00443        igmp->igmp_msgtype, group->group_state, &group, group->interface));
00444      break;
00445    }
00446   }
00447 
00448   pbuf_free(p);
00449   return;
00450 }
00451 
00452 /**
00453  * Join a group on one network interface.
00454  *
00455  * @param ifaddr ip address of the network interface which should join a new group
00456  * @param groupaddr the ip address of the group which to join
00457  * @return ERR_OK if group was joined on the netif(s), an err_t otherwise
00458  */
00459 err_t
00460 igmp_joingroup(struct ip_addr *ifaddr, struct ip_addr *groupaddr)
00461 {
00462   err_t              err = ERR_VAL; /* no matching interface */
00463   struct igmp_group *group;
00464   struct netif      *netif;
00465 
00466   /* make sure it is multicast address */
00467   LWIP_ERROR("igmp_joingroup: attempt to join non-multicast address", ip_addr_ismulticast(groupaddr), return ERR_VAL;);
00468   LWIP_ERROR("igmp_joingroup: attempt to join allsystems address", (!ip_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;);
00469 
00470   /* loop through netif's */
00471   netif = netif_list;
00472   while (netif != NULL) {
00473     /* Should we join this interface ? */
00474     if ((netif->flags & NETIF_FLAG_IGMP) && ((ip_addr_isany(ifaddr) || ip_addr_cmp(&(netif->ip_addr), ifaddr)))) {
00475       /* find group or create a new one if not found */
00476       group = igmp_lookup_group(netif, groupaddr);
00477 
00478       if (group != NULL) {
00479         /* This should create a new group, check the state to make sure */
00480         if (group->group_state != IGMP_GROUP_NON_MEMBER) {
00481           LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: join to group not in state IGMP_GROUP_NON_MEMBER\n"));
00482         } else {
00483           /* OK - it was new group */
00484           LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: join to new group: "));
00485           ip_addr_debug_print(IGMP_DEBUG, groupaddr);
00486           LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
00487 
00488           /* If first use of the group, allow the group at the MAC level */
00489           if ((group->use==0) && (netif->igmp_mac_filter != NULL)) {
00490             LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: igmp_mac_filter(ADD "));
00491             ip_addr_debug_print(IGMP_DEBUG, groupaddr);
00492             LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif));
00493             netif->igmp_mac_filter(netif, groupaddr, IGMP_ADD_MAC_FILTER);
00494           }
00495 
00496           IGMP_STATS_INC(igmp.join_sent);
00497           igmp_send(group, IGMP_V2_MEMB_REPORT);
00498 
00499           igmp_start_timer(group, IGMP_JOIN_DELAYING_MEMBER_TMR);
00500 
00501           /* Need to work out where this timer comes from */
00502           group->group_state = IGMP_GROUP_DELAYING_MEMBER;
00503         }
00504         /* Increment group use */
00505         group->use++;
00506         /* Join on this interface */
00507         err = ERR_OK;
00508       } else {
00509         /* Return an error even if some network interfaces are joined */
00510         /** @todo undo any other netif already joined */
00511         LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: Not enought memory to join to group\n"));
00512         return ERR_MEM;
00513       }
00514     }
00515     /* proceed to next network interface */
00516     netif = netif->next;
00517   }
00518 
00519   return err;
00520 }
00521 
00522 /**
00523  * Leave a group on one network interface.
00524  *
00525  * @param ifaddr ip address of the network interface which should leave a group
00526  * @param groupaddr the ip address of the group which to leave
00527  * @return ERR_OK if group was left on the netif(s), an err_t otherwise
00528  */
00529 err_t
00530 igmp_leavegroup(struct ip_addr *ifaddr, struct ip_addr *groupaddr)
00531 {
00532   err_t              err = ERR_VAL; /* no matching interface */
00533   struct igmp_group *group;
00534   struct netif      *netif;
00535 
00536   /* make sure it is multicast address */
00537   LWIP_ERROR("igmp_leavegroup: attempt to leave non-multicast address", ip_addr_ismulticast(groupaddr), return ERR_VAL;);
00538   LWIP_ERROR("igmp_leavegroup: attempt to leave allsystems address", (!ip_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;);
00539 
00540   /* loop through netif's */
00541   netif = netif_list;
00542   while (netif != NULL) {
00543     /* Should we leave this interface ? */
00544     if ((netif->flags & NETIF_FLAG_IGMP) && ((ip_addr_isany(ifaddr) || ip_addr_cmp(&(netif->ip_addr), ifaddr)))) {
00545       /* find group */
00546       group = igmp_lookfor_group(netif, groupaddr);
00547 
00548       if (group != NULL) {
00549         /* Only send a leave if the flag is set according to the state diagram */
00550         LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: Leaving group: "));
00551         ip_addr_debug_print(IGMP_DEBUG, groupaddr);
00552         LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
00553 
00554         /* If there is no other use of the group */
00555         if (group->use <= 1) {
00556           /* If we are the last reporter for this group */
00557           if (group->last_reporter_flag) {
00558             LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: sending leaving group\n"));
00559             IGMP_STATS_INC(igmp.leave_sent);
00560             igmp_send(group, IGMP_LEAVE_GROUP);
00561           }
00562           
00563           /* Disable the group at the MAC level */
00564           if (netif->igmp_mac_filter != NULL) {
00565             LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: igmp_mac_filter(DEL "));
00566             ip_addr_debug_print(IGMP_DEBUG, groupaddr);
00567             LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif));
00568             netif->igmp_mac_filter(netif, groupaddr, IGMP_DEL_MAC_FILTER);
00569           }
00570           
00571           LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: remove group: "));
00572           ip_addr_debug_print(IGMP_DEBUG, groupaddr);
00573           LWIP_DEBUGF(IGMP_DEBUG, ("\n"));          
00574           
00575           /* Free the group */
00576           igmp_remove_group(group);
00577         } else {
00578           /* Decrement group use */
00579           group->use--;
00580         }
00581         /* Leave on this interface */
00582         err = ERR_OK;
00583       } else {
00584         /* It's not a fatal error on "leavegroup" */
00585         LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: not member of group\n"));
00586       }
00587     }
00588     /* proceed to next network interface */
00589     netif = netif->next;
00590   }
00591 
00592   return err;
00593 }
00594 
00595 /**
00596  * The igmp timer function (both for NO_SYS=1 and =0)
00597  * Should be called every IGMP_TMR_INTERVAL milliseconds (100 ms is default).
00598  */
00599 void
00600 igmp_tmr(void)
00601 {
00602   struct igmp_group *group = igmp_group_list;
00603 
00604   while (group != NULL) {
00605     if (group->timer != 0) {
00606       group->timer -= 1;
00607       if (group->timer == 0) {
00608         igmp_timeout(group);
00609       }
00610     }
00611     group = group->next;
00612   }
00613 }
00614 
00615 /**
00616  * Called if a timeout for one group is reached.
00617  * Sends a report for this group.
00618  *
00619  * @param group an igmp_group for which a timeout is reached
00620  */
00621 void
00622 igmp_timeout(struct igmp_group *group)
00623 {
00624   /* If the state is IGMP_GROUP_DELAYING_MEMBER then we send a report for this group */
00625   if (group->group_state == IGMP_GROUP_DELAYING_MEMBER) {
00626     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_timeout: report membership for group with address "));
00627     ip_addr_debug_print(IGMP_DEBUG, &(group->group_address));
00628     LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", group->interface));
00629 
00630     igmp_send(group, IGMP_V2_MEMB_REPORT);
00631   }
00632 }
00633 
00634 /**
00635  * Start a timer for an igmp group
00636  *
00637  * @param group the igmp_group for which to start a timer
00638  * @param max_time the time in multiples of IGMP_TMR_INTERVAL (decrease with
00639  *        every call to igmp_tmr())
00640  */
00641 void
00642 igmp_start_timer(struct igmp_group *group, u8_t max_time)
00643 {
00644   /**
00645    * @todo Important !! this should be random 0 -> max_time. Find out how to do this
00646    */
00647   group->timer = max_time;
00648 }
00649 
00650 /**
00651  * Stop a timer for an igmp_group
00652  *
00653  * @param group the igmp_group for which to stop the timer
00654  */
00655 void
00656 igmp_stop_timer(struct igmp_group *group)
00657 {
00658   group->timer = 0;
00659 }
00660 
00661 /**
00662  * Delaying membership report for a group if necessary
00663  *
00664  * @param group the igmp_group for which "delaying" membership report
00665  * @param maxresp query delay
00666  */
00667 void
00668 igmp_delaying_member( struct igmp_group *group, u8_t maxresp)
00669 {
00670   if ((group->group_state == IGMP_GROUP_IDLE_MEMBER) ||
00671      ((group->group_state == IGMP_GROUP_DELAYING_MEMBER) && (maxresp > group->timer))) {
00672     igmp_start_timer(group, (maxresp)/2);
00673     group->group_state = IGMP_GROUP_DELAYING_MEMBER;
00674   }
00675 }
00676 
00677 
00678 /**
00679  * Sends an IP packet on a network interface. This function constructs the IP header
00680  * and calculates the IP header checksum. If the source IP address is NULL,
00681  * the IP address of the outgoing network interface is filled in as source address.
00682  *
00683  * @param p the packet to send (p->payload points to the data, e.g. next
00684             protocol header; if dest == IP_HDRINCL, p already includes an IP
00685             header and p->payload points to that IP header)
00686  * @param src the source IP address to send from (if src == IP_ADDR_ANY, the
00687  *         IP  address of the netif used to send is used as source address)
00688  * @param dest the destination IP address to send the packet to
00689  * @param ttl the TTL value to be set in the IP header
00690  * @param proto the PROTOCOL to be set in the IP header
00691  * @param netif the netif on which to send this packet
00692  * @return ERR_OK if the packet was sent OK
00693  *         ERR_BUF if p doesn't have enough space for IP/LINK headers
00694  *         returns errors returned by netif->output
00695  */
00696 err_t
00697 igmp_ip_output_if(struct pbuf *p, struct ip_addr *src, struct ip_addr *dest,
00698                   u8_t ttl, u8_t proto, struct netif *netif)
00699 {
00700   /* This is the "router alert" option */
00701   u16_t ra[2];
00702   ra[0] = htons (ROUTER_ALERT);
00703   ra[1] = 0x0000; /* Router shall examine packet */
00704   return ip_output_if_opt(p, src, dest, ttl, 0, proto, netif, ra, ROUTER_ALERTLEN);
00705 }
00706 
00707 /**
00708  * Send an igmp packet to a specific group.
00709  *
00710  * @param group the group to which to send the packet
00711  * @param type the type of igmp packet to send
00712  */
00713 void
00714 igmp_send(struct igmp_group *group, u8_t type)
00715 {
00716   struct pbuf*     p    = NULL;
00717   struct igmp_msg* igmp = NULL;
00718   struct ip_addr   src  = {0};
00719   struct ip_addr*  dest = NULL;
00720 
00721   /* IP header + "router alert" option + IGMP header */
00722   p = pbuf_alloc(PBUF_TRANSPORT, IGMP_MINLEN, PBUF_RAM);
00723   
00724   if (p) {
00725     igmp = p->payload;
00726     LWIP_ASSERT("igmp_send: check that first pbuf can hold struct igmp_msg",
00727                (p->len >= sizeof(struct igmp_msg)));
00728     ip_addr_set(&src, &((group->interface)->ip_addr));
00729      
00730     if (type == IGMP_V2_MEMB_REPORT) {
00731       dest = &(group->group_address);
00732       IGMP_STATS_INC(igmp.report_sent);
00733       ip_addr_set(&(igmp->igmp_group_address), &(group->group_address));
00734       group->last_reporter_flag = 1; /* Remember we were the last to report */
00735     } else {
00736       if (type == IGMP_LEAVE_GROUP) {
00737         dest = &allrouters;
00738         ip_addr_set(&(igmp->igmp_group_address), &(group->group_address));
00739       }
00740     }
00741 
00742     if ((type == IGMP_V2_MEMB_REPORT) || (type == IGMP_LEAVE_GROUP)) {
00743       igmp->igmp_msgtype  = type;
00744       igmp->igmp_maxresp  = 0;
00745       igmp->igmp_checksum = 0;
00746       igmp->igmp_checksum = inet_chksum( igmp, IGMP_MINLEN);
00747 
00748       igmp_ip_output_if(p, &src, dest, IGMP_TTL, IP_PROTO_IGMP, group->interface);
00749     }
00750 
00751     pbuf_free(p);
00752   } else {
00753     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_send: not enough memory for igmp_send\n"));
00754   }
00755 }
00756 
00757 #endif /* LWIP_IGMP */