]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/ankh/lib/lwip/lib/contrib/src/core/ipv4/igmp.c
update
[l4.git] / l4 / pkg / ankh / lib / lwip / lib / contrib / 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                   0x9404
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_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_stop_timer(struct igmp_group *group);
143 static void   igmp_delaying_member(struct igmp_group *group, u8_t maxresp);
144 static err_t  igmp_ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, struct netif *netif);
145 static void   igmp_send(struct igmp_group *group, u8_t type);
146
147
148 static struct igmp_group* igmp_group_list;
149 static ip_addr_t     allsystems;
150 static ip_addr_t     allrouters;
151
152
153 /**
154  * Initialize the IGMP module
155  */
156 void
157 igmp_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  */
169 void
170 igmp_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  */
191 err_t
192 igmp_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  */
223 err_t
224 igmp_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  */
267 void
268 igmp_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  */
290 struct igmp_group *
291 igmp_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  */
316 struct igmp_group *
317 igmp_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 = 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  */
355 static err_t
356 igmp_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  */
389 void
390 igmp_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 = 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        }
447        else {
448          IGMP_STATS_INC(igmp.rx_general);
449        }
450
451        groupref = igmp_group_list;
452        while (groupref) {
453          /* Do not send messages on the all systems group address! */
454          if ((groupref->netif == inp) && (!(ip_addr_cmp(&(groupref->group_address), &allsystems)))) {
455            igmp_delaying_member(groupref, igmp->igmp_maxresp);
456          }
457          groupref = groupref->next;
458        }
459      } else {
460        /* IGMP_MEMB_QUERY to a specific group ? */
461        if (!ip_addr_isany(&igmp->igmp_group_address)) {
462          LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_MEMB_QUERY to a specific group "));
463          ip_addr_debug_print(IGMP_DEBUG, &igmp->igmp_group_address);
464          if (ip_addr_cmp(dest, &allsystems)) {
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-lookfor the group since we used dest last time */
467            group = igmp_lookfor_group(inp, &igmp->igmp_group_address);
468          } else {
469            LWIP_DEBUGF(IGMP_DEBUG, (" with the group address as destination [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp)));
470          }
471
472          if (group != NULL) {
473            IGMP_STATS_INC(igmp.rx_group);
474            igmp_delaying_member(group, igmp->igmp_maxresp);
475          }
476          else {
477            IGMP_STATS_INC(igmp.drop);
478          }
479          }
480        else {
481          IGMP_STATS_INC(igmp.proterr);
482        }
483      }
484      break;
485    }
486    case IGMP_V2_MEMB_REPORT: {
487      LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_V2_MEMB_REPORT\n"));
488      IGMP_STATS_INC(igmp.rx_report);
489      if (group->group_state == IGMP_GROUP_DELAYING_MEMBER) {
490        /* This is on a specific group we have already looked up */
491        group->timer = 0; /* stopped */
492        group->group_state = IGMP_GROUP_IDLE_MEMBER;
493        group->last_reporter_flag = 0;
494      }
495      break;
496    }
497    default: {
498      LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: unexpected msg %d in state %d on group %p on if %p\n",
499        igmp->igmp_msgtype, group->group_state, &group, group->netif));
500      IGMP_STATS_INC(igmp.proterr);
501      break;
502    }
503   }
504
505   pbuf_free(p);
506   return;
507 }
508
509 /**
510  * Join a group on one network interface.
511  *
512  * @param ifaddr ip address of the network interface which should join a new group
513  * @param groupaddr the ip address of the group which to join
514  * @return ERR_OK if group was joined on the netif(s), an err_t otherwise
515  */
516 err_t
517 igmp_joingroup(ip_addr_t *ifaddr, ip_addr_t *groupaddr)
518 {
519   err_t              err = ERR_VAL; /* no matching interface */
520   struct igmp_group *group;
521   struct netif      *netif;
522
523   /* make sure it is multicast address */
524   LWIP_ERROR("igmp_joingroup: attempt to join non-multicast address", ip_addr_ismulticast(groupaddr), return ERR_VAL;);
525   LWIP_ERROR("igmp_joingroup: attempt to join allsystems address", (!ip_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;);
526
527   /* loop through netif's */
528   netif = netif_list;
529   while (netif != NULL) {
530     /* Should we join this interface ? */
531     if ((netif->flags & NETIF_FLAG_IGMP) && ((ip_addr_isany(ifaddr) || ip_addr_cmp(&(netif->ip_addr), ifaddr)))) {
532       /* find group or create a new one if not found */
533       group = igmp_lookup_group(netif, groupaddr);
534
535       if (group != NULL) {
536         /* This should create a new group, check the state to make sure */
537         if (group->group_state != IGMP_GROUP_NON_MEMBER) {
538           LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: join to group not in state IGMP_GROUP_NON_MEMBER\n"));
539         } else {
540           /* OK - it was new group */
541           LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: join to new group: "));
542           ip_addr_debug_print(IGMP_DEBUG, groupaddr);
543           LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
544
545           /* If first use of the group, allow the group at the MAC level */
546           if ((group->use==0) && (netif->igmp_mac_filter != NULL)) {
547             LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: igmp_mac_filter(ADD "));
548             ip_addr_debug_print(IGMP_DEBUG, groupaddr);
549             LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif));
550             netif->igmp_mac_filter(netif, groupaddr, IGMP_ADD_MAC_FILTER);
551           }
552
553           IGMP_STATS_INC(igmp.tx_join);
554           igmp_send(group, IGMP_V2_MEMB_REPORT);
555
556           igmp_start_timer(group, IGMP_JOIN_DELAYING_MEMBER_TMR);
557
558           /* Need to work out where this timer comes from */
559           group->group_state = IGMP_GROUP_DELAYING_MEMBER;
560         }
561         /* Increment group use */
562         group->use++;
563         /* Join on this interface */
564         err = ERR_OK;
565       } else {
566         /* Return an error even if some network interfaces are joined */
567         /** @todo undo any other netif already joined */
568         LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: Not enought memory to join to group\n"));
569         return ERR_MEM;
570       }
571     }
572     /* proceed to next network interface */
573     netif = netif->next;
574   }
575
576   return err;
577 }
578
579 /**
580  * Leave a group on one network interface.
581  *
582  * @param ifaddr ip address of the network interface which should leave a group
583  * @param groupaddr the ip address of the group which to leave
584  * @return ERR_OK if group was left on the netif(s), an err_t otherwise
585  */
586 err_t
587 igmp_leavegroup(ip_addr_t *ifaddr, ip_addr_t *groupaddr)
588 {
589   err_t              err = ERR_VAL; /* no matching interface */
590   struct igmp_group *group;
591   struct netif      *netif;
592
593   /* make sure it is multicast address */
594   LWIP_ERROR("igmp_leavegroup: attempt to leave non-multicast address", ip_addr_ismulticast(groupaddr), return ERR_VAL;);
595   LWIP_ERROR("igmp_leavegroup: attempt to leave allsystems address", (!ip_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;);
596
597   /* loop through netif's */
598   netif = netif_list;
599   while (netif != NULL) {
600     /* Should we leave this interface ? */
601     if ((netif->flags & NETIF_FLAG_IGMP) && ((ip_addr_isany(ifaddr) || ip_addr_cmp(&(netif->ip_addr), ifaddr)))) {
602       /* find group */
603       group = igmp_lookfor_group(netif, groupaddr);
604
605       if (group != NULL) {
606         /* Only send a leave if the flag is set according to the state diagram */
607         LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: Leaving group: "));
608         ip_addr_debug_print(IGMP_DEBUG, groupaddr);
609         LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
610
611         /* If there is no other use of the group */
612         if (group->use <= 1) {
613           /* If we are the last reporter for this group */
614           if (group->last_reporter_flag) {
615             LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: sending leaving group\n"));
616             IGMP_STATS_INC(igmp.tx_leave);
617             igmp_send(group, IGMP_LEAVE_GROUP);
618           }
619           
620           /* Disable the group at the MAC level */
621           if (netif->igmp_mac_filter != NULL) {
622             LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: igmp_mac_filter(DEL "));
623             ip_addr_debug_print(IGMP_DEBUG, groupaddr);
624             LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif));
625             netif->igmp_mac_filter(netif, groupaddr, IGMP_DEL_MAC_FILTER);
626           }
627           
628           LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: remove group: "));
629           ip_addr_debug_print(IGMP_DEBUG, groupaddr);
630           LWIP_DEBUGF(IGMP_DEBUG, ("\n"));          
631           
632           /* Free the group */
633           igmp_remove_group(group);
634         } else {
635           /* Decrement group use */
636           group->use--;
637         }
638         /* Leave on this interface */
639         err = ERR_OK;
640       } else {
641         /* It's not a fatal error on "leavegroup" */
642         LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: not member of group\n"));
643       }
644     }
645     /* proceed to next network interface */
646     netif = netif->next;
647   }
648
649   return err;
650 }
651
652 /**
653  * The igmp timer function (both for NO_SYS=1 and =0)
654  * Should be called every IGMP_TMR_INTERVAL milliseconds (100 ms is default).
655  */
656 void
657 igmp_tmr(void)
658 {
659   struct igmp_group *group = igmp_group_list;
660
661   while (group != NULL) {
662     if (group->timer > 0) {
663       group->timer--;
664       if (group->timer == 0) {
665         igmp_timeout(group);
666       }
667     }
668     group = group->next;
669   }
670 }
671
672 /**
673  * Called if a timeout for one group is reached.
674  * Sends a report for this group.
675  *
676  * @param group an igmp_group for which a timeout is reached
677  */
678 static void
679 igmp_timeout(struct igmp_group *group)
680 {
681   /* If the state is IGMP_GROUP_DELAYING_MEMBER then we send a report for this group */
682   if (group->group_state == IGMP_GROUP_DELAYING_MEMBER) {
683     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_timeout: report membership for group with address "));
684     ip_addr_debug_print(IGMP_DEBUG, &(group->group_address));
685     LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", group->netif));
686
687     IGMP_STATS_INC(igmp.tx_report);
688     igmp_send(group, IGMP_V2_MEMB_REPORT);
689   }
690 }
691
692 /**
693  * Start a timer for an igmp group
694  *
695  * @param group the igmp_group for which to start a timer
696  * @param max_time the time in multiples of IGMP_TMR_INTERVAL (decrease with
697  *        every call to igmp_tmr())
698  */
699 static void
700 igmp_start_timer(struct igmp_group *group, u8_t max_time)
701 {
702   /* ensure the input value is > 0 */
703   if (max_time == 0) {
704     max_time = 1;
705   }
706   /* ensure the random value is > 0 */
707   group->timer = (LWIP_RAND() % (max_time - 1)) + 1;
708 }
709
710 /**
711  * Stop a timer for an igmp_group
712  *
713  * @param group the igmp_group for which to stop the timer
714  */
715 static void
716 igmp_stop_timer(struct igmp_group *group)
717 {
718   group->timer = 0;
719 }
720
721 /**
722  * Delaying membership report for a group if necessary
723  *
724  * @param group the igmp_group for which "delaying" membership report
725  * @param maxresp query delay
726  */
727 static void
728 igmp_delaying_member(struct igmp_group *group, u8_t maxresp)
729 {
730   if ((group->group_state == IGMP_GROUP_IDLE_MEMBER) ||
731      ((group->group_state == IGMP_GROUP_DELAYING_MEMBER) &&
732       ((group->timer == 0) || (maxresp < group->timer)))) {
733     igmp_start_timer(group, maxresp);
734     group->group_state = IGMP_GROUP_DELAYING_MEMBER;
735   }
736 }
737
738
739 /**
740  * Sends an IP packet on a network interface. This function constructs the IP header
741  * and calculates the IP header checksum. If the source IP address is NULL,
742  * the IP address of the outgoing network interface is filled in as source address.
743  *
744  * @param p the packet to send (p->payload points to the data, e.g. next
745             protocol header; if dest == IP_HDRINCL, p already includes an IP
746             header and p->payload points to that IP header)
747  * @param src the source IP address to send from (if src == IP_ADDR_ANY, the
748  *         IP  address of the netif used to send is used as source address)
749  * @param dest the destination IP address to send the packet to
750  * @param ttl the TTL value to be set in the IP header
751  * @param proto the PROTOCOL to be set in the IP header
752  * @param netif the netif on which to send this packet
753  * @return ERR_OK if the packet was sent OK
754  *         ERR_BUF if p doesn't have enough space for IP/LINK headers
755  *         returns errors returned by netif->output
756  */
757 static err_t
758 igmp_ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, struct netif *netif)
759 {
760   /* This is the "router alert" option */
761   u16_t ra[2];
762   ra[0] = PP_HTONS(ROUTER_ALERT);
763   ra[1] = 0x0000; /* Router shall examine packet */
764   IGMP_STATS_INC(igmp.xmit);
765   return ip_output_if_opt(p, src, dest, IGMP_TTL, 0, IP_PROTO_IGMP, netif, ra, ROUTER_ALERTLEN);
766 }
767
768 /**
769  * Send an igmp packet to a specific group.
770  *
771  * @param group the group to which to send the packet
772  * @param type the type of igmp packet to send
773  */
774 static void
775 igmp_send(struct igmp_group *group, u8_t type)
776 {
777   struct pbuf*     p    = NULL;
778   struct igmp_msg* igmp = NULL;
779   ip_addr_t   src  = *IP_ADDR_ANY;
780   ip_addr_t*  dest = NULL;
781
782   /* IP header + "router alert" option + IGMP header */
783   p = pbuf_alloc(PBUF_TRANSPORT, IGMP_MINLEN, PBUF_RAM);
784   
785   if (p) {
786     igmp = p->payload;
787     LWIP_ASSERT("igmp_send: check that first pbuf can hold struct igmp_msg",
788                (p->len >= sizeof(struct igmp_msg)));
789     ip_addr_copy(src, group->netif->ip_addr);
790      
791     if (type == IGMP_V2_MEMB_REPORT) {
792       dest = &(group->group_address);
793       ip_addr_copy(igmp->igmp_group_address, group->group_address);
794       group->last_reporter_flag = 1; /* Remember we were the last to report */
795     } else {
796       if (type == IGMP_LEAVE_GROUP) {
797         dest = &allrouters;
798         ip_addr_copy(igmp->igmp_group_address, group->group_address);
799       }
800     }
801
802     if ((type == IGMP_V2_MEMB_REPORT) || (type == IGMP_LEAVE_GROUP)) {
803       igmp->igmp_msgtype  = type;
804       igmp->igmp_maxresp  = 0;
805       igmp->igmp_checksum = 0;
806       igmp->igmp_checksum = inet_chksum(igmp, IGMP_MINLEN);
807
808       igmp_ip_output_if(p, &src, dest, group->netif);
809     }
810
811     pbuf_free(p);
812   } else {
813     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_send: not enough memory for igmp_send\n"));
814     IGMP_STATS_INC(igmp.memerr);
815   }
816 }
817
818 #endif /* LWIP_IGMP */