Ethernet for Nucleo and Disco board STM32F746 works with gcc and arm. IAC is untested

Dependents:   STM32F746_iothub_client_sample_mqtt DISCO-F746NG_Ethernet Nucleo_F746ZG_Ethernet thethingsiO-DISCO_F746NG-mqtt ... more

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers igmp.c Source File

igmp.c

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