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