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