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