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