Version of http://mbed.org/cookbook/NetServicesTribute with setting set the same for LPC2368

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