Kenji Arai / mbed-os_TYBLE16

Dependents:   TYBLE16_simple_data_logger TYBLE16_MP3_Air

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