]> rtime.felk.cvut.cz Git - pes-rpp/rpp-lwip.git/blob - src/core/ipv4/igmp.c
fixed bug #38526 Coverity: Recursive Header Inclusion in ip6.h
[pes-rpp/rpp-lwip.git] / src / core / ipv4 / igmp.c
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 /*-------------------------------------------------------------
42 Note 1)
43 Although the rfc requires V1 AND V2 capability
44 we will only support v2 since now V1 is very old (August 1989)
45 V1 can be added if required
46
47 a debug print and statistic have been implemented to
48 show this up.
49 -------------------------------------------------------------
50 -------------------------------------------------------------
51 Note 2)
52 A query for a specific group address (as opposed to ALLHOSTS)
53 has now been implemented as I am unsure if it is required
54
55 a debug print and statistic have been implemented to
56 show this up.
57 -------------------------------------------------------------
58 -------------------------------------------------------------
59 Note 3)
60 The router alert rfc 2113 is implemented in outgoing packets
61 but not checked rigorously incoming
62 -------------------------------------------------------------
63 Steve 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
125 PACK_STRUCT_BEGIN
126 struct 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;
132 PACK_STRUCT_END
133 #ifdef PACK_STRUCT_USE_INCLUDES
134 #  include "arch/epstruct.h"
135 #endif
136
137
138 static struct igmp_group *igmp_lookup_group(struct netif *ifp, ip_addr_t *addr);
139 static err_t  igmp_remove_group(struct igmp_group *group);
140 static void   igmp_timeout( struct igmp_group *group);
141 static void   igmp_start_timer(struct igmp_group *group, u8_t max_time);
142 static void   igmp_delaying_member(struct igmp_group *group, u8_t maxresp);
143 static err_t  igmp_ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, struct netif *netif);
144 static void   igmp_send(struct igmp_group *group, u8_t type);
145
146
147 static struct igmp_group* igmp_group_list;
148 static ip_addr_t     allsystems;
149 static ip_addr_t     allrouters;
150
151
152 /**
153  * Initialize the IGMP module
154  */
155 void
156 igmp_init(void)
157 {
158   LWIP_DEBUGF(IGMP_DEBUG, ("igmp_init: initializing\n"));
159
160   IP4_ADDR(&allsystems, 224, 0, 0, 1);
161   IP4_ADDR(&allrouters, 224, 0, 0, 2);
162 }
163
164 #ifdef LWIP_DEBUG
165 /**
166  * Dump global IGMP groups list
167  */
168 void
169 igmp_dump_group_list()
170
171   struct igmp_group *group = igmp_group_list;
172
173   while (group != NULL) {
174     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_dump_group_list: [%"U32_F"] ", (u32_t)(group->group_state)));
175     ip_addr_debug_print(IGMP_DEBUG, &group->group_address);
176     LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", group->netif));
177     group = group->next;
178   }
179   LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
180 }
181 #else
182 #define igmp_dump_group_list()
183 #endif /* LWIP_DEBUG */
184
185 /**
186  * Start IGMP processing on interface
187  *
188  * @param netif network interface on which start IGMP processing
189  */
190 err_t
191 igmp_start(struct netif *netif)
192 {
193   struct igmp_group* group;
194
195   LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: starting IGMP processing on if %p\n", netif));
196
197   group = igmp_lookup_group(netif, &allsystems);
198
199   if (group != NULL) {
200     group->group_state = IGMP_GROUP_IDLE_MEMBER;
201     group->use++;
202
203     /* Allow the igmp messages at the MAC level */
204     if (netif->igmp_mac_filter != NULL) {
205       LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: igmp_mac_filter(ADD "));
206       ip_addr_debug_print(IGMP_DEBUG, &allsystems);
207       LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif));
208       netif->igmp_mac_filter(netif, &allsystems, IGMP_ADD_MAC_FILTER);
209     }
210
211     return ERR_OK;
212   }
213
214   return ERR_MEM;
215 }
216
217 /**
218  * Stop IGMP processing on interface
219  *
220  * @param netif network interface on which stop IGMP processing
221  */
222 err_t
223 igmp_stop(struct netif *netif)
224 {
225   struct igmp_group *group = igmp_group_list;
226   struct igmp_group *prev  = NULL;
227   struct igmp_group *next;
228
229   /* look for groups joined on this interface further down the list */
230   while (group != NULL) {
231     next = group->next;
232     /* is it a group joined on this interface? */
233     if (group->netif == netif) {
234       /* is it the first group of the list? */
235       if (group == igmp_group_list) {
236         igmp_group_list = next;
237       }
238       /* is there a "previous" group defined? */
239       if (prev != NULL) {
240         prev->next = next;
241       }
242       /* disable the group at the MAC level */
243       if (netif->igmp_mac_filter != NULL) {
244         LWIP_DEBUGF(IGMP_DEBUG, ("igmp_stop: igmp_mac_filter(DEL "));
245         ip_addr_debug_print(IGMP_DEBUG, &group->group_address);
246         LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif));
247         netif->igmp_mac_filter(netif, &(group->group_address), IGMP_DEL_MAC_FILTER);
248       }
249       /* free group */
250       memp_free(MEMP_IGMP_GROUP, group);
251     } else {
252       /* change the "previous" */
253       prev = group;
254     }
255     /* move to "next" */
256     group = next;
257   }
258   return ERR_OK;
259 }
260
261 /**
262  * Report IGMP memberships for this interface
263  *
264  * @param netif network interface on which report IGMP memberships
265  */
266 void
267 igmp_report_groups(struct netif *netif)
268 {
269   struct igmp_group *group = igmp_group_list;
270
271   LWIP_DEBUGF(IGMP_DEBUG, ("igmp_report_groups: sending IGMP reports on if %p\n", netif));
272
273   while (group != NULL) {
274     if (group->netif == netif) {
275       igmp_delaying_member(group, IGMP_JOIN_DELAYING_MEMBER_TMR);
276     }
277     group = group->next;
278   }
279 }
280
281 /**
282  * Search for a group in the global igmp_group_list
283  *
284  * @param ifp the network interface for which to look
285  * @param addr the group ip address to search for
286  * @return a struct igmp_group* if the group has been found,
287  *         NULL if the group wasn't found.
288  */
289 struct igmp_group *
290 igmp_lookfor_group(struct netif *ifp, ip_addr_t *addr)
291 {
292   struct igmp_group *group = igmp_group_list;
293
294   while (group != NULL) {
295     if ((group->netif == ifp) && (ip_addr_cmp(&(group->group_address), addr))) {
296       return group;
297     }
298     group = group->next;
299   }
300
301   /* to be clearer, we return NULL here instead of
302    * 'group' (which is also NULL at this point).
303    */
304   return NULL;
305 }
306
307 /**
308  * Search for a specific igmp group and create a new one if not found-
309  *
310  * @param ifp the network interface for which to look
311  * @param addr the group ip address to search
312  * @return a struct igmp_group*,
313  *         NULL on memory error.
314  */
315 struct igmp_group *
316 igmp_lookup_group(struct netif *ifp, ip_addr_t *addr)
317 {
318   struct igmp_group *group = igmp_group_list;
319   
320   /* Search if the group already exists */
321   group = igmp_lookfor_group(ifp, addr);
322   if (group != NULL) {
323     /* Group already exists. */
324     return group;
325   }
326
327   /* Group doesn't exist yet, create a new one */
328   group = (struct igmp_group *)memp_malloc(MEMP_IGMP_GROUP);
329   if (group != NULL) {
330     group->netif              = ifp;
331     ip_addr_set(&(group->group_address), addr);
332     group->timer              = 0; /* Not running */
333     group->group_state        = IGMP_GROUP_NON_MEMBER;
334     group->last_reporter_flag = 0;
335     group->use                = 0;
336     group->next               = igmp_group_list;
337     
338     igmp_group_list = group;
339   }
340
341   LWIP_DEBUGF(IGMP_DEBUG, ("igmp_lookup_group: %sallocated a new group with address ", (group?"":"impossible to ")));
342   ip_addr_debug_print(IGMP_DEBUG, addr);
343   LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", ifp));
344
345   return group;
346 }
347
348 /**
349  * Remove a group in the global igmp_group_list
350  *
351  * @param group the group to remove from the global igmp_group_list
352  * @return ERR_OK if group was removed from the list, an err_t otherwise
353  */
354 static err_t
355 igmp_remove_group(struct igmp_group *group)
356 {
357   err_t err = ERR_OK;
358
359   /* Is it the first group? */
360   if (igmp_group_list == group) {
361     igmp_group_list = group->next;
362   } else {
363     /* look for group further down the list */
364     struct igmp_group *tmpGroup;
365     for (tmpGroup = igmp_group_list; tmpGroup != NULL; tmpGroup = tmpGroup->next) {
366       if (tmpGroup->next == group) {
367         tmpGroup->next = group->next;
368         break;
369       }
370     }
371     /* Group not found in the global igmp_group_list */
372     if (tmpGroup == NULL)
373       err = ERR_ARG;
374   }
375   /* free group */
376   memp_free(MEMP_IGMP_GROUP, group);
377
378   return err;
379 }
380
381 /**
382  * Called from ip_input() if a new IGMP packet is received.
383  *
384  * @param p received igmp packet, p->payload pointing to the igmp header
385  * @param inp network interface on which the packet was received
386  * @param dest destination ip address of the igmp packet
387  */
388 void
389 igmp_input(struct pbuf *p, struct netif *inp, ip_addr_t *dest)
390 {
391   struct igmp_msg*   igmp;
392   struct igmp_group* group;
393   struct igmp_group* groupref;
394
395   IGMP_STATS_INC(igmp.recv);
396
397   /* Note that the length CAN be greater than 8 but only 8 are used - All are included in the checksum */    
398   if (p->len < IGMP_MINLEN) {
399     pbuf_free(p);
400     IGMP_STATS_INC(igmp.lenerr);
401     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: length error\n"));
402     return;
403   }
404
405   LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: message from "));
406   ip_addr_debug_print(IGMP_DEBUG, &(ip_current_header()->src));
407   LWIP_DEBUGF(IGMP_DEBUG, (" to address "));
408   ip_addr_debug_print(IGMP_DEBUG, &(ip_current_header()->dest));
409   LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", inp));
410
411   /* Now calculate and check the checksum */
412   igmp = (struct igmp_msg *)p->payload;
413   if (inet_chksum(igmp, p->len)) {
414     pbuf_free(p);
415     IGMP_STATS_INC(igmp.chkerr);
416     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: checksum error\n"));
417     return;
418   }
419
420   /* Packet is ok so find an existing group */
421   group = igmp_lookfor_group(inp, dest); /* use the destination IP address of incoming packet */
422   
423   /* If group can be found or create... */
424   if (!group) {
425     pbuf_free(p);
426     IGMP_STATS_INC(igmp.drop);
427     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP frame not for us\n"));
428     return;
429   }
430
431   /* NOW ACT ON THE INCOMING MESSAGE TYPE... */
432   switch (igmp->igmp_msgtype) {
433    case IGMP_MEMB_QUERY: {
434      /* IGMP_MEMB_QUERY to the "all systems" address ? */
435      if ((ip_addr_cmp(dest, &allsystems)) && ip_addr_isany(&igmp->igmp_group_address)) {
436        /* THIS IS THE GENERAL QUERY */
437        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)));
438
439        if (igmp->igmp_maxresp == 0) {
440          IGMP_STATS_INC(igmp.rx_v1);
441          LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: got an all hosts query with time== 0 - this is V1 and not implemented - treat as v2\n"));
442          igmp->igmp_maxresp = IGMP_V1_DELAYING_MEMBER_TMR;
443        } else {
444          IGMP_STATS_INC(igmp.rx_general);
445        }
446
447        groupref = igmp_group_list;
448        while (groupref) {
449          /* Do not send messages on the all systems group address! */
450          if ((groupref->netif == inp) && (!(ip_addr_cmp(&(groupref->group_address), &allsystems)))) {
451            igmp_delaying_member(groupref, igmp->igmp_maxresp);
452          }
453          groupref = groupref->next;
454        }
455      } else {
456        /* IGMP_MEMB_QUERY to a specific group ? */
457        if (!ip_addr_isany(&igmp->igmp_group_address)) {
458          LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_MEMB_QUERY to a specific group "));
459          ip_addr_debug_print(IGMP_DEBUG, &igmp->igmp_group_address);
460          if (ip_addr_cmp(dest, &allsystems)) {
461            ip_addr_t groupaddr;
462            LWIP_DEBUGF(IGMP_DEBUG, (" using \"ALL SYSTEMS\" address (224.0.0.1) [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp)));
463            /* we first need to re-look for the group since we used dest last time */
464            ip_addr_copy(groupaddr, igmp->igmp_group_address);
465            group = igmp_lookfor_group(inp, &groupaddr);
466          } else {
467            LWIP_DEBUGF(IGMP_DEBUG, (" with the group address as destination [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp)));
468          }
469
470          if (group != NULL) {
471            IGMP_STATS_INC(igmp.rx_group);
472            igmp_delaying_member(group, igmp->igmp_maxresp);
473          } else {
474            IGMP_STATS_INC(igmp.drop);
475          }
476        } else {
477          IGMP_STATS_INC(igmp.proterr);
478        }
479      }
480      break;
481    }
482    case IGMP_V2_MEMB_REPORT: {
483      LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_V2_MEMB_REPORT\n"));
484      IGMP_STATS_INC(igmp.rx_report);
485      if (group->group_state == IGMP_GROUP_DELAYING_MEMBER) {
486        /* This is on a specific group we have already looked up */
487        group->timer = 0; /* stopped */
488        group->group_state = IGMP_GROUP_IDLE_MEMBER;
489        group->last_reporter_flag = 0;
490      }
491      break;
492    }
493    default: {
494      LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: unexpected msg %d in state %d on group %p on if %p\n",
495        igmp->igmp_msgtype, group->group_state, &group, group->netif));
496      IGMP_STATS_INC(igmp.proterr);
497      break;
498    }
499   }
500
501   pbuf_free(p);
502   return;
503 }
504
505 /**
506  * Join a group on one network interface.
507  *
508  * @param ifaddr ip address of the network interface which should join a new group
509  * @param groupaddr the ip address of the group which to join
510  * @return ERR_OK if group was joined on the netif(s), an err_t otherwise
511  */
512 err_t
513 igmp_joingroup(ip_addr_t *ifaddr, ip_addr_t *groupaddr)
514 {
515   err_t              err = ERR_VAL; /* no matching interface */
516   struct igmp_group *group;
517   struct netif      *netif;
518
519   /* make sure it is multicast address */
520   LWIP_ERROR("igmp_joingroup: attempt to join non-multicast address", ip_addr_ismulticast(groupaddr), return ERR_VAL;);
521   LWIP_ERROR("igmp_joingroup: attempt to join allsystems address", (!ip_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;);
522
523   /* loop through netif's */
524   netif = netif_list;
525   while (netif != NULL) {
526     /* Should we join this interface ? */
527     if ((netif->flags & NETIF_FLAG_IGMP) && ((ip_addr_isany(ifaddr) || ip_addr_cmp(&(netif->ip_addr), ifaddr)))) {
528       /* find group or create a new one if not found */
529       group = igmp_lookup_group(netif, groupaddr);
530
531       if (group != NULL) {
532         /* This should create a new group, check the state to make sure */
533         if (group->group_state != IGMP_GROUP_NON_MEMBER) {
534           LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: join to group not in state IGMP_GROUP_NON_MEMBER\n"));
535         } else {
536           /* OK - it was new group */
537           LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: join to new group: "));
538           ip_addr_debug_print(IGMP_DEBUG, groupaddr);
539           LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
540
541           /* If first use of the group, allow the group at the MAC level */
542           if ((group->use==0) && (netif->igmp_mac_filter != NULL)) {
543             LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: igmp_mac_filter(ADD "));
544             ip_addr_debug_print(IGMP_DEBUG, groupaddr);
545             LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif));
546             netif->igmp_mac_filter(netif, groupaddr, IGMP_ADD_MAC_FILTER);
547           }
548
549           IGMP_STATS_INC(igmp.tx_join);
550           igmp_send(group, IGMP_V2_MEMB_REPORT);
551
552           igmp_start_timer(group, IGMP_JOIN_DELAYING_MEMBER_TMR);
553
554           /* Need to work out where this timer comes from */
555           group->group_state = IGMP_GROUP_DELAYING_MEMBER;
556         }
557         /* Increment group use */
558         group->use++;
559         /* Join on this interface */
560         err = ERR_OK;
561       } else {
562         /* Return an error even if some network interfaces are joined */
563         /** @todo undo any other netif already joined */
564         LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: Not enought memory to join to group\n"));
565         return ERR_MEM;
566       }
567     }
568     /* proceed to next network interface */
569     netif = netif->next;
570   }
571
572   return err;
573 }
574
575 /**
576  * Leave a group on one network interface.
577  *
578  * @param ifaddr ip address of the network interface which should leave a group
579  * @param groupaddr the ip address of the group which to leave
580  * @return ERR_OK if group was left on the netif(s), an err_t otherwise
581  */
582 err_t
583 igmp_leavegroup(ip_addr_t *ifaddr, ip_addr_t *groupaddr)
584 {
585   err_t              err = ERR_VAL; /* no matching interface */
586   struct igmp_group *group;
587   struct netif      *netif;
588
589   /* make sure it is multicast address */
590   LWIP_ERROR("igmp_leavegroup: attempt to leave non-multicast address", ip_addr_ismulticast(groupaddr), return ERR_VAL;);
591   LWIP_ERROR("igmp_leavegroup: attempt to leave allsystems address", (!ip_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;);
592
593   /* loop through netif's */
594   netif = netif_list;
595   while (netif != NULL) {
596     /* Should we leave this interface ? */
597     if ((netif->flags & NETIF_FLAG_IGMP) && ((ip_addr_isany(ifaddr) || ip_addr_cmp(&(netif->ip_addr), ifaddr)))) {
598       /* find group */
599       group = igmp_lookfor_group(netif, groupaddr);
600
601       if (group != NULL) {
602         /* Only send a leave if the flag is set according to the state diagram */
603         LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: Leaving group: "));
604         ip_addr_debug_print(IGMP_DEBUG, groupaddr);
605         LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
606
607         /* If there is no other use of the group */
608         if (group->use <= 1) {
609           /* If we are the last reporter for this group */
610           if (group->last_reporter_flag) {
611             LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: sending leaving group\n"));
612             IGMP_STATS_INC(igmp.tx_leave);
613             igmp_send(group, IGMP_LEAVE_GROUP);
614           }
615           
616           /* Disable the group at the MAC level */
617           if (netif->igmp_mac_filter != NULL) {
618             LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: igmp_mac_filter(DEL "));
619             ip_addr_debug_print(IGMP_DEBUG, groupaddr);
620             LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif));
621             netif->igmp_mac_filter(netif, groupaddr, IGMP_DEL_MAC_FILTER);
622           }
623           
624           LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: remove group: "));
625           ip_addr_debug_print(IGMP_DEBUG, groupaddr);
626           LWIP_DEBUGF(IGMP_DEBUG, ("\n"));          
627           
628           /* Free the group */
629           igmp_remove_group(group);
630         } else {
631           /* Decrement group use */
632           group->use--;
633         }
634         /* Leave on this interface */
635         err = ERR_OK;
636       } else {
637         /* It's not a fatal error on "leavegroup" */
638         LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: not member of group\n"));
639       }
640     }
641     /* proceed to next network interface */
642     netif = netif->next;
643   }
644
645   return err;
646 }
647
648 /**
649  * The igmp timer function (both for NO_SYS=1 and =0)
650  * Should be called every IGMP_TMR_INTERVAL milliseconds (100 ms is default).
651  */
652 void
653 igmp_tmr(void)
654 {
655   struct igmp_group *group = igmp_group_list;
656
657   while (group != NULL) {
658     if (group->timer > 0) {
659       group->timer--;
660       if (group->timer == 0) {
661         igmp_timeout(group);
662       }
663     }
664     group = group->next;
665   }
666 }
667
668 /**
669  * Called if a timeout for one group is reached.
670  * Sends a report for this group.
671  *
672  * @param group an igmp_group for which a timeout is reached
673  */
674 static void
675 igmp_timeout(struct igmp_group *group)
676 {
677   /* If the state is IGMP_GROUP_DELAYING_MEMBER then we send a report for this group */
678   if (group->group_state == IGMP_GROUP_DELAYING_MEMBER) {
679     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_timeout: report membership for group with address "));
680     ip_addr_debug_print(IGMP_DEBUG, &(group->group_address));
681     LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", group->netif));
682
683     IGMP_STATS_INC(igmp.tx_report);
684     igmp_send(group, IGMP_V2_MEMB_REPORT);
685   }
686 }
687
688 /**
689  * Start a timer for an igmp group
690  *
691  * @param group the igmp_group for which to start a timer
692  * @param max_time the time in multiples of IGMP_TMR_INTERVAL (decrease with
693  *        every call to igmp_tmr())
694  */
695 static void
696 igmp_start_timer(struct igmp_group *group, u8_t max_time)
697 {
698   /* ensure the input value is > 0 */
699   if (max_time == 0) {
700     max_time = 1;
701   }
702 #ifdef LWIP_RAND
703   /* ensure the random value is > 0 */
704   group->timer = (LWIP_RAND() % (max_time - 1)) + 1;
705 #endif /* LWIP_RAND */
706 }
707
708 /**
709  * Delaying membership report for a group if necessary
710  *
711  * @param group the igmp_group for which "delaying" membership report
712  * @param maxresp query delay
713  */
714 static void
715 igmp_delaying_member(struct igmp_group *group, u8_t maxresp)
716 {
717   if ((group->group_state == IGMP_GROUP_IDLE_MEMBER) ||
718      ((group->group_state == IGMP_GROUP_DELAYING_MEMBER) &&
719       ((group->timer == 0) || (maxresp < group->timer)))) {
720     igmp_start_timer(group, maxresp);
721     group->group_state = IGMP_GROUP_DELAYING_MEMBER;
722   }
723 }
724
725
726 /**
727  * Sends an IP packet on a network interface. This function constructs the IP header
728  * and calculates the IP header checksum. If the source IP address is NULL,
729  * the IP address of the outgoing network interface is filled in as source address.
730  *
731  * @param p the packet to send (p->payload points to the data, e.g. next
732             protocol header; if dest == IP_HDRINCL, p already includes an IP
733             header and p->payload points to that IP header)
734  * @param src the source IP address to send from (if src == IP_ADDR_ANY, the
735  *         IP  address of the netif used to send is used as source address)
736  * @param dest the destination IP address to send the packet to
737  * @param ttl the TTL value to be set in the IP header
738  * @param proto the PROTOCOL to be set in the IP header
739  * @param netif the netif on which to send this packet
740  * @return ERR_OK if the packet was sent OK
741  *         ERR_BUF if p doesn't have enough space for IP/LINK headers
742  *         returns errors returned by netif->output
743  */
744 static err_t
745 igmp_ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, struct netif *netif)
746 {
747   /* This is the "router alert" option */
748   u16_t ra[2];
749   ra[0] = PP_HTONS(ROUTER_ALERT);
750   ra[1] = 0x0000; /* Router shall examine packet */
751   IGMP_STATS_INC(igmp.xmit);
752   return ip_output_if_opt(p, src, dest, IGMP_TTL, 0, IP_PROTO_IGMP, netif, ra, ROUTER_ALERTLEN);
753 }
754
755 /**
756  * Send an igmp packet to a specific group.
757  *
758  * @param group the group to which to send the packet
759  * @param type the type of igmp packet to send
760  */
761 static void
762 igmp_send(struct igmp_group *group, u8_t type)
763 {
764   struct pbuf*     p    = NULL;
765   struct igmp_msg* igmp = NULL;
766   ip_addr_t   src  = *IP_ADDR_ANY;
767   ip_addr_t*  dest = NULL;
768
769   /* IP header + "router alert" option + IGMP header */
770   p = pbuf_alloc(PBUF_TRANSPORT, IGMP_MINLEN, PBUF_RAM);
771   
772   if (p) {
773     igmp = (struct igmp_msg *)p->payload;
774     LWIP_ASSERT("igmp_send: check that first pbuf can hold struct igmp_msg",
775                (p->len >= sizeof(struct igmp_msg)));
776     ip_addr_copy(src, group->netif->ip_addr);
777      
778     if (type == IGMP_V2_MEMB_REPORT) {
779       dest = &(group->group_address);
780       ip_addr_copy(igmp->igmp_group_address, group->group_address);
781       group->last_reporter_flag = 1; /* Remember we were the last to report */
782     } else {
783       if (type == IGMP_LEAVE_GROUP) {
784         dest = &allrouters;
785         ip_addr_copy(igmp->igmp_group_address, group->group_address);
786       }
787     }
788
789     if ((type == IGMP_V2_MEMB_REPORT) || (type == IGMP_LEAVE_GROUP)) {
790       igmp->igmp_msgtype  = type;
791       igmp->igmp_maxresp  = 0;
792       igmp->igmp_checksum = 0;
793       igmp->igmp_checksum = inet_chksum(igmp, IGMP_MINLEN);
794
795       igmp_ip_output_if(p, &src, dest, group->netif);
796     }
797
798     pbuf_free(p);
799   } else {
800     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_send: not enough memory for igmp_send\n"));
801     IGMP_STATS_INC(igmp.memerr);
802   }
803 }
804
805 #endif /* LWIP_IGMP */