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