Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


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