Rizky Ardi Maulana / mbed-os
Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers lwip_igmp.c Source File

lwip_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  * @defgroup igmp IGMP
00043  * @ingroup ip4
00044  * To be called from TCPIP thread
00045  */
00046 
00047 /*-------------------------------------------------------------
00048 Note 1)
00049 Although the rfc requires V1 AND V2 capability
00050 we will only support v2 since now V1 is very old (August 1989)
00051 V1 can be added if required
00052 
00053 a debug print and statistic have been implemented to
00054 show this up.
00055 -------------------------------------------------------------
00056 -------------------------------------------------------------
00057 Note 2)
00058 A query for a specific group address (as opposed to ALLHOSTS)
00059 has now been implemented as I am unsure if it is required
00060 
00061 a debug print and statistic have been implemented to
00062 show this up.
00063 -------------------------------------------------------------
00064 -------------------------------------------------------------
00065 Note 3)
00066 The router alert rfc 2113 is implemented in outgoing packets
00067 but not checked rigorously incoming
00068 -------------------------------------------------------------
00069 Steve Reynolds
00070 ------------------------------------------------------------*/
00071 
00072 /*-----------------------------------------------------------------------------
00073  * RFC 988  - Host extensions for IP multicasting                         - V0
00074  * RFC 1054 - Host extensions for IP multicasting                         -
00075  * RFC 1112 - Host extensions for IP multicasting                         - V1
00076  * RFC 2236 - Internet Group Management Protocol, Version 2               - V2  <- this code is based on this RFC (it's the "de facto" standard)
00077  * RFC 3376 - Internet Group Management Protocol, Version 3               - V3
00078  * RFC 4604 - Using Internet Group Management Protocol Version 3...       - V3+
00079  * RFC 2113 - IP Router Alert Option                                      -
00080  *----------------------------------------------------------------------------*/
00081 
00082 /*-----------------------------------------------------------------------------
00083  * Includes
00084  *----------------------------------------------------------------------------*/
00085 
00086 #include "lwip/opt.h"
00087 
00088 #if LWIP_IPV4 && LWIP_IGMP /* don't build if not configured for use in lwipopts.h */
00089 
00090 #include "lwip/igmp.h"
00091 #include "lwip/debug.h"
00092 #include "lwip/def.h"
00093 #include "lwip/mem.h"
00094 #include "lwip/ip.h"
00095 #include "lwip/inet_chksum.h"
00096 #include "lwip/netif.h"
00097 #include "lwip/stats.h"
00098 
00099 #include "string.h"
00100 
00101 /*
00102  * IGMP constants
00103  */
00104 #define IGMP_TTL                       1
00105 #define IGMP_MINLEN                    8
00106 #define ROUTER_ALERT                   0x9404U
00107 #define ROUTER_ALERTLEN                4
00108 
00109 /*
00110  * IGMP message types, including version number.
00111  */
00112 #define IGMP_MEMB_QUERY                0x11 /* Membership query         */
00113 #define IGMP_V1_MEMB_REPORT            0x12 /* Ver. 1 membership report */
00114 #define IGMP_V2_MEMB_REPORT            0x16 /* Ver. 2 membership report */
00115 #define IGMP_LEAVE_GROUP               0x17 /* Leave-group message      */
00116 
00117 /* Group  membership states */
00118 #define IGMP_GROUP_NON_MEMBER          0
00119 #define IGMP_GROUP_DELAYING_MEMBER     1
00120 #define IGMP_GROUP_IDLE_MEMBER         2
00121 
00122 /**
00123  * IGMP packet format.
00124  */
00125 #ifdef PACK_STRUCT_USE_INCLUDES
00126 #  include "arch/bpstruct.h"
00127 #endif
00128 PACK_STRUCT_BEGIN
00129 struct igmp_msg {
00130   PACK_STRUCT_FLD_8(u8_t         igmp_msgtype);
00131   PACK_STRUCT_FLD_8(u8_t         igmp_maxresp);
00132   PACK_STRUCT_FIELD(u16_t        igmp_checksum);
00133   PACK_STRUCT_FLD_S(ip4_addr_p_t igmp_group_address);
00134 } PACK_STRUCT_STRUCT;
00135 PACK_STRUCT_END
00136 #ifdef PACK_STRUCT_USE_INCLUDES
00137 #  include "arch/epstruct.h"
00138 #endif
00139 
00140 
00141 static struct igmp_group *igmp_lookup_group(struct netif *ifp, const ip4_addr_t *addr);
00142 static err_t  igmp_remove_group(struct igmp_group *group);
00143 static void   igmp_timeout( struct igmp_group *group);
00144 static void   igmp_start_timer(struct igmp_group *group, u8_t max_time);
00145 static void   igmp_delaying_member(struct igmp_group *group, u8_t maxresp);
00146 static err_t  igmp_ip_output_if(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest, struct netif *netif);
00147 static void   igmp_send(struct igmp_group *group, u8_t type);
00148 
00149 
00150 static struct igmp_group* igmp_group_list;
00151 static ip4_addr_t     allsystems;
00152 static ip4_addr_t     allrouters;
00153 
00154 
00155 /**
00156  * Initialize the IGMP module
00157  */
00158 void
00159 igmp_init(void)
00160 {
00161   LWIP_DEBUGF(IGMP_DEBUG, ("igmp_init: initializing\n"));
00162 
00163   IP4_ADDR(&allsystems, 224, 0, 0, 1);
00164   IP4_ADDR(&allrouters, 224, 0, 0, 2);
00165 }
00166 
00167 /**
00168  * Start IGMP processing on interface
00169  *
00170  * @param netif network interface on which start IGMP processing
00171  */
00172 err_t
00173 igmp_start(struct netif *netif)
00174 {
00175   struct igmp_group* group;
00176 
00177   LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: starting IGMP processing on if %p\n", (void*)netif));
00178 
00179   group = igmp_lookup_group(netif, &allsystems);
00180 
00181   if (group != NULL) {
00182     group->group_state = IGMP_GROUP_IDLE_MEMBER;
00183     group->use++;
00184 
00185     /* Allow the igmp messages at the MAC level */
00186     if (netif->igmp_mac_filter != NULL) {
00187       LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: igmp_mac_filter(ADD "));
00188       ip4_addr_debug_print_val(IGMP_DEBUG, allsystems);
00189       LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", (void*)netif));
00190       netif->igmp_mac_filter(netif, &allsystems, IGMP_ADD_MAC_FILTER);
00191     }
00192 
00193     return ERR_OK;
00194   }
00195 
00196   return ERR_MEM;
00197 }
00198 
00199 /**
00200  * Stop IGMP processing on interface
00201  *
00202  * @param netif network interface on which stop IGMP processing
00203  */
00204 err_t
00205 igmp_stop(struct netif *netif)
00206 {
00207   struct igmp_group *group = igmp_group_list;
00208   struct igmp_group *prev  = NULL;
00209   struct igmp_group *next;
00210 
00211   /* look for groups joined on this interface further down the list */
00212   while (group != NULL) {
00213     next = group->next;
00214     /* is it a group joined on this interface? */
00215     if (group->netif == netif) {
00216       /* is it the first group of the list? */
00217       if (group == igmp_group_list) {
00218         igmp_group_list = next;
00219       }
00220       /* is there a "previous" group defined? */
00221       if (prev != NULL) {
00222         prev->next = next;
00223       }
00224       /* disable the group at the MAC level */
00225       if (netif->igmp_mac_filter != NULL) {
00226         LWIP_DEBUGF(IGMP_DEBUG, ("igmp_stop: igmp_mac_filter(DEL "));
00227         ip4_addr_debug_print(IGMP_DEBUG, &group->group_address);
00228         LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", (void*)netif));
00229         netif->igmp_mac_filter(netif, &(group->group_address), IGMP_DEL_MAC_FILTER);
00230       }
00231       /* free group */
00232       memp_free(MEMP_IGMP_GROUP, group);
00233     } else {
00234       /* change the "previous" */
00235       prev = group;
00236     }
00237     /* move to "next" */
00238     group = next;
00239   }
00240   return ERR_OK;
00241 }
00242 
00243 /**
00244  * Report IGMP memberships for this interface
00245  *
00246  * @param netif network interface on which report IGMP memberships
00247  */
00248 void
00249 igmp_report_groups(struct netif *netif)
00250 {
00251   struct igmp_group *group = igmp_group_list;
00252 
00253   LWIP_DEBUGF(IGMP_DEBUG, ("igmp_report_groups: sending IGMP reports on if %p\n", (void*)netif));
00254 
00255   while (group != NULL) {
00256     if ((group->netif == netif) && (!(ip4_addr_cmp(&(group->group_address), &allsystems)))) {
00257       igmp_delaying_member(group, IGMP_JOIN_DELAYING_MEMBER_TMR);
00258     }
00259     group = group->next;
00260   }
00261 }
00262 
00263 /**
00264  * Search for a group in the global igmp_group_list
00265  *
00266  * @param ifp the network interface for which to look
00267  * @param addr the group ip address to search for
00268  * @return a struct igmp_group* if the group has been found,
00269  *         NULL if the group wasn't found.
00270  */
00271 struct igmp_group *
00272 igmp_lookfor_group(struct netif *ifp, const ip4_addr_t *addr)
00273 {
00274   struct igmp_group *group = igmp_group_list;
00275 
00276   while (group != NULL) {
00277     if ((group->netif == ifp) && (ip4_addr_cmp(&(group->group_address), addr))) {
00278       return group;
00279     }
00280     group = group->next;
00281   }
00282 
00283   /* to be clearer, we return NULL here instead of
00284    * 'group' (which is also NULL at this point).
00285    */
00286   return NULL;
00287 }
00288 
00289 /**
00290  * Search for a specific igmp group and create a new one if not found-
00291  *
00292  * @param ifp the network interface for which to look
00293  * @param addr the group ip address to search
00294  * @return a struct igmp_group*,
00295  *         NULL on memory error.
00296  */
00297 struct igmp_group *
00298 igmp_lookup_group(struct netif *ifp, const ip4_addr_t *addr)
00299 {
00300   struct igmp_group *group;
00301 
00302   /* Search if the group already exists */
00303   group = igmp_lookfor_group(ifp, addr);
00304   if (group != NULL) {
00305     /* Group already exists. */
00306     return group;
00307   }
00308 
00309   /* Group doesn't exist yet, create a new one */
00310   group = (struct igmp_group *)memp_malloc(MEMP_IGMP_GROUP);
00311   if (group != NULL) {
00312     group->netif              = ifp;
00313     ip4_addr_set(&(group->group_address), addr);
00314     group->timer              = 0; /* Not running */
00315     group->group_state        = IGMP_GROUP_NON_MEMBER;
00316     group->last_reporter_flag = 0;
00317     group->use                = 0;
00318     group->next               = igmp_group_list;
00319 
00320     igmp_group_list = group;
00321   }
00322 
00323   LWIP_DEBUGF(IGMP_DEBUG, ("igmp_lookup_group: %sallocated a new group with address ", (group?"":"impossible to ")));
00324   ip4_addr_debug_print(IGMP_DEBUG, addr);
00325   LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", (void*)ifp));
00326 
00327   return group;
00328 }
00329 
00330 /**
00331  * Remove a group in the global igmp_group_list
00332  *
00333  * @param group the group to remove from the global igmp_group_list
00334  * @return ERR_OK if group was removed from the list, an err_t otherwise
00335  */
00336 static err_t
00337 igmp_remove_group(struct igmp_group *group)
00338 {
00339   err_t err = ERR_OK;
00340 
00341   /* Is it the first group? */
00342   if (igmp_group_list == group) {
00343     igmp_group_list = group->next;
00344   } else {
00345     /* look for group further down the list */
00346     struct igmp_group *tmpGroup;
00347     for (tmpGroup = igmp_group_list; tmpGroup != NULL; tmpGroup = tmpGroup->next) {
00348       if (tmpGroup->next == group) {
00349         tmpGroup->next = group->next;
00350         break;
00351       }
00352     }
00353     /* Group not found in the global igmp_group_list */
00354     if (tmpGroup == NULL) {
00355       err = ERR_ARG;
00356     }
00357   }
00358   /* free group */
00359   memp_free(MEMP_IGMP_GROUP, group);
00360 
00361   return err;
00362 }
00363 
00364 /**
00365  * Called from ip_input() if a new IGMP packet is received.
00366  *
00367  * @param p received igmp packet, p->payload pointing to the igmp header
00368  * @param inp network interface on which the packet was received
00369  * @param dest destination ip address of the igmp packet
00370  */
00371 void
00372 igmp_input(struct pbuf *p, struct netif *inp, const ip4_addr_t *dest)
00373 {
00374   struct igmp_msg*   igmp;
00375   struct igmp_group* group;
00376   struct igmp_group* groupref;
00377 
00378   IGMP_STATS_INC(igmp.recv);
00379 
00380   /* Note that the length CAN be greater than 8 but only 8 are used - All are included in the checksum */
00381   if (p->len < IGMP_MINLEN) {
00382     pbuf_free(p);
00383     IGMP_STATS_INC(igmp.lenerr);
00384     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: length error\n"));
00385     return;
00386   }
00387 
00388   LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: message from "));
00389   ip4_addr_debug_print(IGMP_DEBUG, &(ip4_current_header()->src));
00390   LWIP_DEBUGF(IGMP_DEBUG, (" to address "));
00391   ip4_addr_debug_print(IGMP_DEBUG, &(ip4_current_header()->dest));
00392   LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", (void*)inp));
00393 
00394   /* Now calculate and check the checksum */
00395   igmp = (struct igmp_msg *)p->payload;
00396   if (inet_chksum(igmp, p->len)) {
00397     pbuf_free(p);
00398     IGMP_STATS_INC(igmp.chkerr);
00399     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: checksum error\n"));
00400     return;
00401   }
00402 
00403   /* Packet is ok so find an existing group */
00404   group = igmp_lookfor_group(inp, dest); /* use the destination IP address of incoming packet */
00405 
00406   /* If group can be found or create... */
00407   if (!group) {
00408     pbuf_free(p);
00409     IGMP_STATS_INC(igmp.drop);
00410     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP frame not for us\n"));
00411     return;
00412   }
00413 
00414   /* NOW ACT ON THE INCOMING MESSAGE TYPE... */
00415   switch (igmp->igmp_msgtype) {
00416   case IGMP_MEMB_QUERY:
00417     /* IGMP_MEMB_QUERY to the "all systems" address ? */
00418     if ((ip4_addr_cmp(dest, &allsystems)) && ip4_addr_isany(&igmp->igmp_group_address)) {
00419       /* THIS IS THE GENERAL QUERY */
00420       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)));
00421 
00422       if (igmp->igmp_maxresp == 0) {
00423         IGMP_STATS_INC(igmp.rx_v1);
00424         LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: got an all hosts query with time== 0 - this is V1 and not implemented - treat as v2\n"));
00425         igmp->igmp_maxresp = IGMP_V1_DELAYING_MEMBER_TMR;
00426       } else {
00427         IGMP_STATS_INC(igmp.rx_general);
00428       }
00429 
00430       groupref = igmp_group_list;
00431       while (groupref) {
00432         /* Do not send messages on the all systems group address! */
00433         if ((groupref->netif == inp) && (!(ip4_addr_cmp(&(groupref->group_address), &allsystems)))) {
00434           igmp_delaying_member(groupref, igmp->igmp_maxresp);
00435         }
00436         groupref = groupref->next;
00437       }
00438     } else {
00439       /* IGMP_MEMB_QUERY to a specific group ? */
00440       if (!ip4_addr_isany(&igmp->igmp_group_address)) {
00441         LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_MEMB_QUERY to a specific group "));
00442         ip4_addr_debug_print(IGMP_DEBUG, &igmp->igmp_group_address);
00443         if (ip4_addr_cmp(dest, &allsystems)) {
00444           ip4_addr_t groupaddr;
00445           LWIP_DEBUGF(IGMP_DEBUG, (" using \"ALL SYSTEMS\" address (224.0.0.1) [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp)));
00446           /* we first need to re-look for the group since we used dest last time */
00447           ip4_addr_copy(groupaddr, igmp->igmp_group_address);
00448           group = igmp_lookfor_group(inp, &groupaddr);
00449         } else {
00450           LWIP_DEBUGF(IGMP_DEBUG, (" with the group address as destination [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp)));
00451         }
00452 
00453         if (group != NULL) {
00454           IGMP_STATS_INC(igmp.rx_group);
00455           igmp_delaying_member(group, igmp->igmp_maxresp);
00456         } else {
00457           IGMP_STATS_INC(igmp.drop);
00458         }
00459       } else {
00460         IGMP_STATS_INC(igmp.proterr);
00461       }
00462     }
00463     break;
00464   case IGMP_V2_MEMB_REPORT:
00465     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_V2_MEMB_REPORT\n"));
00466     IGMP_STATS_INC(igmp.rx_report);
00467     if (group->group_state == IGMP_GROUP_DELAYING_MEMBER) {
00468       /* This is on a specific group we have already looked up */
00469       group->timer = 0; /* stopped */
00470       group->group_state = IGMP_GROUP_IDLE_MEMBER;
00471       group->last_reporter_flag = 0;
00472     }
00473     break;
00474   default:
00475     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: unexpected msg %d in state %d on group %p on if %p\n",
00476       igmp->igmp_msgtype, group->group_state, (void*)&group, (void*)group->netif));
00477     IGMP_STATS_INC(igmp.proterr);
00478     break;
00479   }
00480 
00481   pbuf_free(p);
00482   return;
00483 }
00484 
00485 /**
00486  * @ingroup igmp
00487  * Join a group on one network interface.
00488  *
00489  * @param ifaddr ip address of the network interface which should join a new group
00490  * @param groupaddr the ip address of the group which to join
00491  * @return ERR_OK if group was joined on the netif(s), an err_t otherwise
00492  */
00493 err_t
00494 igmp_joingroup(const ip4_addr_t *ifaddr, const ip4_addr_t *groupaddr)
00495 {
00496   err_t err = ERR_VAL; /* no matching interface */
00497   struct netif *netif;
00498 
00499   /* make sure it is multicast address */
00500   LWIP_ERROR("igmp_joingroup: attempt to join non-multicast address", ip4_addr_ismulticast(groupaddr), return ERR_VAL;);
00501   LWIP_ERROR("igmp_joingroup: attempt to join allsystems address", (!ip4_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;);
00502 
00503   /* loop through netif's */
00504   netif = netif_list;
00505   while (netif != NULL) {
00506     /* Should we join this interface ? */
00507     if ((netif->flags & NETIF_FLAG_IGMP) && ((ip4_addr_isany(ifaddr) || ip4_addr_cmp(netif_ip4_addr(netif), ifaddr)))) {
00508       err = igmp_joingroup_netif(netif, groupaddr);
00509       if (err != ERR_OK) {
00510         /* Return an error even if some network interfaces are joined */
00511         /** @todo undo any other netif already joined */
00512         return err;
00513       }
00514     }
00515     /* proceed to next network interface */
00516     netif = netif->next;
00517   }
00518 
00519   return err;
00520 }
00521 
00522 /**
00523  * @ingroup igmp
00524  * Join a group on one network interface.
00525  *
00526  * @param netif the network interface which should join a new group
00527  * @param groupaddr the ip address of the group which to join
00528  * @return ERR_OK if group was joined on the netif, an err_t otherwise
00529  */
00530 err_t
00531 igmp_joingroup_netif(struct netif *netif, const ip4_addr_t *groupaddr)
00532 {
00533   struct igmp_group *group;
00534 
00535   /* make sure it is multicast address */
00536   LWIP_ERROR("igmp_joingroup_netif: attempt to join non-multicast address", ip4_addr_ismulticast(groupaddr), return ERR_VAL;);
00537   LWIP_ERROR("igmp_joingroup_netif: attempt to join allsystems address", (!ip4_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;);
00538 
00539   /* make sure it is an igmp-enabled netif */
00540   LWIP_ERROR("igmp_joingroup_netif: attempt to join on non-IGMP netif", netif->flags & NETIF_FLAG_IGMP, return ERR_VAL;);
00541 
00542   /* find group or create a new one if not found */
00543   group = igmp_lookup_group(netif, groupaddr);
00544 
00545   if (group != NULL) {
00546     /* This should create a new group, check the state to make sure */
00547     if (group->group_state != IGMP_GROUP_NON_MEMBER) {
00548       LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup_netif: join to group not in state IGMP_GROUP_NON_MEMBER\n"));
00549     } else {
00550       /* OK - it was new group */
00551       LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup_netif: join to new group: "));
00552       ip4_addr_debug_print(IGMP_DEBUG, groupaddr);
00553       LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
00554 
00555       /* If first use of the group, allow the group at the MAC level */
00556       if ((group->use==0) && (netif->igmp_mac_filter != NULL)) {
00557         LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup_netif: igmp_mac_filter(ADD "));
00558         ip4_addr_debug_print(IGMP_DEBUG, groupaddr);
00559         LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", (void*)netif));
00560         netif->igmp_mac_filter(netif, groupaddr, IGMP_ADD_MAC_FILTER);
00561       }
00562 
00563       IGMP_STATS_INC(igmp.tx_join);
00564       igmp_send(group, IGMP_V2_MEMB_REPORT);
00565 
00566       igmp_start_timer(group, IGMP_JOIN_DELAYING_MEMBER_TMR);
00567 
00568       /* Need to work out where this timer comes from */
00569       group->group_state = IGMP_GROUP_DELAYING_MEMBER;
00570     }
00571     /* Increment group use */
00572     group->use++;
00573     /* Join on this interface */
00574     return ERR_OK;
00575   } else {
00576     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup_netif: Not enough memory to join to group\n"));
00577     return ERR_MEM;
00578   }
00579 }
00580 
00581 /**
00582  * @ingroup igmp
00583  * Leave a group on one network interface.
00584  *
00585  * @param ifaddr ip address of the network interface which should leave a group
00586  * @param groupaddr the ip address of the group which to leave
00587  * @return ERR_OK if group was left on the netif(s), an err_t otherwise
00588  */
00589 err_t
00590 igmp_leavegroup(const ip4_addr_t *ifaddr, const ip4_addr_t *groupaddr)
00591 {
00592   err_t err = ERR_VAL; /* no matching interface */
00593   struct netif *netif;
00594 
00595   /* make sure it is multicast address */
00596   LWIP_ERROR("igmp_leavegroup: attempt to leave non-multicast address", ip4_addr_ismulticast(groupaddr), return ERR_VAL;);
00597   LWIP_ERROR("igmp_leavegroup: attempt to leave allsystems address", (!ip4_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;);
00598 
00599   /* loop through netif's */
00600   netif = netif_list;
00601   while (netif != NULL) {
00602     /* Should we leave this interface ? */
00603     if ((netif->flags & NETIF_FLAG_IGMP) && ((ip4_addr_isany(ifaddr) || ip4_addr_cmp(netif_ip4_addr(netif), ifaddr)))) {
00604       err_t res = igmp_leavegroup_netif(netif, groupaddr);
00605       if (err != ERR_OK) {
00606         /* Store this result if we have not yet gotten a success */
00607         err = res;
00608       }
00609     }
00610     /* proceed to next network interface */
00611     netif = netif->next;
00612   }
00613 
00614   return err;
00615 }
00616 
00617 /**
00618  * @ingroup igmp
00619  * Leave a group on one network interface.
00620  *
00621  * @param netif the network interface which should leave a group
00622  * @param groupaddr the ip address of the group which to leave
00623  * @return ERR_OK if group was left on the netif, an err_t otherwise
00624  */
00625 err_t
00626 igmp_leavegroup_netif(struct netif *netif, const ip4_addr_t *groupaddr)
00627 {
00628   struct igmp_group *group;
00629 
00630   /* make sure it is multicast address */
00631   LWIP_ERROR("igmp_leavegroup_netif: attempt to leave non-multicast address", ip4_addr_ismulticast(groupaddr), return ERR_VAL;);
00632   LWIP_ERROR("igmp_leavegroup_netif: attempt to leave allsystems address", (!ip4_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;);
00633 
00634   /* make sure it is an igmp-enabled netif */
00635   LWIP_ERROR("igmp_leavegroup_netif: attempt to leave on non-IGMP netif", netif->flags & NETIF_FLAG_IGMP, return ERR_VAL;);
00636 
00637   /* find group */
00638   group = igmp_lookfor_group(netif, groupaddr);
00639 
00640   if (group != NULL) {
00641     /* Only send a leave if the flag is set according to the state diagram */
00642     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup_netif: Leaving group: "));
00643     ip4_addr_debug_print(IGMP_DEBUG, groupaddr);
00644     LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
00645 
00646     /* If there is no other use of the group */
00647     if (group->use <= 1) {
00648       /* If we are the last reporter for this group */
00649       if (group->last_reporter_flag) {
00650         LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup_netif: sending leaving group\n"));
00651         IGMP_STATS_INC(igmp.tx_leave);
00652         igmp_send(group, IGMP_LEAVE_GROUP);
00653       }
00654 
00655       /* Disable the group at the MAC level */
00656       if (netif->igmp_mac_filter != NULL) {
00657         LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup_netif: igmp_mac_filter(DEL "));
00658         ip4_addr_debug_print(IGMP_DEBUG, groupaddr);
00659         LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", (void*)netif));
00660         netif->igmp_mac_filter(netif, groupaddr, IGMP_DEL_MAC_FILTER);
00661       }
00662 
00663       LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup_netif: remove group: "));
00664       ip4_addr_debug_print(IGMP_DEBUG, groupaddr);
00665       LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
00666 
00667       /* Free the group */
00668       igmp_remove_group(group);
00669     } else {
00670       /* Decrement group use */
00671       group->use--;
00672     }
00673     return ERR_OK;
00674   } else {
00675     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup_netif: not member of group\n"));
00676     return ERR_VAL;
00677   }
00678 }
00679 
00680 /**
00681  * The igmp timer function (both for NO_SYS=1 and =0)
00682  * Should be called every IGMP_TMR_INTERVAL milliseconds (100 ms is default).
00683  */
00684 void
00685 igmp_tmr(void)
00686 {
00687   struct igmp_group *group = igmp_group_list;
00688 
00689   while (group != NULL) {
00690     if (group->timer > 0) {
00691       group->timer--;
00692       if (group->timer == 0) {
00693         igmp_timeout(group);
00694       }
00695     }
00696     group = group->next;
00697   }
00698 }
00699 
00700 /**
00701  * Called if a timeout for one group is reached.
00702  * Sends a report for this group.
00703  *
00704  * @param group an igmp_group for which a timeout is reached
00705  */
00706 static void
00707 igmp_timeout(struct igmp_group *group)
00708 {
00709   /* If the state is IGMP_GROUP_DELAYING_MEMBER then we send a report for this group
00710      (unless it is the allsystems group) */
00711   if ((group->group_state == IGMP_GROUP_DELAYING_MEMBER) &&
00712       (!(ip4_addr_cmp(&(group->group_address), &allsystems)))) {
00713     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_timeout: report membership for group with address "));
00714     ip4_addr_debug_print(IGMP_DEBUG, &(group->group_address));
00715     LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", (void*)group->netif));
00716 
00717     IGMP_STATS_INC(igmp.tx_report);
00718     igmp_send(group, IGMP_V2_MEMB_REPORT);
00719   }
00720 }
00721 
00722 /**
00723  * Start a timer for an igmp group
00724  *
00725  * @param group the igmp_group for which to start a timer
00726  * @param max_time the time in multiples of IGMP_TMR_INTERVAL (decrease with
00727  *        every call to igmp_tmr())
00728  */
00729 static void
00730 igmp_start_timer(struct igmp_group *group, u8_t max_time)
00731 {
00732 #ifdef LWIP_RAND
00733   group->timer = max_time > 2 ? (LWIP_RAND() % max_time) : 1;
00734 #else /* LWIP_RAND */
00735   /* ATTENTION: use this only if absolutely necessary! */
00736   group->timer = max_time / 2;
00737 #endif /* LWIP_RAND */
00738 
00739   if (group->timer == 0) {
00740     group->timer = 1;
00741   }
00742 }
00743 
00744 /**
00745  * Delaying membership report for a group if necessary
00746  *
00747  * @param group the igmp_group for which "delaying" membership report
00748  * @param maxresp query delay
00749  */
00750 static void
00751 igmp_delaying_member(struct igmp_group *group, u8_t maxresp)
00752 {
00753   if ((group->group_state == IGMP_GROUP_IDLE_MEMBER) ||
00754      ((group->group_state == IGMP_GROUP_DELAYING_MEMBER) &&
00755       ((group->timer == 0) || (maxresp < group->timer)))) {
00756     igmp_start_timer(group, maxresp);
00757     group->group_state = IGMP_GROUP_DELAYING_MEMBER;
00758   }
00759 }
00760 
00761 
00762 /**
00763  * Sends an IP packet on a network interface. This function constructs the IP header
00764  * and calculates the IP header checksum. If the source IP address is NULL,
00765  * the IP address of the outgoing network interface is filled in as source address.
00766  *
00767  * @param p the packet to send (p->payload points to the data, e.g. next
00768             protocol header; if dest == IP_HDRINCL, p already includes an IP
00769             header and p->payload points to that IP header)
00770  * @param src the source IP address to send from (if src == IP_ADDR_ANY, the
00771  *         IP  address of the netif used to send is used as source address)
00772  * @param dest the destination IP address to send the packet to
00773  * @param ttl the TTL value to be set in the IP header
00774  * @param proto the PROTOCOL to be set in the IP header
00775  * @param netif the netif on which to send this packet
00776  * @return ERR_OK if the packet was sent OK
00777  *         ERR_BUF if p doesn't have enough space for IP/LINK headers
00778  *         returns errors returned by netif->output
00779  */
00780 static err_t
00781 igmp_ip_output_if(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest, struct netif *netif)
00782 {
00783   /* This is the "router alert" option */
00784   u16_t ra[2];
00785   ra[0] = PP_HTONS(ROUTER_ALERT);
00786   ra[1] = 0x0000; /* Router shall examine packet */
00787   IGMP_STATS_INC(igmp.xmit);
00788   return ip4_output_if_opt(p, src, dest, IGMP_TTL, 0, IP_PROTO_IGMP, netif, ra, ROUTER_ALERTLEN);
00789 }
00790 
00791 /**
00792  * Send an igmp packet to a specific group.
00793  *
00794  * @param group the group to which to send the packet
00795  * @param type the type of igmp packet to send
00796  */
00797 static void
00798 igmp_send(struct igmp_group *group, u8_t type)
00799 {
00800   struct pbuf*     p    = NULL;
00801   struct igmp_msg* igmp = NULL;
00802   ip4_addr_t   src  = *IP4_ADDR_ANY;
00803   ip4_addr_t*  dest = NULL;
00804 
00805   /* IP header + "router alert" option + IGMP header */
00806   p = pbuf_alloc(PBUF_TRANSPORT, IGMP_MINLEN, PBUF_RAM);
00807 
00808   if (p) {
00809     igmp = (struct igmp_msg *)p->payload;
00810     LWIP_ASSERT("igmp_send: check that first pbuf can hold struct igmp_msg",
00811                (p->len >= sizeof(struct igmp_msg)));
00812     ip4_addr_copy(src, *netif_ip4_addr(group->netif));
00813 
00814     if (type == IGMP_V2_MEMB_REPORT) {
00815       dest = &(group->group_address);
00816       ip4_addr_copy(igmp->igmp_group_address, group->group_address);
00817       group->last_reporter_flag = 1; /* Remember we were the last to report */
00818     } else {
00819       if (type == IGMP_LEAVE_GROUP) {
00820         dest = &allrouters;
00821         ip4_addr_copy(igmp->igmp_group_address, group->group_address);
00822       }
00823     }
00824 
00825     if ((type == IGMP_V2_MEMB_REPORT) || (type == IGMP_LEAVE_GROUP)) {
00826       igmp->igmp_msgtype  = type;
00827       igmp->igmp_maxresp  = 0;
00828       igmp->igmp_checksum = 0;
00829       igmp->igmp_checksum = inet_chksum(igmp, IGMP_MINLEN);
00830 
00831       igmp_ip_output_if(p, &src, dest, group->netif);
00832     }
00833 
00834     pbuf_free(p);
00835   } else {
00836     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_send: not enough memory for igmp_send\n"));
00837     IGMP_STATS_INC(igmp.memerr);
00838   }
00839 }
00840 
00841 #endif /* LWIP_IPV4 && LWIP_IGMP */