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