• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_ERROR("igmp_start: netif != NULL", (netif != NULL), return ERR_ARG);
133   LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: starting IGMP processing on if %p\n", (void *)netif));
134 
135   group = igmp_lookup_group(netif, &allsystems);
136 
137   if (group != NULL) {
138     group->group_state = IGMP_GROUP_IDLE_MEMBER;
139     group->use++;
140 
141 #if LWIP_LINK_MCAST_FILTER
142     /* Allow the igmp messages at the MAC level */
143     if (netif->igmp_mac_filter != NULL) {
144       LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: igmp_mac_filter(ADD "));
145       ip4_addr_debug_print_val(IGMP_DEBUG, allsystems);
146       LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", (void *)netif));
147       (void)netif->igmp_mac_filter(netif, &allsystems, NETIF_ADD_MAC_FILTER);
148     }
149 #endif /* LWIP_LINK_MCAST_FILTER */
150 
151     return ERR_OK;
152   }
153 
154   return ERR_MEM;
155 }
156 
157 /**
158  * Stop IGMP processing on interface
159  *
160  * @param netif network interface on which stop IGMP processing
161  */
162 err_t
igmp_stop(struct netif * netif)163 igmp_stop(struct netif *netif)
164 {
165   struct igmp_group *group = NULL;
166   LWIP_ERROR("igmp_stop: Invalid argument netif is NULL", (netif != NULL), return ERR_ARG);
167 
168   group = netif_igmp_data(netif);
169 
170   netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_IGMP, NULL);
171 
172   while (group != NULL) {
173     struct igmp_group *next = group->next; /* avoid use-after-free below */
174 
175 #if LWIP_LINK_MCAST_FILTER
176     /* disable the group at the MAC level */
177     if (netif->igmp_mac_filter != NULL) {
178       LWIP_DEBUGF(IGMP_DEBUG, ("igmp_stop: igmp_mac_filter(DEL "));
179 #ifdef  LWIP_DEV_DEBUG
180       ip4_addr_debug_print_val(IGMP_DEBUG, group->group_address);
181 #endif
182       LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", (void *)netif));
183       (void)netif->igmp_mac_filter(netif, &(group->group_address), NETIF_DEL_MAC_FILTER);
184     }
185 #endif /* LWIP_LINK_MCAST_FILTER */
186 
187     /* free group */
188     memp_free(MEMP_IGMP_GROUP, group);
189 
190     /* move to "next" */
191     group = next;
192   }
193   return ERR_OK;
194 }
195 
196 /**
197  * Report IGMP memberships for this interface
198  *
199  * @param netif network interface on which report IGMP memberships
200  */
201 void
igmp_report_groups(struct netif * netif)202 igmp_report_groups(struct netif *netif)
203 {
204   struct igmp_group *group = NULL;
205   LWIP_ERROR("igmp_report_groups: Invalid argument netif is NULL", (netif != NULL), return);
206   group = netif_igmp_data(netif);
207 
208   LWIP_DEBUGF(IGMP_DEBUG, ("igmp_report_groups: sending IGMP reports on if %p\n", (void *)netif));
209 
210   /* Skip the first group in the list, it is always the allsystems group added in igmp_start() */
211   if (group != NULL) {
212     group = group->next;
213   }
214 
215   while (group != NULL) {
216     igmp_delaying_member(group, IGMP_JOIN_DELAYING_MEMBER_TMR);
217     group = group->next;
218   }
219 }
220 
221 /**
222  * Search for a group in the netif's igmp group list
223  *
224  * @param ifp the network interface for which to look
225  * @param addr the group ip address to search for
226  * @return a struct igmp_group* if the group has been found,
227  *         NULL if the group wasn't found.
228  */
229 struct igmp_group *
igmp_lookfor_group(struct netif * ifp,const ip4_addr_t * addr)230 igmp_lookfor_group(struct netif *ifp, const ip4_addr_t *addr)
231 {
232   struct igmp_group *group = NULL;
233   LWIP_ERROR("igmp_lookfor_group: Invalid argument ifp is NULL", (ifp != NULL), return NULL);
234   LWIP_ERROR("igmp_lookfor_group: Invalid argument addr is NULL", (addr != NULL), return NULL);
235 
236   group = netif_igmp_data(ifp);
237 
238   while (group != NULL) {
239     if (ip4_addr_cmp(&(group->group_address), addr)) {
240       return group;
241     }
242     group = group->next;
243   }
244 
245   /* to be clearer, we return NULL here instead of
246    * 'group' (which is also NULL at this point).
247    */
248   return NULL;
249 }
250 
251 /**
252  * Search for a specific igmp group and create a new one if not found-
253  *
254  * @param ifp the network interface for which to look
255  * @param addr the group ip address to search
256  * @return a struct igmp_group*,
257  *         NULL on memory error.
258  */
259 static struct igmp_group *
igmp_lookup_group(struct netif * ifp,const ip4_addr_t * addr)260 igmp_lookup_group(struct netif *ifp, const ip4_addr_t *addr)
261 {
262   struct igmp_group *group;
263   struct igmp_group *list_head = netif_igmp_data(ifp);
264 
265   /* Search if the group already exists */
266   group = igmp_lookfor_group(ifp, addr);
267   if (group != NULL) {
268     /* Group already exists. */
269     return group;
270   }
271 
272   /* Group doesn't exist yet, create a new one */
273   group = (struct igmp_group *)memp_malloc(MEMP_IGMP_GROUP);
274   if (group != NULL) {
275     ip4_addr_set(&(group->group_address), addr);
276     group->timer              = 0; /* Not running */
277     group->group_state        = IGMP_GROUP_NON_MEMBER;
278     group->last_reporter_flag = 0;
279     group->use                = 0;
280 
281     /* Ensure allsystems group is always first in list */
282     if (list_head == NULL) {
283       /* this is the first entry in linked list */
284       LWIP_ASSERT("igmp_lookup_group: first group must be allsystems",
285                   (ip4_addr_cmp(addr, &allsystems) != 0));
286       group->next = NULL;
287       netif_set_client_data(ifp, LWIP_NETIF_CLIENT_DATA_INDEX_IGMP, group);
288     } else {
289       /* append _after_ first entry */
290       LWIP_ASSERT("igmp_lookup_group: all except first group must not be allsystems",
291                   (ip4_addr_cmp(addr, &allsystems) == 0));
292       group->next = list_head->next;
293       list_head->next = group;
294     }
295   }
296 
297   LWIP_DEBUGF(IGMP_DEBUG, ("igmp_lookup_group: %sallocated a new group with address ", (group ? "" : "impossible to ")));
298   ip4_addr_debug_print(IGMP_DEBUG, addr);
299   LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", (void *)ifp));
300 
301   return group;
302 }
303 
304 /**
305  * Remove a group from netif's igmp group list, but don't free it yet
306  *
307  * @param group the group to remove from the netif's igmp group list
308  * @return ERR_OK if group was removed from the list, an err_t otherwise
309  */
310 static err_t
igmp_remove_group(struct netif * netif,struct igmp_group * group)311 igmp_remove_group(struct netif *netif, struct igmp_group *group)
312 {
313   err_t err = ERR_OK;
314   struct igmp_group *tmp_group;
315 
316   /* Skip the first group in the list, it is always the allsystems group added in igmp_start() */
317   for (tmp_group = netif_igmp_data(netif); tmp_group != NULL; tmp_group = tmp_group->next) {
318     if (tmp_group->next == group) {
319       tmp_group->next = group->next;
320       break;
321     }
322   }
323   /* Group not found in netif's igmp group list */
324   if (tmp_group == NULL) {
325     err = ERR_ARG;
326   }
327 
328   return err;
329 }
330 
331 /**
332  * Called from ip_input() if a new IGMP packet is received.
333  *
334  * @param p received igmp packet, p->payload pointing to the igmp header
335  * @param inp network interface on which the packet was received
336  * @param dest destination ip address of the igmp packet
337  */
338 void
igmp_input(struct pbuf * p,struct netif * inp,const ip4_addr_t * dest)339 igmp_input(struct pbuf *p, struct netif *inp, const ip4_addr_t *dest)
340 {
341   struct igmp_msg   *igmp;
342   struct igmp_group *group;
343   struct igmp_group *groupref;
344 
345   IGMP_STATS_INC(igmp.recv);
346 
347   /* Note that the length CAN be greater than 8 but only 8 are used - All are included in the checksum */
348   if (p->len < IGMP_MINLEN) {
349     pbuf_free(p);
350     IGMP_STATS_INC(igmp.lenerr);
351     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: length error\n"));
352     return;
353   }
354 
355   LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: message from "));
356   ip4_addr_debug_print_val(IGMP_DEBUG, ip4_current_header()->src);
357   LWIP_DEBUGF(IGMP_DEBUG, (" to address "));
358   ip4_addr_debug_print_val(IGMP_DEBUG, ip4_current_header()->dest);
359   LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", (void *)inp));
360 
361   /* Now calculate and check the checksum */
362   igmp = (struct igmp_msg *)p->payload;
363   if (inet_chksum(igmp, p->len)) {
364     pbuf_free(p);
365     IGMP_STATS_INC(igmp.chkerr);
366     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: checksum error\n"));
367     return;
368   }
369 
370   /* Packet is ok so find an existing group */
371   group = igmp_lookfor_group(inp, dest); /* use the destination IP address of incoming packet */
372 
373   /* If group can be found or create... */
374   if (!group) {
375     pbuf_free(p);
376     IGMP_STATS_INC(igmp.drop);
377     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP frame not for us\n"));
378     return;
379   }
380 
381   /* NOW ACT ON THE INCOMING MESSAGE TYPE... */
382   switch (igmp->igmp_msgtype) {
383     case IGMP_MEMB_QUERY:
384       /* IGMP_MEMB_QUERY to the "all systems" address ? */
385       if ((ip4_addr_cmp(dest, &allsystems)) && ip4_addr_isany(&igmp->igmp_group_address)) {
386         /* THIS IS THE GENERAL QUERY */
387         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)));
388 
389         if (igmp->igmp_maxresp == 0) {
390           IGMP_STATS_INC(igmp.rx_v1);
391           LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: got an all hosts query with time== 0 - this is V1 and not implemented - treat as v2\n"));
392           igmp->igmp_maxresp = IGMP_V1_DELAYING_MEMBER_TMR;
393         } else {
394           IGMP_STATS_INC(igmp.rx_general);
395         }
396 
397         groupref = netif_igmp_data(inp);
398 
399         /* Do not send messages on the all systems group address! */
400         /* Skip the first group in the list, it is always the allsystems group added in igmp_start() */
401         if (groupref != NULL) {
402           groupref = groupref->next;
403         }
404 
405         while (groupref) {
406           igmp_delaying_member(groupref, igmp->igmp_maxresp);
407           groupref = groupref->next;
408         }
409       } else {
410         /* IGMP_MEMB_QUERY to a specific group ? */
411         if (!ip4_addr_isany(&igmp->igmp_group_address)) {
412           LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_MEMB_QUERY to a specific group "));
413           ip4_addr_debug_print_val(IGMP_DEBUG, igmp->igmp_group_address);
414           if (ip4_addr_cmp(dest, &allsystems)) {
415             ip4_addr_t groupaddr;
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-look for the group since we used dest last time */
418             ip4_addr_copy(groupaddr, igmp->igmp_group_address);
419             group = igmp_lookfor_group(inp, &groupaddr);
420           } else {
421             LWIP_DEBUGF(IGMP_DEBUG, (" with the group address as destination [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp)));
422           }
423 
424           if (group != NULL) {
425             IGMP_STATS_INC(igmp.rx_group);
426             igmp_delaying_member(group, igmp->igmp_maxresp);
427           } else {
428             IGMP_STATS_INC(igmp.drop);
429           }
430         } else {
431           IGMP_STATS_INC(igmp.proterr);
432         }
433       }
434       break;
435     case IGMP_V2_MEMB_REPORT:
436       LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_V2_MEMB_REPORT\n"));
437       IGMP_STATS_INC(igmp.rx_report);
438       if (group->group_state == IGMP_GROUP_DELAYING_MEMBER) {
439         /* This is on a specific group we have already looked up */
440         group->timer = 0; /* stopped */
441         group->group_state = IGMP_GROUP_IDLE_MEMBER;
442         group->last_reporter_flag = 0;
443       }
444       break;
445     default:
446       LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: unexpected msg %d in state %d on group %p on if %p\n",
447                                igmp->igmp_msgtype, group->group_state, (void *)&group, (void *)inp));
448       IGMP_STATS_INC(igmp.proterr);
449       break;
450   }
451 
452   pbuf_free(p);
453   return;
454 }
455 
456 /**
457  * @ingroup igmp
458  * Join a group on one network interface.
459  *
460  * @param ifaddr ip address of the network interface which should join a new group
461  * @param groupaddr the ip address of the group which to join
462  * @return ERR_OK if group was joined on the netif(s), an err_t otherwise
463  */
464 err_t
igmp_joingroup(const ip4_addr_t * ifaddr,const ip4_addr_t * groupaddr)465 igmp_joingroup(const ip4_addr_t *ifaddr, const ip4_addr_t *groupaddr)
466 {
467   err_t err = ERR_VAL; /* no matching interface */
468   struct netif *netif;
469 
470   LWIP_ASSERT_CORE_LOCKED();
471   LWIP_ERROR("igmp_joingroup: groupaddr != NULL", (groupaddr != NULL), return ERR_ARG);
472   /* make sure it is multicast address */
473   LWIP_ERROR("igmp_joingroup: attempt to join non-multicast address", ip4_addr_ismulticast(groupaddr), return ERR_VAL;);
474   LWIP_ERROR("igmp_joingroup: attempt to join allsystems address", (!ip4_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;);
475 
476   /* loop through netif's */
477   NETIF_FOREACH(netif) {
478     /* Should we join this interface ? */
479     if ((netif->flags & NETIF_FLAG_IGMP) && ((ip4_addr_isany(ifaddr) || ip4_addr_cmp(netif_ip4_addr(netif), ifaddr)))) {
480       err = igmp_joingroup_netif(netif, groupaddr);
481       if (err != ERR_OK) {
482         /* Return an error even if some network interfaces are joined */
483         /** @todo undo any other netif already joined */
484         return err;
485       }
486     }
487   }
488 
489   return err;
490 }
491 
492 /**
493  * @ingroup igmp
494  * Join a group on one network interface.
495  *
496  * @param netif the network interface which should join a new group
497  * @param groupaddr the ip address of the group which to join
498  * @return ERR_OK if group was joined on the netif, an err_t otherwise
499  */
500 err_t
igmp_joingroup_netif(struct netif * netif,const ip4_addr_t * groupaddr)501 igmp_joingroup_netif(struct netif *netif, const ip4_addr_t *groupaddr)
502 {
503   struct igmp_group *group = NULL;
504 
505   LWIP_ERROR("igmp_joingroup_netif: Invalid argument netif is NULL", (netif != NULL), return ERR_ARG);
506   LWIP_ERROR("igmp_joingroup_netif: Invalid argument groupaddr is NULL", (groupaddr != NULL), return ERR_ARG);
507 
508   /* make sure it is multicast address */
509   LWIP_ERROR("igmp_joingroup_netif: attempt to join non-multicast address", ip4_addr_ismulticast(groupaddr),
510              return ERR_VAL);
511   LWIP_ERROR("igmp_joingroup_netif: attempt to join allsystems address", (!ip4_addr_cmp(groupaddr, &allsystems)),
512              return ERR_VAL);
513 
514   /* make sure it is an igmp-enabled netif */
515   LWIP_ERROR("igmp_joingroup_netif: attempt to join on non-IGMP netif", netif->flags & NETIF_FLAG_IGMP, return ERR_VAL);
516 
517   /* find group or create a new one if not found */
518   group = igmp_lookup_group(netif, groupaddr);
519 
520   if (group != NULL) {
521     /* This should create a new group, check the state to make sure */
522     if (group->group_state != IGMP_GROUP_NON_MEMBER) {
523       LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup_netif: join to group not in state IGMP_GROUP_NON_MEMBER\n"));
524     } else {
525       /* OK - it was new group */
526       LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup_netif: join to new group: "));
527       ip4_addr_debug_print(IGMP_DEBUG, groupaddr);
528       LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
529 
530 #if LWIP_LINK_MCAST_FILTER
531       /* If first use of the group, allow the group at the MAC level */
532       if ((group->use == 0) && (netif->igmp_mac_filter != NULL)) {
533         LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup_netif: igmp_mac_filter(ADD "));
534         ip4_addr_debug_print(IGMP_DEBUG, groupaddr);
535         LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", (void *)netif));
536         (void)netif->igmp_mac_filter(netif, groupaddr, NETIF_ADD_MAC_FILTER);
537       }
538 #endif /* LWIP_LINK_MCAST_FILTER */
539 
540       IGMP_STATS_INC(igmp.tx_join);
541       igmp_send(netif, group, IGMP_V2_MEMB_REPORT);
542 
543       igmp_start_timer(group, IGMP_JOIN_DELAYING_MEMBER_TMR);
544 
545       /* Need to work out where this timer comes from */
546       group->group_state = IGMP_GROUP_DELAYING_MEMBER;
547 #if (LWIP_IGMP_REPORT_TIMES > 2)
548       group->report_cnt = 1;
549 #endif
550     }
551     /* Increment group use */
552     group->use++;
553     /* Join on this interface */
554     return ERR_OK;
555   } else {
556     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup_netif: Not enough memory to join to group\n"));
557     return ERR_MEM;
558   }
559 }
560 
561 /**
562  * @ingroup igmp
563  * Leave a group on one network interface.
564  *
565  * @param ifaddr ip address of the network interface which should leave a group
566  * @param groupaddr the ip address of the group which to leave
567  * @return ERR_OK if group was left on the netif(s), an err_t otherwise
568  */
569 err_t
igmp_leavegroup(const ip4_addr_t * ifaddr,const ip4_addr_t * groupaddr)570 igmp_leavegroup(const ip4_addr_t *ifaddr, const ip4_addr_t *groupaddr)
571 {
572   err_t err = ERR_VAL; /* no matching interface */
573   struct netif *netif = NULL;
574 
575   LWIP_ASSERT_CORE_LOCKED();
576   LWIP_ERROR("igmp_leavegroup:Invalid argument groupaddr is NULL \n", (groupaddr != NULL), return ERR_ARG);
577   LWIP_ERROR("igmp_leavegroup:Invalid argument ifaddr is NULL \n", (ifaddr != NULL), return ERR_ARG);
578 
579   /* make sure it is multicast address */
580   LWIP_ERROR("igmp_leavegroup: attempt to leave non-multicast address", ip4_addr_ismulticast(groupaddr), return ERR_VAL;);
581   LWIP_ERROR("igmp_leavegroup: attempt to leave allsystems address", (!ip4_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;);
582 
583   /* loop through netif's */
584   NETIF_FOREACH(netif) {
585     /* Should we leave this interface ? */
586     if ((netif->flags & NETIF_FLAG_IGMP) && ((ip4_addr_isany(ifaddr) || ip4_addr_cmp(netif_ip4_addr(netif), ifaddr)))) {
587       err_t res = igmp_leavegroup_netif(netif, groupaddr);
588       if (err != ERR_OK) {
589         /* Store this result if we have not yet gotten a success */
590         err = res;
591       }
592     }
593   }
594 
595   return err;
596 }
597 
598 /**
599  * @ingroup igmp
600  * Leave a group on one network interface.
601  *
602  * @param netif the network interface which should leave a group
603  * @param groupaddr the ip address of the group which to leave
604  * @return ERR_OK if group was left on the netif, an err_t otherwise
605  */
606 err_t
igmp_leavegroup_netif(struct netif * netif,const ip4_addr_t * groupaddr)607 igmp_leavegroup_netif(struct netif *netif, const ip4_addr_t *groupaddr)
608 {
609   struct igmp_group *group = NULL;
610   LWIP_ASSERT_CORE_LOCKED();
611   LWIP_ERROR("igmp_leavegroup_netif: invalid argument netif is NULL", (netif != NULL), return ERR_ARG);
612   LWIP_ERROR("igmp_leavegroup_netif: invalid argument groupaddr is NULL", (groupaddr != NULL), return ERR_ARG);
613   /* make sure it is multicast address */
614   LWIP_ERROR("igmp_leavegroup_netif: attempt to leave non-multicast address", ip4_addr_ismulticast(groupaddr),
615              return ERR_VAL);
616   LWIP_ERROR("igmp_leavegroup_netif: attempt to leave allsystems address", (!ip4_addr_cmp(groupaddr, &allsystems)),
617              return ERR_VAL);
618 
619   /* make sure it is an igmp-enabled netif */
620   LWIP_ERROR("igmp_leavegroup_netif: attempt to leave on non-IGMP netif", netif->flags & NETIF_FLAG_IGMP,
621              return ERR_VAL);
622 
623   /* find group */
624   group = igmp_lookfor_group(netif, groupaddr);
625 
626   if (group != NULL) {
627     /* Only send a leave if the flag is set according to the state diagram */
628     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup_netif: Leaving group: "));
629     ip4_addr_debug_print(IGMP_DEBUG, groupaddr);
630     LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
631 
632     /* If there is no other use of the group */
633     if (group->use <= 1) {
634       /* Remove the group from the list */
635       igmp_remove_group(netif, group);
636 
637       /* If we are the last reporter for this group */
638       if (group->last_reporter_flag) {
639         LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup_netif: sending leaving group\n"));
640         IGMP_STATS_INC(igmp.tx_leave);
641         igmp_send(netif, group, IGMP_LEAVE_GROUP);
642       }
643 
644 #if LWIP_LINK_MCAST_FILTER
645       /* Disable the group at the MAC level */
646       if (netif->igmp_mac_filter != NULL) {
647         LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup_netif: igmp_mac_filter(DEL "));
648         ip4_addr_debug_print(IGMP_DEBUG, groupaddr);
649         LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", (void *)netif));
650         (void)netif->igmp_mac_filter(netif, groupaddr, NETIF_DEL_MAC_FILTER);
651       }
652 #endif /* LWIP_LINK_MCAST_FILTER */
653 
654       /* Free group struct */
655       memp_free(MEMP_IGMP_GROUP, group);
656     } else {
657       /* Decrement group use */
658       group->use--;
659     }
660     return ERR_OK;
661   } else {
662     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup_netif: not member of group\n"));
663     return ERR_VAL;
664   }
665 }
666 
667 /**
668  * The igmp timer function (both for NO_SYS=1 and =0)
669  * Should be called every IGMP_TMR_INTERVAL milliseconds (100 ms is default).
670  */
671 void
igmp_tmr(void)672 igmp_tmr(void)
673 {
674   struct netif *netif;
675 
676   NETIF_FOREACH(netif) {
677     struct igmp_group *group = netif_igmp_data(netif);
678 
679     while (group != NULL) {
680       if (group->timer > 0) {
681         group->timer--;
682         if (group->timer == 0) {
683           igmp_timeout(netif, group);
684         }
685       }
686       group = group->next;
687     }
688   }
689 }
690 
691 #if LWIP_LOWPOWER
692 #include "lwip/lowpower.h"
693 u32_t
igmp_tmr_tick(void)694 igmp_tmr_tick(void)
695 {
696   struct netif *netif;
697   u32_t tick = 0;
698 
699   NETIF_FOREACH(netif) {
700     struct igmp_group *group = netif_igmp_data(netif);
701     while (group != NULL) {
702       if (group->timer > 0) {
703         SET_TMR_TICK(tick, group->timer);
704       }
705       group = group->next;
706     }
707   }
708 
709   LOWPOWER_DEBUG(("%s tmr tick: %u\n", __func__, tick));
710   return tick;
711 }
712 #endif
713 
714 /**
715  * Called if a timeout for one group is reached.
716  * Sends a report for this group.
717  *
718  * @param group an igmp_group for which a timeout is reached
719  */
720 static void
igmp_timeout(struct netif * netif,struct igmp_group * group)721 igmp_timeout(struct netif *netif, struct igmp_group *group)
722 {
723   /* If the state is IGMP_GROUP_DELAYING_MEMBER then we send a report for this group
724      (unless it is the allsystems group) */
725   if ((group->group_state == IGMP_GROUP_DELAYING_MEMBER) &&
726       (!(ip4_addr_cmp(&(group->group_address), &allsystems)))) {
727     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_timeout: report membership for group with address "));
728     ip4_addr_debug_print_val(IGMP_DEBUG, group->group_address);
729     LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", (void *)netif));
730 
731     IGMP_STATS_INC(igmp.tx_report);
732     igmp_send(netif, group, IGMP_V2_MEMB_REPORT);
733 #if (LWIP_IGMP_REPORT_TIMES > 2)
734     group->report_cnt++;
735     if (group->report_cnt < LWIP_IGMP_REPORT_TIMES) {
736       igmp_start_timer(group, group->report_timer);
737     } else
738 #endif
739     {
740       group->group_state = IGMP_GROUP_IDLE_MEMBER;
741     }
742   }
743 }
744 
745 /**
746  * Start a timer for an igmp group
747  *
748  * @param group the igmp_group for which to start a timer
749  * @param max_time the time in multiples of IGMP_TMR_INTERVAL (decrease with
750  *        every call to igmp_tmr())
751  */
752 static void
igmp_start_timer(struct igmp_group * group,u8_t max_time)753 igmp_start_timer(struct igmp_group *group, u8_t max_time)
754 {
755 #ifdef LWIP_RAND
756   group->timer = (u16_t)(max_time > 2 ? (LWIP_RAND() % max_time) : 1);
757 #else /* LWIP_RAND */
758   /* ATTENTION: use this only if absolutely necessary! */
759   group->timer = max_time / 2;
760 #endif /* LWIP_RAND */
761 
762   if (group->timer == 0) {
763     group->timer = 1;
764   }
765 #if (LWIP_IGMP_REPORT_TIMES > 2)
766   group->report_timer = max_time;
767 #endif
768 }
769 
770 /**
771  * Delaying membership report for a group if necessary
772  *
773  * @param group the igmp_group for which "delaying" membership report
774  * @param maxresp query delay
775  */
776 static void
igmp_delaying_member(struct igmp_group * group,u8_t maxresp)777 igmp_delaying_member(struct igmp_group *group, u8_t maxresp)
778 {
779   if ((group->group_state == IGMP_GROUP_IDLE_MEMBER) ||
780       ((group->group_state == IGMP_GROUP_DELAYING_MEMBER) &&
781        ((group->timer == 0) || (maxresp < group->timer)))) {
782     igmp_start_timer(group, maxresp);
783 #if (LWIP_IGMP_REPORT_TIMES > 2)
784     group->report_cnt = 1;
785 #endif
786     group->group_state = IGMP_GROUP_DELAYING_MEMBER;
787   }
788 }
789 
790 
791 /**
792  * Sends an IP packet on a network interface. This function constructs the IP header
793  * and calculates the IP header checksum. If the source IP address is NULL,
794  * the IP address of the outgoing network interface is filled in as source address.
795  *
796  * @param p the packet to send (p->payload points to the data, e.g. next
797             protocol header; if dest == LWIP_IP_HDRINCL, p already includes an
798             IP header and p->payload points to that IP header)
799  * @param src the source IP address to send from (if src == IP4_ADDR_ANY, the
800  *         IP  address of the netif used to send is used as source address)
801  * @param dest the destination IP address to send the packet to
802  * @param netif the netif on which to send this packet
803  * @return ERR_OK if the packet was sent OK
804  *         ERR_BUF if p doesn't have enough space for IP/LINK headers
805  *         returns errors returned by netif->output
806  */
807 static err_t
igmp_ip_output_if(struct pbuf * p,const ip4_addr_t * src,const ip4_addr_t * dest,struct netif * netif)808 igmp_ip_output_if(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest, struct netif *netif)
809 {
810   /* This is the "router alert" option */
811   u16_t ra[2];
812   ra[0] = PP_HTONS(ROUTER_ALERT);
813   ra[1] = 0x0000; /* Router shall examine packet */
814   IGMP_STATS_INC(igmp.xmit);
815   return ip4_output_if_opt(p, src, dest, IGMP_TTL, 0, IP_PROTO_IGMP, netif, ra, ROUTER_ALERTLEN);
816 }
817 
818 /**
819  * Send an igmp packet to a specific group.
820  *
821  * @param group the group to which to send the packet
822  * @param type the type of igmp packet to send
823  */
824 static void
igmp_send(struct netif * netif,struct igmp_group * group,u8_t type)825 igmp_send(struct netif *netif, struct igmp_group *group, u8_t type)
826 {
827   struct pbuf     *p    = NULL;
828   struct igmp_msg *igmp = NULL;
829   ip4_addr_t   src  = *IP4_ADDR_ANY4;
830   ip4_addr_t  *dest = NULL;
831 
832   /* IP header + "router alert" option + IGMP header */
833   p = pbuf_alloc(PBUF_TRANSPORT, IGMP_MINLEN, PBUF_RAM);
834 
835   if (p) {
836     igmp = (struct igmp_msg *)p->payload;
837     LWIP_ASSERT("igmp_send: check that first pbuf can hold struct igmp_msg",
838                 (p->len >= sizeof(struct igmp_msg)));
839     ip4_addr_copy(src, *netif_ip4_addr(netif));
840 
841     if (type == IGMP_V2_MEMB_REPORT) {
842       dest = &(group->group_address);
843       ip4_addr_copy(igmp->igmp_group_address, group->group_address);
844       group->last_reporter_flag = 1; /* Remember we were the last to report */
845     } else {
846       if (type == IGMP_LEAVE_GROUP) {
847         dest = &allrouters;
848         ip4_addr_copy(igmp->igmp_group_address, group->group_address);
849       }
850     }
851 
852     if ((type == IGMP_V2_MEMB_REPORT) || (type == IGMP_LEAVE_GROUP)) {
853       igmp->igmp_msgtype  = type;
854       igmp->igmp_maxresp  = 0;
855       igmp->igmp_checksum = 0;
856       igmp->igmp_checksum = inet_chksum(igmp, IGMP_MINLEN);
857 
858       igmp_ip_output_if(p, &src, dest, netif);
859     }
860 
861     pbuf_free(p);
862   } else {
863     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_send: not enough memory for igmp_send\n"));
864     IGMP_STATS_INC(igmp.memerr);
865   }
866 }
867 
868 #endif /* LWIP_IPV4 && LWIP_IGMP */
869