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