1/**
2 * @file
3 * IGMP - Internet Group Management Protocol
4 *
5 */
6
7/*
8 * Copyright (c) 2002 CITEL Technologies Ltd.
9 * All rights reserved.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 *    notice, this list of conditions and the following disclaimer in the
18 *    documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of CITEL Technologies Ltd nor the names of its contributors
20 *    may be used to endorse or promote products derived from this software
21 *    without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY CITEL TECHNOLOGIES AND CONTRIBUTORS ``AS IS''
24 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED.  IN NO EVENT SHALL CITEL TECHNOLOGIES OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 *
35 * This file is a contribution to the lwIP TCP/IP stack.
36 * The Swedish Institute of Computer Science and Adam Dunkels
37 * are specifically granted permission to redistribute this
38 * source code.
39*/
40
41/*-------------------------------------------------------------
42Note 1)
43Although the rfc requires V1 AND V2 capability
44we will only support v2 since now V1 is very old (August 1989)
45V1 can be added if required
46
47a debug print and statistic have been implemented to
48show this up.
49-------------------------------------------------------------
50-------------------------------------------------------------
51Note 2)
52A query for a specific group address (as opposed to ALLHOSTS)
53has now been implemented as I am unsure if it is required
54
55a debug print and statistic have been implemented to
56show this up.
57-------------------------------------------------------------
58-------------------------------------------------------------
59Note 3)
60The router alert rfc 2113 is implemented in outgoing packets
61but not checked rigorously incoming
62-------------------------------------------------------------
63Steve Reynolds
64------------------------------------------------------------*/
65
66/*-----------------------------------------------------------------------------
67 * RFC 988  - Host extensions for IP multicasting                         - V0
68 * RFC 1054 - Host extensions for IP multicasting                         -
69 * RFC 1112 - Host extensions for IP multicasting                         - V1
70 * RFC 2236 - Internet Group Management Protocol, Version 2               - V2  <- this code is based on this RFC (it's the "de facto" standard)
71 * RFC 3376 - Internet Group Management Protocol, Version 3               - V3
72 * RFC 4604 - Using Internet Group Management Protocol Version 3...       - V3+
73 * RFC 2113 - IP Router Alert Option                                      -
74 *----------------------------------------------------------------------------*/
75
76/*-----------------------------------------------------------------------------
77 * Includes
78 *----------------------------------------------------------------------------*/
79
80#include "lwip/opt.h"
81
82#if LWIP_IGMP /* don't build if not configured for use in lwipopts.h */
83
84#include "lwip/igmp.h"
85#include "lwip/debug.h"
86#include "lwip/def.h"
87#include "lwip/mem.h"
88#include "lwip/ip.h"
89#include "lwip/inet_chksum.h"
90#include "lwip/netif.h"
91#include "lwip/icmp.h"
92#include "lwip/udp.h"
93#include "lwip/tcp.h"
94#include "lwip/stats.h"
95
96#include "string.h"
97
98/*
99 * IGMP constants
100 */
101#define IGMP_TTL                       1
102#define IGMP_MINLEN                    8
103#define ROUTER_ALERT                   0x9404U
104#define ROUTER_ALERTLEN                4
105
106/*
107 * IGMP message types, including version number.
108 */
109#define IGMP_MEMB_QUERY                0x11 /* Membership query         */
110#define IGMP_V1_MEMB_REPORT            0x12 /* Ver. 1 membership report */
111#define IGMP_V2_MEMB_REPORT            0x16 /* Ver. 2 membership report */
112#define IGMP_LEAVE_GROUP               0x17 /* Leave-group message      */
113
114/* Group  membership states */
115#define IGMP_GROUP_NON_MEMBER          0
116#define IGMP_GROUP_DELAYING_MEMBER     1
117#define IGMP_GROUP_IDLE_MEMBER         2
118
119/**
120 * IGMP packet format.
121 */
122#ifdef PACK_STRUCT_USE_INCLUDES
123#  include "arch/bpstruct.h"
124#endif
125PACK_STRUCT_BEGIN
126struct igmp_msg {
127 PACK_STRUCT_FIELD(u8_t           igmp_msgtype);
128 PACK_STRUCT_FIELD(u8_t           igmp_maxresp);
129 PACK_STRUCT_FIELD(u16_t          igmp_checksum);
130 PACK_STRUCT_FIELD(ip_addr_p_t    igmp_group_address);
131} PACK_STRUCT_STRUCT;
132PACK_STRUCT_END
133#ifdef PACK_STRUCT_USE_INCLUDES
134#  include "arch/epstruct.h"
135#endif
136
137
138static struct igmp_group *igmp_lookup_group(struct netif *ifp, ip_addr_t *addr);
139static err_t  igmp_remove_group(struct igmp_group *group);
140static void   igmp_timeout( struct igmp_group *group);
141static void   igmp_start_timer(struct igmp_group *group, u8_t max_time);
142static void   igmp_stop_timer(struct igmp_group *group);
143static void   igmp_delaying_member(struct igmp_group *group, u8_t maxresp);
144static err_t  igmp_ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, struct netif *netif);
145static void   igmp_send(struct igmp_group *group, u8_t type);
146
147
148static struct igmp_group* igmp_group_list;
149static ip_addr_t     allsystems;
150static ip_addr_t     allrouters;
151
152
153/**
154 * Initialize the IGMP module
155 */
156void
157igmp_init(void)
158{
159  LWIP_DEBUGF(IGMP_DEBUG, ("igmp_init: initializing\n"));
160
161  IP4_ADDR(&allsystems, 224, 0, 0, 1);
162  IP4_ADDR(&allrouters, 224, 0, 0, 2);
163}
164
165#ifdef LWIP_DEBUG
166/**
167 * Dump global IGMP groups list
168 */
169void
170igmp_dump_group_list()
171{
172  struct igmp_group *group = igmp_group_list;
173
174  while (group != NULL) {
175    LWIP_DEBUGF(IGMP_DEBUG, ("igmp_dump_group_list: [%"U32_F"] ", (u32_t)(group->group_state)));
176    ip_addr_debug_print(IGMP_DEBUG, &group->group_address);
177    LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", group->netif));
178    group = group->next;
179  }
180  LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
181}
182#else
183#define igmp_dump_group_list()
184#endif /* LWIP_DEBUG */
185
186/**
187 * Start IGMP processing on interface
188 *
189 * @param netif network interface on which start IGMP processing
190 */
191err_t
192igmp_start(struct netif *netif)
193{
194  struct igmp_group* group;
195
196  LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: starting IGMP processing on if %p\n", netif));
197
198  group = igmp_lookup_group(netif, &allsystems);
199
200  if (group != NULL) {
201    group->group_state = IGMP_GROUP_IDLE_MEMBER;
202    group->use++;
203
204    /* Allow the igmp messages at the MAC level */
205    if (netif->igmp_mac_filter != NULL) {
206      LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: igmp_mac_filter(ADD "));
207      ip_addr_debug_print(IGMP_DEBUG, &allsystems);
208      LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif));
209      netif->igmp_mac_filter(netif, &allsystems, IGMP_ADD_MAC_FILTER);
210    }
211
212    return ERR_OK;
213  }
214
215  return ERR_MEM;
216}
217
218/**
219 * Stop IGMP processing on interface
220 *
221 * @param netif network interface on which stop IGMP processing
222 */
223err_t
224igmp_stop(struct netif *netif)
225{
226  struct igmp_group *group = igmp_group_list;
227  struct igmp_group *prev  = NULL;
228  struct igmp_group *next;
229
230  /* look for groups joined on this interface further down the list */
231  while (group != NULL) {
232    next = group->next;
233    /* is it a group joined on this interface? */
234    if (group->netif == netif) {
235      /* is it the first group of the list? */
236      if (group == igmp_group_list) {
237        igmp_group_list = next;
238      }
239      /* is there a "previous" group defined? */
240      if (prev != NULL) {
241        prev->next = next;
242      }
243      /* disable the group at the MAC level */
244      if (netif->igmp_mac_filter != NULL) {
245        LWIP_DEBUGF(IGMP_DEBUG, ("igmp_stop: igmp_mac_filter(DEL "));
246        ip_addr_debug_print(IGMP_DEBUG, &group->group_address);
247        LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif));
248        netif->igmp_mac_filter(netif, &(group->group_address), IGMP_DEL_MAC_FILTER);
249      }
250      /* free group */
251      memp_free(MEMP_IGMP_GROUP, group);
252    } else {
253      /* change the "previous" */
254      prev = group;
255    }
256    /* move to "next" */
257    group = next;
258  }
259  return ERR_OK;
260}
261
262/**
263 * Report IGMP memberships for this interface
264 *
265 * @param netif network interface on which report IGMP memberships
266 */
267void
268igmp_report_groups(struct netif *netif)
269{
270  struct igmp_group *group = igmp_group_list;
271
272  LWIP_DEBUGF(IGMP_DEBUG, ("igmp_report_groups: sending IGMP reports on if %p\n", netif));
273
274  while (group != NULL) {
275    if (group->netif == netif) {
276      igmp_delaying_member(group, IGMP_JOIN_DELAYING_MEMBER_TMR);
277    }
278    group = group->next;
279  }
280}
281
282/**
283 * Search for a group in the global igmp_group_list
284 *
285 * @param ifp the network interface for which to look
286 * @param addr the group ip address to search for
287 * @return a struct igmp_group* if the group has been found,
288 *         NULL if the group wasn't found.
289 */
290struct igmp_group *
291igmp_lookfor_group(struct netif *ifp, ip_addr_t *addr)
292{
293  struct igmp_group *group = igmp_group_list;
294
295  while (group != NULL) {
296    if ((group->netif == ifp) && (ip_addr_cmp(&(group->group_address), addr))) {
297      return group;
298    }
299    group = group->next;
300  }
301
302  /* to be clearer, we return NULL here instead of
303   * 'group' (which is also NULL at this point).
304   */
305  return NULL;
306}
307
308/**
309 * Search for a specific igmp group and create a new one if not found-
310 *
311 * @param ifp the network interface for which to look
312 * @param addr the group ip address to search
313 * @return a struct igmp_group*,
314 *         NULL on memory error.
315 */
316struct igmp_group *
317igmp_lookup_group(struct netif *ifp, ip_addr_t *addr)
318{
319  struct igmp_group *group = igmp_group_list;
320
321  /* Search if the group already exists */
322  group = igmp_lookfor_group(ifp, addr);
323  if (group != NULL) {
324    /* Group already exists. */
325    return group;
326  }
327
328  /* Group doesn't exist yet, create a new one */
329  group = (struct igmp_group *)memp_malloc(MEMP_IGMP_GROUP);
330  if (group != NULL) {
331    group->netif              = ifp;
332    ip_addr_set(&(group->group_address), addr);
333    group->timer              = 0; /* Not running */
334    group->group_state        = IGMP_GROUP_NON_MEMBER;
335    group->last_reporter_flag = 0;
336    group->use                = 0;
337    group->next               = igmp_group_list;
338
339    igmp_group_list = group;
340  }
341
342  LWIP_DEBUGF(IGMP_DEBUG, ("igmp_lookup_group: %sallocated a new group with address ", (group?"":"impossible to ")));
343  ip_addr_debug_print(IGMP_DEBUG, addr);
344  LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", ifp));
345
346  return group;
347}
348
349/**
350 * Remove a group in the global igmp_group_list
351 *
352 * @param group the group to remove from the global igmp_group_list
353 * @return ERR_OK if group was removed from the list, an err_t otherwise
354 */
355static err_t
356igmp_remove_group(struct igmp_group *group)
357{
358  err_t err = ERR_OK;
359
360  /* Is it the first group? */
361  if (igmp_group_list == group) {
362    igmp_group_list = group->next;
363  } else {
364    /* look for group further down the list */
365    struct igmp_group *tmpGroup;
366    for (tmpGroup = igmp_group_list; tmpGroup != NULL; tmpGroup = tmpGroup->next) {
367      if (tmpGroup->next == group) {
368        tmpGroup->next = group->next;
369        break;
370      }
371    }
372    /* Group not found in the global igmp_group_list */
373    if (tmpGroup == NULL)
374      err = ERR_ARG;
375  }
376  /* free group */
377  memp_free(MEMP_IGMP_GROUP, group);
378
379  return err;
380}
381
382/**
383 * Called from ip_input() if a new IGMP packet is received.
384 *
385 * @param p received igmp packet, p->payload pointing to the ip header
386 * @param inp network interface on which the packet was received
387 * @param dest destination ip address of the igmp packet
388 */
389void
390igmp_input(struct pbuf *p, struct netif *inp, ip_addr_t *dest)
391{
392  struct ip_hdr *    iphdr;
393  struct igmp_msg*   igmp;
394  struct igmp_group* group;
395  struct igmp_group* groupref;
396
397  IGMP_STATS_INC(igmp.recv);
398
399  /* Note that the length CAN be greater than 8 but only 8 are used - All are included in the checksum */
400  iphdr = (struct ip_hdr *)p->payload;
401  if (pbuf_header(p, -(s16_t)(IPH_HL(iphdr) * 4)) || (p->len < IGMP_MINLEN)) {
402    pbuf_free(p);
403    IGMP_STATS_INC(igmp.lenerr);
404    LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: length error\n"));
405    return;
406  }
407
408  LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: message from "));
409  ip_addr_debug_print(IGMP_DEBUG, &(iphdr->src));
410  LWIP_DEBUGF(IGMP_DEBUG, (" to address "));
411  ip_addr_debug_print(IGMP_DEBUG, &(iphdr->dest));
412  LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", inp));
413
414  /* Now calculate and check the checksum */
415  igmp = (struct igmp_msg *)p->payload;
416  if (inet_chksum(igmp, p->len)) {
417    pbuf_free(p);
418    IGMP_STATS_INC(igmp.chkerr);
419    LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: checksum error\n"));
420    return;
421  }
422
423  /* Packet is ok so find an existing group */
424  group = igmp_lookfor_group(inp, dest); /* use the destination IP address of incoming packet */
425
426  /* If group can be found or create... */
427  if (!group) {
428    pbuf_free(p);
429    IGMP_STATS_INC(igmp.drop);
430    LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP frame not for us\n"));
431    return;
432  }
433
434  /* NOW ACT ON THE INCOMING MESSAGE TYPE... */
435  switch (igmp->igmp_msgtype) {
436   case IGMP_MEMB_QUERY: {
437     /* IGMP_MEMB_QUERY to the "all systems" address ? */
438     if ((ip_addr_cmp(dest, &allsystems)) && ip_addr_isany(&igmp->igmp_group_address)) {
439       /* THIS IS THE GENERAL QUERY */
440       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)));
441
442       if (igmp->igmp_maxresp == 0) {
443         IGMP_STATS_INC(igmp.rx_v1);
444         LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: got an all hosts query with time== 0 - this is V1 and not implemented - treat as v2\n"));
445         igmp->igmp_maxresp = IGMP_V1_DELAYING_MEMBER_TMR;
446       } else {
447         IGMP_STATS_INC(igmp.rx_general);
448       }
449
450       groupref = igmp_group_list;
451       while (groupref) {
452         /* Do not send messages on the all systems group address! */
453         if ((groupref->netif == inp) && (!(ip_addr_cmp(&(groupref->group_address), &allsystems)))) {
454           igmp_delaying_member(groupref, igmp->igmp_maxresp);
455         }
456         groupref = groupref->next;
457       }
458     } else {
459       /* IGMP_MEMB_QUERY to a specific group ? */
460       if (!ip_addr_isany(&igmp->igmp_group_address)) {
461         LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_MEMB_QUERY to a specific group "));
462         ip_addr_debug_print(IGMP_DEBUG, &igmp->igmp_group_address);
463         if (ip_addr_cmp(dest, &allsystems)) {
464           ip_addr_t groupaddr;
465           LWIP_DEBUGF(IGMP_DEBUG, (" using \"ALL SYSTEMS\" address (224.0.0.1) [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp)));
466           /* we first need to re-look for the group since we used dest last time */
467           ip_addr_copy(groupaddr, igmp->igmp_group_address);
468           group = igmp_lookfor_group(inp, &groupaddr);
469         } else {
470           LWIP_DEBUGF(IGMP_DEBUG, (" with the group address as destination [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp)));
471         }
472
473         if (group != NULL) {
474           IGMP_STATS_INC(igmp.rx_group);
475           igmp_delaying_member(group, igmp->igmp_maxresp);
476         } else {
477           IGMP_STATS_INC(igmp.drop);
478         }
479       } else {
480         IGMP_STATS_INC(igmp.proterr);
481       }
482     }
483     break;
484   }
485   case IGMP_V2_MEMB_REPORT: {
486     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_V2_MEMB_REPORT\n"));
487     IGMP_STATS_INC(igmp.rx_report);
488     if (group->group_state == IGMP_GROUP_DELAYING_MEMBER) {
489       /* This is on a specific group we have already looked up */
490       group->timer = 0; /* stopped */
491       group->group_state = IGMP_GROUP_IDLE_MEMBER;
492       group->last_reporter_flag = 0;
493     }
494     break;
495   }
496   default: {
497     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: unexpected msg %d in state %d on group %p on if %p\n",
498       igmp->igmp_msgtype, group->group_state, &group, group->netif));
499     IGMP_STATS_INC(igmp.proterr);
500     break;
501   }
502  }
503
504  pbuf_free(p);
505  return;
506}
507
508/**
509 * Join a group on one network interface.
510 *
511 * @param ifaddr ip address of the network interface which should join a new group
512 * @param groupaddr the ip address of the group which to join
513 * @return ERR_OK if group was joined on the netif(s), an err_t otherwise
514 */
515err_t
516igmp_joingroup(ip_addr_t *ifaddr, ip_addr_t *groupaddr)
517{
518  err_t              err = ERR_VAL; /* no matching interface */
519  struct igmp_group *group;
520  struct netif      *netif;
521
522  /* make sure it is multicast address */
523  LWIP_ERROR("igmp_joingroup: attempt to join non-multicast address", ip_addr_ismulticast(groupaddr), return ERR_VAL;);
524  LWIP_ERROR("igmp_joingroup: attempt to join allsystems address", (!ip_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;);
525
526  /* loop through netif's */
527  netif = netif_list;
528  while (netif != NULL) {
529    /* Should we join this interface ? */
530    if ((netif->flags & NETIF_FLAG_IGMP) && ((ip_addr_isany(ifaddr) || ip_addr_cmp(&(netif->ip_addr), ifaddr)))) {
531      /* find group or create a new one if not found */
532      group = igmp_lookup_group(netif, groupaddr);
533
534      if (group != NULL) {
535        /* This should create a new group, check the state to make sure */
536        if (group->group_state != IGMP_GROUP_NON_MEMBER) {
537          LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: join to group not in state IGMP_GROUP_NON_MEMBER\n"));
538        } else {
539          /* OK - it was new group */
540          LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: join to new group: "));
541          ip_addr_debug_print(IGMP_DEBUG, groupaddr);
542          LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
543
544          /* If first use of the group, allow the group at the MAC level */
545          if ((group->use==0) && (netif->igmp_mac_filter != NULL)) {
546            LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: igmp_mac_filter(ADD "));
547            ip_addr_debug_print(IGMP_DEBUG, groupaddr);
548            LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif));
549            netif->igmp_mac_filter(netif, groupaddr, IGMP_ADD_MAC_FILTER);
550          }
551
552          IGMP_STATS_INC(igmp.tx_join);
553          igmp_send(group, IGMP_V2_MEMB_REPORT);
554
555          igmp_start_timer(group, IGMP_JOIN_DELAYING_MEMBER_TMR);
556
557          /* Need to work out where this timer comes from */
558          group->group_state = IGMP_GROUP_DELAYING_MEMBER;
559        }
560        /* Increment group use */
561        group->use++;
562        /* Join on this interface */
563        err = ERR_OK;
564      } else {
565        /* Return an error even if some network interfaces are joined */
566        /** @todo undo any other netif already joined */
567        LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: Not enought memory to join to group\n"));
568        return ERR_MEM;
569      }
570    }
571    /* proceed to next network interface */
572    netif = netif->next;
573  }
574
575  return err;
576}
577
578/**
579 * Leave a group on one network interface.
580 *
581 * @param ifaddr ip address of the network interface which should leave a group
582 * @param groupaddr the ip address of the group which to leave
583 * @return ERR_OK if group was left on the netif(s), an err_t otherwise
584 */
585err_t
586igmp_leavegroup(ip_addr_t *ifaddr, ip_addr_t *groupaddr)
587{
588  err_t              err = ERR_VAL; /* no matching interface */
589  struct igmp_group *group;
590  struct netif      *netif;
591
592  /* make sure it is multicast address */
593  LWIP_ERROR("igmp_leavegroup: attempt to leave non-multicast address", ip_addr_ismulticast(groupaddr), return ERR_VAL;);
594  LWIP_ERROR("igmp_leavegroup: attempt to leave allsystems address", (!ip_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;);
595
596  /* loop through netif's */
597  netif = netif_list;
598  while (netif != NULL) {
599    /* Should we leave this interface ? */
600    if ((netif->flags & NETIF_FLAG_IGMP) && ((ip_addr_isany(ifaddr) || ip_addr_cmp(&(netif->ip_addr), ifaddr)))) {
601      /* find group */
602      group = igmp_lookfor_group(netif, groupaddr);
603
604      if (group != NULL) {
605        /* Only send a leave if the flag is set according to the state diagram */
606        LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: Leaving group: "));
607        ip_addr_debug_print(IGMP_DEBUG, groupaddr);
608        LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
609
610        /* If there is no other use of the group */
611        if (group->use <= 1) {
612          /* If we are the last reporter for this group */
613          if (group->last_reporter_flag) {
614            LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: sending leaving group\n"));
615            IGMP_STATS_INC(igmp.tx_leave);
616            igmp_send(group, IGMP_LEAVE_GROUP);
617          }
618
619          /* Disable the group at the MAC level */
620          if (netif->igmp_mac_filter != NULL) {
621            LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: igmp_mac_filter(DEL "));
622            ip_addr_debug_print(IGMP_DEBUG, groupaddr);
623            LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif));
624            netif->igmp_mac_filter(netif, groupaddr, IGMP_DEL_MAC_FILTER);
625          }
626
627          LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: remove group: "));
628          ip_addr_debug_print(IGMP_DEBUG, groupaddr);
629          LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
630
631          /* Free the group */
632          igmp_remove_group(group);
633        } else {
634          /* Decrement group use */
635          group->use--;
636        }
637        /* Leave on this interface */
638        err = ERR_OK;
639      } else {
640        /* It's not a fatal error on "leavegroup" */
641        LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: not member of group\n"));
642      }
643    }
644    /* proceed to next network interface */
645    netif = netif->next;
646  }
647
648  return err;
649}
650
651/**
652 * The igmp timer function (both for NO_SYS=1 and =0)
653 * Should be called every IGMP_TMR_INTERVAL milliseconds (100 ms is default).
654 */
655void
656igmp_tmr(void)
657{
658  struct igmp_group *group = igmp_group_list;
659
660  while (group != NULL) {
661    if (group->timer > 0) {
662      group->timer--;
663      if (group->timer == 0) {
664        igmp_timeout(group);
665      }
666    }
667    group = group->next;
668  }
669}
670
671/**
672 * Called if a timeout for one group is reached.
673 * Sends a report for this group.
674 *
675 * @param group an igmp_group for which a timeout is reached
676 */
677static void
678igmp_timeout(struct igmp_group *group)
679{
680  /* If the state is IGMP_GROUP_DELAYING_MEMBER then we send a report for this group */
681  if (group->group_state == IGMP_GROUP_DELAYING_MEMBER) {
682    LWIP_DEBUGF(IGMP_DEBUG, ("igmp_timeout: report membership for group with address "));
683    ip_addr_debug_print(IGMP_DEBUG, &(group->group_address));
684    LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", group->netif));
685
686    IGMP_STATS_INC(igmp.tx_report);
687    igmp_send(group, IGMP_V2_MEMB_REPORT);
688  }
689}
690
691/**
692 * Start a timer for an igmp group
693 *
694 * @param group the igmp_group for which to start a timer
695 * @param max_time the time in multiples of IGMP_TMR_INTERVAL (decrease with
696 *        every call to igmp_tmr())
697 */
698static void
699igmp_start_timer(struct igmp_group *group, u8_t max_time)
700{
701  /* ensure the input value is > 0 */
702  if (max_time == 0) {
703    max_time = 1;
704  }
705  /* ensure the random value is > 0 */
706  group->timer = (LWIP_RAND() % (max_time - 1)) + 1;
707}
708
709/**
710 * Stop a timer for an igmp_group
711 *
712 * @param group the igmp_group for which to stop the timer
713 */
714static void
715igmp_stop_timer(struct igmp_group *group)
716{
717  group->timer = 0;
718}
719
720/**
721 * Delaying membership report for a group if necessary
722 *
723 * @param group the igmp_group for which "delaying" membership report
724 * @param maxresp query delay
725 */
726static void
727igmp_delaying_member(struct igmp_group *group, u8_t maxresp)
728{
729  if ((group->group_state == IGMP_GROUP_IDLE_MEMBER) ||
730     ((group->group_state == IGMP_GROUP_DELAYING_MEMBER) &&
731      ((group->timer == 0) || (maxresp < group->timer)))) {
732    igmp_start_timer(group, maxresp);
733    group->group_state = IGMP_GROUP_DELAYING_MEMBER;
734  }
735}
736
737
738/**
739 * Sends an IP packet on a network interface. This function constructs the IP header
740 * and calculates the IP header checksum. If the source IP address is NULL,
741 * the IP address of the outgoing network interface is filled in as source address.
742 *
743 * @param p the packet to send (p->payload points to the data, e.g. next
744            protocol header; if dest == IP_HDRINCL, p already includes an IP
745            header and p->payload points to that IP header)
746 * @param src the source IP address to send from (if src == IP_ADDR_ANY, the
747 *         IP  address of the netif used to send is used as source address)
748 * @param dest the destination IP address to send the packet to
749 * @param ttl the TTL value to be set in the IP header
750 * @param proto the PROTOCOL to be set in the IP header
751 * @param netif the netif on which to send this packet
752 * @return ERR_OK if the packet was sent OK
753 *         ERR_BUF if p doesn't have enough space for IP/LINK headers
754 *         returns errors returned by netif->output
755 */
756static err_t
757igmp_ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, struct netif *netif)
758{
759  /* This is the "router alert" option */
760  u16_t ra[2];
761  ra[0] = PP_HTONS(ROUTER_ALERT);
762  ra[1] = 0x0000; /* Router shall examine packet */
763  IGMP_STATS_INC(igmp.xmit);
764  return ip_output_if_opt(p, src, dest, IGMP_TTL, 0, IP_PROTO_IGMP, netif, ra, ROUTER_ALERTLEN);
765}
766
767/**
768 * Send an igmp packet to a specific group.
769 *
770 * @param group the group to which to send the packet
771 * @param type the type of igmp packet to send
772 */
773static void
774igmp_send(struct igmp_group *group, u8_t type)
775{
776  struct pbuf*     p    = NULL;
777  struct igmp_msg* igmp = NULL;
778  ip_addr_t   src  = *IP_ADDR_ANY;
779  ip_addr_t*  dest = NULL;
780
781  /* IP header + "router alert" option + IGMP header */
782  p = pbuf_alloc(PBUF_TRANSPORT, IGMP_MINLEN, PBUF_RAM);
783
784  if (p) {
785    igmp = (struct igmp_msg *)p->payload;
786    LWIP_ASSERT("igmp_send: check that first pbuf can hold struct igmp_msg",
787               (p->len >= sizeof(struct igmp_msg)));
788    ip_addr_copy(src, group->netif->ip_addr);
789
790    if (type == IGMP_V2_MEMB_REPORT) {
791      dest = &(group->group_address);
792      ip_addr_copy(igmp->igmp_group_address, group->group_address);
793      group->last_reporter_flag = 1; /* Remember we were the last to report */
794    } else {
795      if (type == IGMP_LEAVE_GROUP) {
796        dest = &allrouters;
797        ip_addr_copy(igmp->igmp_group_address, group->group_address);
798      }
799    }
800
801    if ((type == IGMP_V2_MEMB_REPORT) || (type == IGMP_LEAVE_GROUP)) {
802      igmp->igmp_msgtype  = type;
803      igmp->igmp_maxresp  = 0;
804      igmp->igmp_checksum = 0;
805      igmp->igmp_checksum = inet_chksum(igmp, IGMP_MINLEN);
806
807      igmp_ip_output_if(p, &src, dest, group->netif);
808    }
809
810    pbuf_free(p);
811  } else {
812    LWIP_DEBUGF(IGMP_DEBUG, ("igmp_send: not enough memory for igmp_send\n"));
813    IGMP_STATS_INC(igmp.memerr);
814  }
815}
816
817#endif /* LWIP_IGMP */
818