NetServices Stack source

Dependents:   HelloWorld ServoInterfaceBoardExample1 4180_Lab4

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