uc-sdk
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
igmp.c
Go to the documentation of this file.
1 
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 0x9404
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 
122 #ifdef PACK_STRUCT_USE_INCLUDES
123 # include "arch/bpstruct.h"
124 #endif
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);
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 
156 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 
169 void
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 
191 err_t
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 
223 err_t
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 
267 void
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 
290 struct igmp_group *
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 
316 struct igmp_group *
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 ")));
344  LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", ifp));
345 
346  return group;
347 }
348 
355 static err_t
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 
389 void
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 
515 err_t
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 */
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 
585 err_t
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 
655 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 
677 static void
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 
698 static void
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 
714 static void
715 igmp_stop_timer(struct igmp_group *group)
716 {
717  group->timer = 0;
718 }
719 
726 static void
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 
756 static err_t
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 
773 static void
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 */