STM32F7 Ethernet interface for nucleo STM32F767

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(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 global 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 in the global igmp_group_list, but don't free it yet
00290  *
00291  * @param group the group to remove from the global 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 the global 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(IGMP_DEBUG, &(ip4_current_header()->src));
00341   LWIP_DEBUGF(IGMP_DEBUG, (" to address "));
00342   ip4_addr_debug_print(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(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   /* make sure it is multicast address */
00455   LWIP_ERROR("igmp_joingroup: attempt to join non-multicast address", ip4_addr_ismulticast(groupaddr), return ERR_VAL;);
00456   LWIP_ERROR("igmp_joingroup: attempt to join allsystems address", (!ip4_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;);
00457 
00458   /* loop through netif's */
00459   netif = netif_list;
00460   while (netif != NULL) {
00461     /* Should we join this interface ? */
00462     if ((netif->flags & NETIF_FLAG_IGMP) && ((ip4_addr_isany(ifaddr) || ip4_addr_cmp(netif_ip4_addr(netif), ifaddr)))) {
00463       err = igmp_joingroup_netif(netif, groupaddr);
00464       if (err != ERR_OK) {
00465         /* Return an error even if some network interfaces are joined */
00466         /** @todo undo any other netif already joined */
00467         return err;
00468       }
00469     }
00470     /* proceed to next network interface */
00471     netif = netif->next;
00472   }
00473 
00474   return err;
00475 }
00476 
00477 /**
00478  * @ingroup igmp
00479  * Join a group on one network interface.
00480  *
00481  * @param netif the network interface which should join a new group
00482  * @param groupaddr the ip address of the group which to join
00483  * @return ERR_OK if group was joined on the netif, an err_t otherwise
00484  */
00485 err_t
00486 igmp_joingroup_netif(struct netif *netif, const ip4_addr_t *groupaddr)
00487 {
00488   struct igmp_group *group;
00489 
00490   /* make sure it is multicast address */
00491   LWIP_ERROR("igmp_joingroup_netif: attempt to join non-multicast address", ip4_addr_ismulticast(groupaddr), return ERR_VAL;);
00492   LWIP_ERROR("igmp_joingroup_netif: attempt to join allsystems address", (!ip4_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;);
00493 
00494   /* make sure it is an igmp-enabled netif */
00495   LWIP_ERROR("igmp_joingroup_netif: attempt to join on non-IGMP netif", netif->flags & NETIF_FLAG_IGMP, return ERR_VAL;);
00496 
00497   /* find group or create a new one if not found */
00498   group = igmp_lookup_group(netif, groupaddr);
00499 
00500   if (group != NULL) {
00501     /* This should create a new group, check the state to make sure */
00502     if (group->group_state != IGMP_GROUP_NON_MEMBER) {
00503       LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup_netif: join to group not in state IGMP_GROUP_NON_MEMBER\n"));
00504     } else {
00505       /* OK - it was new group */
00506       LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup_netif: join to new group: "));
00507       ip4_addr_debug_print(IGMP_DEBUG, groupaddr);
00508       LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
00509 
00510       /* If first use of the group, allow the group at the MAC level */
00511       if ((group->use==0) && (netif->igmp_mac_filter != NULL)) {
00512         LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup_netif: igmp_mac_filter(ADD "));
00513         ip4_addr_debug_print(IGMP_DEBUG, groupaddr);
00514         LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", (void*)netif));
00515         netif->igmp_mac_filter(netif, groupaddr, NETIF_ADD_MAC_FILTER);
00516       }
00517 
00518       IGMP_STATS_INC(igmp.tx_join);
00519       igmp_send(netif, group, IGMP_V2_MEMB_REPORT);
00520 
00521       igmp_start_timer(group, IGMP_JOIN_DELAYING_MEMBER_TMR);
00522 
00523       /* Need to work out where this timer comes from */
00524       group->group_state = IGMP_GROUP_DELAYING_MEMBER;
00525     }
00526     /* Increment group use */
00527     group->use++;
00528     /* Join on this interface */
00529     return ERR_OK;
00530   } else {
00531     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup_netif: Not enough memory to join to group\n"));
00532     return ERR_MEM;
00533   }
00534 }
00535 
00536 /**
00537  * @ingroup igmp
00538  * Leave a group on one network interface.
00539  *
00540  * @param ifaddr ip address of the network interface which should leave a group
00541  * @param groupaddr the ip address of the group which to leave
00542  * @return ERR_OK if group was left on the netif(s), an err_t otherwise
00543  */
00544 err_t
00545 igmp_leavegroup(const ip4_addr_t *ifaddr, const ip4_addr_t *groupaddr)
00546 {
00547   err_t err = ERR_VAL; /* no matching interface */
00548   struct netif *netif;
00549 
00550   /* make sure it is multicast address */
00551   LWIP_ERROR("igmp_leavegroup: attempt to leave non-multicast address", ip4_addr_ismulticast(groupaddr), return ERR_VAL;);
00552   LWIP_ERROR("igmp_leavegroup: attempt to leave allsystems address", (!ip4_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;);
00553 
00554   /* loop through netif's */
00555   netif = netif_list;
00556   while (netif != NULL) {
00557     /* Should we leave this interface ? */
00558     if ((netif->flags & NETIF_FLAG_IGMP) && ((ip4_addr_isany(ifaddr) || ip4_addr_cmp(netif_ip4_addr(netif), ifaddr)))) {
00559       err_t res = igmp_leavegroup_netif(netif, groupaddr);
00560       if (err != ERR_OK) {
00561         /* Store this result if we have not yet gotten a success */
00562         err = res;
00563       }
00564     }
00565     /* proceed to next network interface */
00566     netif = netif->next;
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   /* make sure it is multicast address */
00586   LWIP_ERROR("igmp_leavegroup_netif: attempt to leave non-multicast address", ip4_addr_ismulticast(groupaddr), return ERR_VAL;);
00587   LWIP_ERROR("igmp_leavegroup_netif: attempt to leave allsystems address", (!ip4_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;);
00588 
00589   /* make sure it is an igmp-enabled netif */
00590   LWIP_ERROR("igmp_leavegroup_netif: attempt to leave on non-IGMP netif", netif->flags & NETIF_FLAG_IGMP, return ERR_VAL;);
00591 
00592   /* find group */
00593   group = igmp_lookfor_group(netif, groupaddr);
00594 
00595   if (group != NULL) {
00596     /* Only send a leave if the flag is set according to the state diagram */
00597     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup_netif: Leaving group: "));
00598     ip4_addr_debug_print(IGMP_DEBUG, groupaddr);
00599     LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
00600 
00601     /* If there is no other use of the group */
00602     if (group->use <= 1) {
00603       /* Remove the group from the list */
00604       igmp_remove_group(netif, group);
00605 
00606       /* If we are the last reporter for this group */
00607       if (group->last_reporter_flag) {
00608         LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup_netif: sending leaving group\n"));
00609         IGMP_STATS_INC(igmp.tx_leave);
00610         igmp_send(netif, group, IGMP_LEAVE_GROUP);
00611       }
00612 
00613       /* Disable the group at the MAC level */
00614       if (netif->igmp_mac_filter != NULL) {
00615         LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup_netif: igmp_mac_filter(DEL "));
00616         ip4_addr_debug_print(IGMP_DEBUG, groupaddr);
00617         LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", (void*)netif));
00618         netif->igmp_mac_filter(netif, groupaddr, NETIF_DEL_MAC_FILTER);
00619       }
00620 
00621       /* Free group struct */
00622       memp_free(MEMP_IGMP_GROUP, group);
00623     } else {
00624       /* Decrement group use */
00625       group->use--;
00626     }
00627     return ERR_OK;
00628   } else {
00629     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup_netif: not member of group\n"));
00630     return ERR_VAL;
00631   }
00632 }
00633 
00634 /**
00635  * The igmp timer function (both for NO_SYS=1 and =0)
00636  * Should be called every IGMP_TMR_INTERVAL milliseconds (100 ms is default).
00637  */
00638 void
00639 igmp_tmr(void)
00640 {
00641   struct netif *netif = netif_list;
00642 
00643   while (netif != NULL) {
00644     struct igmp_group *group = netif_igmp_data(netif);
00645 
00646     while (group != NULL) {
00647       if (group->timer > 0) {
00648         group->timer--;
00649         if (group->timer == 0) {
00650           igmp_timeout(netif, group);
00651         }
00652       }
00653       group = group->next;
00654     }
00655     netif = netif->next;
00656   }
00657 }
00658 
00659 /**
00660  * Called if a timeout for one group is reached.
00661  * Sends a report for this group.
00662  *
00663  * @param group an igmp_group for which a timeout is reached
00664  */
00665 static void
00666 igmp_timeout(struct netif *netif, struct igmp_group *group)
00667 {
00668   /* If the state is IGMP_GROUP_DELAYING_MEMBER then we send a report for this group
00669      (unless it is the allsystems group) */
00670   if ((group->group_state == IGMP_GROUP_DELAYING_MEMBER) &&
00671       (!(ip4_addr_cmp(&(group->group_address), &allsystems)))) {
00672     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_timeout: report membership for group with address "));
00673     ip4_addr_debug_print(IGMP_DEBUG, &(group->group_address));
00674     LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", (void*)netif));
00675 
00676     group->group_state = IGMP_GROUP_IDLE_MEMBER;
00677     
00678     IGMP_STATS_INC(igmp.tx_report);
00679     igmp_send(netif, group, IGMP_V2_MEMB_REPORT);
00680   }
00681 }
00682 
00683 /**
00684  * Start a timer for an igmp group
00685  *
00686  * @param group the igmp_group for which to start a timer
00687  * @param max_time the time in multiples of IGMP_TMR_INTERVAL (decrease with
00688  *        every call to igmp_tmr())
00689  */
00690 static void
00691 igmp_start_timer(struct igmp_group *group, u8_t max_time)
00692 {
00693 #ifdef LWIP_RAND
00694   group->timer = max_time > 2 ? (LWIP_RAND() % max_time) : 1;
00695 #else /* LWIP_RAND */
00696   /* ATTENTION: use this only if absolutely necessary! */
00697   group->timer = max_time / 2;
00698 #endif /* LWIP_RAND */
00699 
00700   if (group->timer == 0) {
00701     group->timer = 1;
00702   }
00703 }
00704 
00705 /**
00706  * Delaying membership report for a group if necessary
00707  *
00708  * @param group the igmp_group for which "delaying" membership report
00709  * @param maxresp query delay
00710  */
00711 static void
00712 igmp_delaying_member(struct igmp_group *group, u8_t maxresp)
00713 {
00714   if ((group->group_state == IGMP_GROUP_IDLE_MEMBER) ||
00715      ((group->group_state == IGMP_GROUP_DELAYING_MEMBER) &&
00716       ((group->timer == 0) || (maxresp < group->timer)))) {
00717     igmp_start_timer(group, maxresp);
00718     group->group_state = IGMP_GROUP_DELAYING_MEMBER;
00719   }
00720 }
00721 
00722 
00723 /**
00724  * Sends an IP packet on a network interface. This function constructs the IP header
00725  * and calculates the IP header checksum. If the source IP address is NULL,
00726  * the IP address of the outgoing network interface is filled in as source address.
00727  *
00728  * @param p the packet to send (p->payload points to the data, e.g. next
00729             protocol header; if dest == LWIP_IP_HDRINCL, p already includes an
00730             IP header and p->payload points to that IP header)
00731  * @param src the source IP address to send from (if src == IP4_ADDR_ANY, the
00732  *         IP  address of the netif used to send is used as source address)
00733  * @param dest the destination IP address to send the packet to
00734  * @param netif the netif on which to send this packet
00735  * @return ERR_OK if the packet was sent OK
00736  *         ERR_BUF if p doesn't have enough space for IP/LINK headers
00737  *         returns errors returned by netif->output
00738  */
00739 static err_t
00740 igmp_ip_output_if(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest, struct netif *netif)
00741 {
00742   /* This is the "router alert" option */
00743   u16_t ra[2];
00744   ra[0] = PP_HTONS(ROUTER_ALERT);
00745   ra[1] = 0x0000; /* Router shall examine packet */
00746   IGMP_STATS_INC(igmp.xmit);
00747   return ip4_output_if_opt(p, src, dest, IGMP_TTL, 0, IP_PROTO_IGMP, netif, ra, ROUTER_ALERTLEN);
00748 }
00749 
00750 /**
00751  * Send an igmp packet to a specific group.
00752  *
00753  * @param group the group to which to send the packet
00754  * @param type the type of igmp packet to send
00755  */
00756 static void
00757 igmp_send(struct netif *netif, struct igmp_group *group, u8_t type)
00758 {
00759   struct pbuf*     p    = NULL;
00760   struct igmp_msg* igmp = NULL;
00761   ip4_addr_t   src  = *IP4_ADDR_ANY4;
00762   ip4_addr_t*  dest = NULL;
00763 
00764   /* IP header + "router alert" option + IGMP header */
00765   p = pbuf_alloc(PBUF_TRANSPORT, IGMP_MINLEN, PBUF_RAM);
00766 
00767   if (p) {
00768     igmp = (struct igmp_msg *)p->payload;
00769     LWIP_ASSERT("igmp_send: check that first pbuf can hold struct igmp_msg",
00770                (p->len >= sizeof(struct igmp_msg)));
00771     ip4_addr_copy(src, *netif_ip4_addr(netif));
00772 
00773     if (type == IGMP_V2_MEMB_REPORT) {
00774       dest = &(group->group_address);
00775       ip4_addr_copy(igmp->igmp_group_address, group->group_address);
00776       group->last_reporter_flag = 1; /* Remember we were the last to report */
00777     } else {
00778       if (type == IGMP_LEAVE_GROUP) {
00779         dest = &allrouters;
00780         ip4_addr_copy(igmp->igmp_group_address, group->group_address);
00781       }
00782     }
00783 
00784     if ((type == IGMP_V2_MEMB_REPORT) || (type == IGMP_LEAVE_GROUP)) {
00785       igmp->igmp_msgtype  = type;
00786       igmp->igmp_maxresp  = 0;
00787       igmp->igmp_checksum = 0;
00788       igmp->igmp_checksum = inet_chksum(igmp, IGMP_MINLEN);
00789 
00790       igmp_ip_output_if(p, &src, dest, netif);
00791     }
00792 
00793     pbuf_free(p);
00794   } else {
00795     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_send: not enough memory for igmp_send\n"));
00796     IGMP_STATS_INC(igmp.memerr);
00797   }
00798 }
00799 
00800 #endif /* LWIP_IPV4 && LWIP_IGMP */