A version of LWIP, provided for backwards compatibility.

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