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