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/*-------------------------------------------------------------
64Note 1)
65Although the rfc requires V1 AND V2 capability
66we will only support v2 since now V1 is very old (August 1989)
67V1 can be added if required
68
69a debug print and statistic have been implemented to
70show this up.
71-------------------------------------------------------------
72-------------------------------------------------------------
73Note 2)
74A query for a specific group address (as opposed to ALLHOSTS)
75has now been implemented as I am unsure if it is required
76
77a debug print and statistic have been implemented to
78show this up.
79-------------------------------------------------------------
80-------------------------------------------------------------
81Note 3)
82The router alert rfc 2113 is implemented in outgoing packets
83but not checked rigorously incoming
84-------------------------------------------------------------
85Steve 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
118static struct igmp_group *igmp_lookup_group(struct netif *ifp, const ip4_addr_t *addr);
119static err_t igmp_remove_group(struct netif* netif, struct igmp_group *group);
120static void igmp_timeout(struct netif *netif, struct igmp_group *group);
121static void igmp_start_timer(struct igmp_group *group, u8_t max_time);
122static void igmp_delaying_member(struct igmp_group *group, u8_t maxresp);
123static err_t igmp_ip_output_if(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest, struct netif *netif);
124static void igmp_send(struct netif *netif, struct igmp_group *group, u8_t type);
125
126static ip4_addr_t allsystems;
127static ip4_addr_t allrouters;
128
132void
133igmp_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
146err_t
147igmp_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
178err_t
179igmp_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
210void
211igmp_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
236struct igmp_group *
237igmp_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
262static struct igmp_group *
263igmp_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
313static err_t
314igmp_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
341void
342igmp_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
467err_t
468igmp_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 */
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
504err_t
505igmp_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
563err_t
564igmp_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 */
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
599err_t
600igmp_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
657void
658igmp_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
684static void
685igmp_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
709static void
710igmp_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
730static void
731igmp_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
758static err_t
759igmp_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
775static void
776igmp_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 */
#define LWIP_DEBUGF(debug, message)
Definition debug.h:183
s8_t err_t
Definition err.h:76
@ ERR_OK
Definition err.h:82
@ ERR_VAL
Definition err.h:94
@ ERR_MEM
Definition err.h:84
@ ERR_ARG
Definition err.h:115
#define IGMP_DEBUG
Definition opt.h:2699
#define NETIF_FLAG_IGMP
Definition netif.h:117
struct pbuf * pbuf_alloc(pbuf_layer layer, u16_t length, pbuf_type type)
Definition pbuf.c:267
u8_t pbuf_free(struct pbuf *p)
Definition pbuf.c:734
@ PBUF_RAM
Definition pbuf.h:127
@ PBUF_TRANSPORT
Definition pbuf.h:95
void * memp_malloc(memp_t type)
Definition memp.c:404
void memp_free(memp_t type, void *mem)
Definition memp.c:488
struct netif * netif_list
Definition netif.c:123
@ NETIF_ADD_MAC_FILTER
Definition netif.h:166
@ NETIF_DEL_MAC_FILTER
Definition netif.h:164
Definition netif.h:244
u8_t flags
Definition netif.h:324
struct netif * next
Definition netif.h:246
Definition pbuf.h:161
u16_t len
Definition pbuf.h:178
void * payload
Definition pbuf.h:166