176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @file
376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * IGMP - Internet Group Management Protocol
476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/*
876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Copyright (c) 2002 CITEL Technologies Ltd.
976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * All rights reserved.
1076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
1176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Redistribution and use in source and binary forms, with or without
1276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * modification, are permitted provided that the following conditions
1376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * are met:
1476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 1. Redistributions of source code must retain the above copyright
1576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *    notice, this list of conditions and the following disclaimer.
1676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 2. Redistributions in binary form must reproduce the above copyright
1776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *    notice, this list of conditions and the following disclaimer in the
1876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *    documentation and/or other materials provided with the distribution.
1976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 3. Neither the name of CITEL Technologies Ltd nor the names of its contributors
2076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *    may be used to endorse or promote products derived from this software
2176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *    without specific prior written permission.
2276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
2376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * THIS SOFTWARE IS PROVIDED BY CITEL TECHNOLOGIES AND CONTRIBUTORS ``AS IS''
2476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * ARE DISCLAIMED.  IN NO EVENT SHALL CITEL TECHNOLOGIES OR CONTRIBUTORS BE LIABLE
2776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * SUCH DAMAGE.
3476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
3576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * This file is a contribution to the lwIP TCP/IP stack.
3676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * The Swedish Institute of Computer Science and Adam Dunkels
3776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * are specifically granted permission to redistribute this
3876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * source code.
3976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman*/
4076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
4176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/*-------------------------------------------------------------
4276d05dc695b06c4e987bb8078f78032441e1430cGreg HartmanNote 1)
4376d05dc695b06c4e987bb8078f78032441e1430cGreg HartmanAlthough the rfc requires V1 AND V2 capability
4476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanwe will only support v2 since now V1 is very old (August 1989)
4576d05dc695b06c4e987bb8078f78032441e1430cGreg HartmanV1 can be added if required
4676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
4776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmana debug print and statistic have been implemented to
4876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanshow this up.
4976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman-------------------------------------------------------------
5076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman-------------------------------------------------------------
5176d05dc695b06c4e987bb8078f78032441e1430cGreg HartmanNote 2)
5276d05dc695b06c4e987bb8078f78032441e1430cGreg HartmanA query for a specific group address (as opposed to ALLHOSTS)
5376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanhas now been implemented as I am unsure if it is required
5476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
5576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmana debug print and statistic have been implemented to
5676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanshow this up.
5776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman-------------------------------------------------------------
5876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman-------------------------------------------------------------
5976d05dc695b06c4e987bb8078f78032441e1430cGreg HartmanNote 3)
6076d05dc695b06c4e987bb8078f78032441e1430cGreg HartmanThe router alert rfc 2113 is implemented in outgoing packets
6176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanbut not checked rigorously incoming
6276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman-------------------------------------------------------------
6376d05dc695b06c4e987bb8078f78032441e1430cGreg HartmanSteve Reynolds
6476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman------------------------------------------------------------*/
6576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
6676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/*-----------------------------------------------------------------------------
6776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * RFC 988  - Host extensions for IP multicasting                         - V0
6876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * RFC 1054 - Host extensions for IP multicasting                         -
6976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * RFC 1112 - Host extensions for IP multicasting                         - V1
7076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * RFC 2236 - Internet Group Management Protocol, Version 2               - V2  <- this code is based on this RFC (it's the "de facto" standard)
7176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * RFC 3376 - Internet Group Management Protocol, Version 3               - V3
7276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * RFC 4604 - Using Internet Group Management Protocol Version 3...       - V3+
7376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * RFC 2113 - IP Router Alert Option                                      -
7476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *----------------------------------------------------------------------------*/
7576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
7676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/*-----------------------------------------------------------------------------
7776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Includes
7876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *----------------------------------------------------------------------------*/
7976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
8076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include "lwip/opt.h"
8176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
8276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#if LWIP_IGMP /* don't build if not configured for use in lwipopts.h */
8376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
8476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include "lwip/igmp.h"
8576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include "lwip/debug.h"
8676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include "lwip/def.h"
8776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include "lwip/mem.h"
8876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include "lwip/ip.h"
8976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include "lwip/inet_chksum.h"
9076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include "lwip/netif.h"
9176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include "lwip/icmp.h"
9276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include "lwip/udp.h"
9376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include "lwip/tcp.h"
9476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include "lwip/stats.h"
9576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
9676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include "string.h"
9776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
9876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/*
9976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * IGMP constants
10076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
10176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#define IGMP_TTL                       1
10276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#define IGMP_MINLEN                    8
10376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#define ROUTER_ALERT                   0x9404U
10476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#define ROUTER_ALERTLEN                4
10576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
10676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/*
10776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * IGMP message types, including version number.
10876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
10976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#define IGMP_MEMB_QUERY                0x11 /* Membership query         */
11076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#define IGMP_V1_MEMB_REPORT            0x12 /* Ver. 1 membership report */
11176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#define IGMP_V2_MEMB_REPORT            0x16 /* Ver. 2 membership report */
11276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#define IGMP_LEAVE_GROUP               0x17 /* Leave-group message      */
11376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
11476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/* Group  membership states */
11576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#define IGMP_GROUP_NON_MEMBER          0
11676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#define IGMP_GROUP_DELAYING_MEMBER     1
11776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#define IGMP_GROUP_IDLE_MEMBER         2
11876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
11976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
12076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * IGMP packet format.
12176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
12276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#ifdef PACK_STRUCT_USE_INCLUDES
12376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#  include "arch/bpstruct.h"
12476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#endif
12576d05dc695b06c4e987bb8078f78032441e1430cGreg HartmanPACK_STRUCT_BEGIN
12676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstruct igmp_msg {
12776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman PACK_STRUCT_FIELD(u8_t           igmp_msgtype);
12876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman PACK_STRUCT_FIELD(u8_t           igmp_maxresp);
12976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman PACK_STRUCT_FIELD(u16_t          igmp_checksum);
13076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman PACK_STRUCT_FIELD(ip_addr_p_t    igmp_group_address);
13176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman} PACK_STRUCT_STRUCT;
13276d05dc695b06c4e987bb8078f78032441e1430cGreg HartmanPACK_STRUCT_END
13376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#ifdef PACK_STRUCT_USE_INCLUDES
13476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#  include "arch/epstruct.h"
13576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#endif
13676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
13776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
13876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic struct igmp_group *igmp_lookup_group(struct netif *ifp, ip_addr_t *addr);
13976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic err_t  igmp_remove_group(struct igmp_group *group);
14076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic void   igmp_timeout( struct igmp_group *group);
14176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic void   igmp_start_timer(struct igmp_group *group, u8_t max_time);
14276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic void   igmp_stop_timer(struct igmp_group *group);
14376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic void   igmp_delaying_member(struct igmp_group *group, u8_t maxresp);
14476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic err_t  igmp_ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, struct netif *netif);
14576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic void   igmp_send(struct igmp_group *group, u8_t type);
14676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
14776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
14876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic struct igmp_group* igmp_group_list;
14976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic ip_addr_t     allsystems;
15076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic ip_addr_t     allrouters;
15176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
15276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
15376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
15476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Initialize the IGMP module
15576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
15676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanvoid
15776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanigmp_init(void)
15876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
15976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  LWIP_DEBUGF(IGMP_DEBUG, ("igmp_init: initializing\n"));
16076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
16176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  IP4_ADDR(&allsystems, 224, 0, 0, 1);
16276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  IP4_ADDR(&allrouters, 224, 0, 0, 2);
16376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
16476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
16576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#ifdef LWIP_DEBUG
16676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
16776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Dump global IGMP groups list
16876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
16976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanvoid
17076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanigmp_dump_group_list()
17176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
17276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  struct igmp_group *group = igmp_group_list;
17376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
17476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  while (group != NULL) {
17576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    LWIP_DEBUGF(IGMP_DEBUG, ("igmp_dump_group_list: [%"U32_F"] ", (u32_t)(group->group_state)));
17676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    ip_addr_debug_print(IGMP_DEBUG, &group->group_address);
17776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", group->netif));
17876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    group = group->next;
17976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  }
18076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
18176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
18276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#else
18376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#define igmp_dump_group_list()
18476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#endif /* LWIP_DEBUG */
18576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
18676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
18776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Start IGMP processing on interface
18876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
18976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @param netif network interface on which start IGMP processing
19076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
19176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanerr_t
19276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanigmp_start(struct netif *netif)
19376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
19476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  struct igmp_group* group;
19576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
19676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: starting IGMP processing on if %p\n", netif));
19776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
19876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  group = igmp_lookup_group(netif, &allsystems);
19976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
20076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  if (group != NULL) {
20176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    group->group_state = IGMP_GROUP_IDLE_MEMBER;
20276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    group->use++;
20376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
20476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    /* Allow the igmp messages at the MAC level */
20576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    if (netif->igmp_mac_filter != NULL) {
20676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman      LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: igmp_mac_filter(ADD "));
20776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman      ip_addr_debug_print(IGMP_DEBUG, &allsystems);
20876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman      LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif));
20976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman      netif->igmp_mac_filter(netif, &allsystems, IGMP_ADD_MAC_FILTER);
21076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    }
21176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
21276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    return ERR_OK;
21376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  }
21476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
21576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  return ERR_MEM;
21676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
21776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
21876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
21976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Stop IGMP processing on interface
22076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
22176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @param netif network interface on which stop IGMP processing
22276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
22376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanerr_t
22476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanigmp_stop(struct netif *netif)
22576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
22676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  struct igmp_group *group = igmp_group_list;
22776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  struct igmp_group *prev  = NULL;
22876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  struct igmp_group *next;
22976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
23076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  /* look for groups joined on this interface further down the list */
23176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  while (group != NULL) {
23276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    next = group->next;
23376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    /* is it a group joined on this interface? */
23476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    if (group->netif == netif) {
23576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman      /* is it the first group of the list? */
23676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman      if (group == igmp_group_list) {
23776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman        igmp_group_list = next;
23876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman      }
23976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman      /* is there a "previous" group defined? */
24076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman      if (prev != NULL) {
24176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman        prev->next = next;
24276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman      }
24376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman      /* disable the group at the MAC level */
24476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman      if (netif->igmp_mac_filter != NULL) {
24576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman        LWIP_DEBUGF(IGMP_DEBUG, ("igmp_stop: igmp_mac_filter(DEL "));
24676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman        ip_addr_debug_print(IGMP_DEBUG, &group->group_address);
24776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman        LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif));
24876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman        netif->igmp_mac_filter(netif, &(group->group_address), IGMP_DEL_MAC_FILTER);
24976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman      }
25076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman      /* free group */
25176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman      memp_free(MEMP_IGMP_GROUP, group);
25276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    } else {
25376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman      /* change the "previous" */
25476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman      prev = group;
25576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    }
25676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    /* move to "next" */
25776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    group = next;
25876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  }
25976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  return ERR_OK;
26076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
26176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
26276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
26376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Report IGMP memberships for this interface
26476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
26576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @param netif network interface on which report IGMP memberships
26676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
26776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanvoid
26876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanigmp_report_groups(struct netif *netif)
26976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
27076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  struct igmp_group *group = igmp_group_list;
27176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
27276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  LWIP_DEBUGF(IGMP_DEBUG, ("igmp_report_groups: sending IGMP reports on if %p\n", netif));
27376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
27476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  while (group != NULL) {
27576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    if (group->netif == netif) {
27676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman      igmp_delaying_member(group, IGMP_JOIN_DELAYING_MEMBER_TMR);
27776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    }
27876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    group = group->next;
27976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  }
28076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
28176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
28276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
28376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Search for a group in the global igmp_group_list
28476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
28576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @param ifp the network interface for which to look
28676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @param addr the group ip address to search for
28776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @return a struct igmp_group* if the group has been found,
28876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *         NULL if the group wasn't found.
28976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
29076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstruct igmp_group *
29176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanigmp_lookfor_group(struct netif *ifp, ip_addr_t *addr)
29276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
29376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  struct igmp_group *group = igmp_group_list;
29476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
29576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  while (group != NULL) {
29676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    if ((group->netif == ifp) && (ip_addr_cmp(&(group->group_address), addr))) {
29776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman      return group;
29876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    }
29976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    group = group->next;
30076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  }
30176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
30276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  /* to be clearer, we return NULL here instead of
30376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman   * 'group' (which is also NULL at this point).
30476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman   */
30576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  return NULL;
30676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
30776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
30876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
30976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Search for a specific igmp group and create a new one if not found-
31076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
31176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @param ifp the network interface for which to look
31276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @param addr the group ip address to search
31376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @return a struct igmp_group*,
31476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *         NULL on memory error.
31576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
31676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstruct igmp_group *
31776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanigmp_lookup_group(struct netif *ifp, ip_addr_t *addr)
31876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
31976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  struct igmp_group *group = igmp_group_list;
32076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
32176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  /* Search if the group already exists */
32276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  group = igmp_lookfor_group(ifp, addr);
32376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  if (group != NULL) {
32476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    /* Group already exists. */
32576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    return group;
32676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  }
32776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
32876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  /* Group doesn't exist yet, create a new one */
32976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  group = (struct igmp_group *)memp_malloc(MEMP_IGMP_GROUP);
33076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  if (group != NULL) {
33176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    group->netif              = ifp;
33276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    ip_addr_set(&(group->group_address), addr);
33376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    group->timer              = 0; /* Not running */
33476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    group->group_state        = IGMP_GROUP_NON_MEMBER;
33576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    group->last_reporter_flag = 0;
33676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    group->use                = 0;
33776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    group->next               = igmp_group_list;
33876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
33976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    igmp_group_list = group;
34076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  }
34176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
34276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  LWIP_DEBUGF(IGMP_DEBUG, ("igmp_lookup_group: %sallocated a new group with address ", (group?"":"impossible to ")));
34376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  ip_addr_debug_print(IGMP_DEBUG, addr);
34476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", ifp));
34576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
34676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  return group;
34776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
34876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
34976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
35076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Remove a group in the global igmp_group_list
35176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
35276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @param group the group to remove from the global igmp_group_list
35376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @return ERR_OK if group was removed from the list, an err_t otherwise
35476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
35576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic err_t
35676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanigmp_remove_group(struct igmp_group *group)
35776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
35876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  err_t err = ERR_OK;
35976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
36076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  /* Is it the first group? */
36176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  if (igmp_group_list == group) {
36276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    igmp_group_list = group->next;
36376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  } else {
36476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    /* look for group further down the list */
36576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    struct igmp_group *tmpGroup;
36676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    for (tmpGroup = igmp_group_list; tmpGroup != NULL; tmpGroup = tmpGroup->next) {
36776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman      if (tmpGroup->next == group) {
36876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman        tmpGroup->next = group->next;
36976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman        break;
37076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman      }
37176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    }
37276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    /* Group not found in the global igmp_group_list */
37376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    if (tmpGroup == NULL)
37476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman      err = ERR_ARG;
37576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  }
37676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  /* free group */
37776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  memp_free(MEMP_IGMP_GROUP, group);
37876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
37976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  return err;
38076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
38176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
38276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
38376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Called from ip_input() if a new IGMP packet is received.
38476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
38576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @param p received igmp packet, p->payload pointing to the ip header
38676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @param inp network interface on which the packet was received
38776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @param dest destination ip address of the igmp packet
38876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
38976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanvoid
39076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanigmp_input(struct pbuf *p, struct netif *inp, ip_addr_t *dest)
39176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
39276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  struct ip_hdr *    iphdr;
39376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  struct igmp_msg*   igmp;
39476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  struct igmp_group* group;
39576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  struct igmp_group* groupref;
39676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
39776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  IGMP_STATS_INC(igmp.recv);
39876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
39976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  /* Note that the length CAN be greater than 8 but only 8 are used - All are included in the checksum */
40076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  iphdr = (struct ip_hdr *)p->payload;
40176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  if (pbuf_header(p, -(s16_t)(IPH_HL(iphdr) * 4)) || (p->len < IGMP_MINLEN)) {
40276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    pbuf_free(p);
40376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    IGMP_STATS_INC(igmp.lenerr);
40476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: length error\n"));
40576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    return;
40676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  }
40776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
40876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: message from "));
40976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  ip_addr_debug_print(IGMP_DEBUG, &(iphdr->src));
41076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  LWIP_DEBUGF(IGMP_DEBUG, (" to address "));
41176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  ip_addr_debug_print(IGMP_DEBUG, &(iphdr->dest));
41276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", inp));
41376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
41476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  /* Now calculate and check the checksum */
41576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  igmp = (struct igmp_msg *)p->payload;
41676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  if (inet_chksum(igmp, p->len)) {
41776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    pbuf_free(p);
41876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    IGMP_STATS_INC(igmp.chkerr);
41976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: checksum error\n"));
42076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    return;
42176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  }
42276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
42376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  /* Packet is ok so find an existing group */
42476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  group = igmp_lookfor_group(inp, dest); /* use the destination IP address of incoming packet */
42576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
42676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  /* If group can be found or create... */
42776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  if (!group) {
42876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    pbuf_free(p);
42976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    IGMP_STATS_INC(igmp.drop);
43076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP frame not for us\n"));
43176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    return;
43276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  }
43376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
43476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  /* NOW ACT ON THE INCOMING MESSAGE TYPE... */
43576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  switch (igmp->igmp_msgtype) {
43676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman   case IGMP_MEMB_QUERY: {
43776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman     /* IGMP_MEMB_QUERY to the "all systems" address ? */
43876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman     if ((ip_addr_cmp(dest, &allsystems)) && ip_addr_isany(&igmp->igmp_group_address)) {
43976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman       /* THIS IS THE GENERAL QUERY */
44076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman       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)));
44176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
44276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman       if (igmp->igmp_maxresp == 0) {
44376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman         IGMP_STATS_INC(igmp.rx_v1);
44476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman         LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: got an all hosts query with time== 0 - this is V1 and not implemented - treat as v2\n"));
44576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman         igmp->igmp_maxresp = IGMP_V1_DELAYING_MEMBER_TMR;
44676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman       } else {
44776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman         IGMP_STATS_INC(igmp.rx_general);
44876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman       }
44976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
45076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman       groupref = igmp_group_list;
45176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman       while (groupref) {
45276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman         /* Do not send messages on the all systems group address! */
45376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman         if ((groupref->netif == inp) && (!(ip_addr_cmp(&(groupref->group_address), &allsystems)))) {
45476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman           igmp_delaying_member(groupref, igmp->igmp_maxresp);
45576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman         }
45676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman         groupref = groupref->next;
45776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman       }
45876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman     } else {
45976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman       /* IGMP_MEMB_QUERY to a specific group ? */
46076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman       if (!ip_addr_isany(&igmp->igmp_group_address)) {
46176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman         LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_MEMB_QUERY to a specific group "));
46276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman         ip_addr_debug_print(IGMP_DEBUG, &igmp->igmp_group_address);
46376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman         if (ip_addr_cmp(dest, &allsystems)) {
46476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman           ip_addr_t groupaddr;
46576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman           LWIP_DEBUGF(IGMP_DEBUG, (" using \"ALL SYSTEMS\" address (224.0.0.1) [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp)));
46676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman           /* we first need to re-look for the group since we used dest last time */
46776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman           ip_addr_copy(groupaddr, igmp->igmp_group_address);
46876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman           group = igmp_lookfor_group(inp, &groupaddr);
46976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman         } else {
47076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman           LWIP_DEBUGF(IGMP_DEBUG, (" with the group address as destination [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp)));
47176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman         }
47276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
47376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman         if (group != NULL) {
47476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman           IGMP_STATS_INC(igmp.rx_group);
47576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman           igmp_delaying_member(group, igmp->igmp_maxresp);
47676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman         } else {
47776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman           IGMP_STATS_INC(igmp.drop);
47876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman         }
47976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman       } else {
48076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman         IGMP_STATS_INC(igmp.proterr);
48176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman       }
48276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman     }
48376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman     break;
48476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman   }
48576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman   case IGMP_V2_MEMB_REPORT: {
48676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_V2_MEMB_REPORT\n"));
48776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman     IGMP_STATS_INC(igmp.rx_report);
48876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman     if (group->group_state == IGMP_GROUP_DELAYING_MEMBER) {
48976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman       /* This is on a specific group we have already looked up */
49076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman       group->timer = 0; /* stopped */
49176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman       group->group_state = IGMP_GROUP_IDLE_MEMBER;
49276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman       group->last_reporter_flag = 0;
49376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman     }
49476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman     break;
49576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman   }
49676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman   default: {
49776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: unexpected msg %d in state %d on group %p on if %p\n",
49876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman       igmp->igmp_msgtype, group->group_state, &group, group->netif));
49976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman     IGMP_STATS_INC(igmp.proterr);
50076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman     break;
50176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman   }
50276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  }
50376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
50476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  pbuf_free(p);
50576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  return;
50676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
50776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
50876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
50976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Join a group on one network interface.
51076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
51176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @param ifaddr ip address of the network interface which should join a new group
51276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @param groupaddr the ip address of the group which to join
51376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @return ERR_OK if group was joined on the netif(s), an err_t otherwise
51476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
51576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanerr_t
51676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanigmp_joingroup(ip_addr_t *ifaddr, ip_addr_t *groupaddr)
51776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
51876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  err_t              err = ERR_VAL; /* no matching interface */
51976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  struct igmp_group *group;
52076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  struct netif      *netif;
52176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
52276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  /* make sure it is multicast address */
52376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  LWIP_ERROR("igmp_joingroup: attempt to join non-multicast address", ip_addr_ismulticast(groupaddr), return ERR_VAL;);
52476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  LWIP_ERROR("igmp_joingroup: attempt to join allsystems address", (!ip_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;);
52576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
52676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  /* loop through netif's */
52776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  netif = netif_list;
52876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  while (netif != NULL) {
52976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    /* Should we join this interface ? */
53076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    if ((netif->flags & NETIF_FLAG_IGMP) && ((ip_addr_isany(ifaddr) || ip_addr_cmp(&(netif->ip_addr), ifaddr)))) {
53176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman      /* find group or create a new one if not found */
53276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman      group = igmp_lookup_group(netif, groupaddr);
53376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
53476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman      if (group != NULL) {
53576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman        /* This should create a new group, check the state to make sure */
53676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman        if (group->group_state != IGMP_GROUP_NON_MEMBER) {
53776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman          LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: join to group not in state IGMP_GROUP_NON_MEMBER\n"));
53876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman        } else {
53976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman          /* OK - it was new group */
54076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman          LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: join to new group: "));
54176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman          ip_addr_debug_print(IGMP_DEBUG, groupaddr);
54276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman          LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
54376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
54476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman          /* If first use of the group, allow the group at the MAC level */
54576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman          if ((group->use==0) && (netif->igmp_mac_filter != NULL)) {
54676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman            LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: igmp_mac_filter(ADD "));
54776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman            ip_addr_debug_print(IGMP_DEBUG, groupaddr);
54876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman            LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif));
54976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman            netif->igmp_mac_filter(netif, groupaddr, IGMP_ADD_MAC_FILTER);
55076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman          }
55176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
55276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman          IGMP_STATS_INC(igmp.tx_join);
55376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman          igmp_send(group, IGMP_V2_MEMB_REPORT);
55476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
55576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman          igmp_start_timer(group, IGMP_JOIN_DELAYING_MEMBER_TMR);
55676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
55776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman          /* Need to work out where this timer comes from */
55876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman          group->group_state = IGMP_GROUP_DELAYING_MEMBER;
55976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman        }
56076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman        /* Increment group use */
56176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman        group->use++;
56276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman        /* Join on this interface */
56376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman        err = ERR_OK;
56476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman      } else {
56576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman        /* Return an error even if some network interfaces are joined */
56676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman        /** @todo undo any other netif already joined */
56776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman        LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: Not enought memory to join to group\n"));
56876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman        return ERR_MEM;
56976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman      }
57076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    }
57176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    /* proceed to next network interface */
57276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    netif = netif->next;
57376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  }
57476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
57576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  return err;
57676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
57776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
57876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
57976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Leave a group on one network interface.
58076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
58176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @param ifaddr ip address of the network interface which should leave a group
58276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @param groupaddr the ip address of the group which to leave
58376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @return ERR_OK if group was left on the netif(s), an err_t otherwise
58476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
58576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanerr_t
58676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanigmp_leavegroup(ip_addr_t *ifaddr, ip_addr_t *groupaddr)
58776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
58876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  err_t              err = ERR_VAL; /* no matching interface */
58976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  struct igmp_group *group;
59076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  struct netif      *netif;
59176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
59276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  /* make sure it is multicast address */
59376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  LWIP_ERROR("igmp_leavegroup: attempt to leave non-multicast address", ip_addr_ismulticast(groupaddr), return ERR_VAL;);
59476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  LWIP_ERROR("igmp_leavegroup: attempt to leave allsystems address", (!ip_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;);
59576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
59676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  /* loop through netif's */
59776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  netif = netif_list;
59876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  while (netif != NULL) {
59976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    /* Should we leave this interface ? */
60076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    if ((netif->flags & NETIF_FLAG_IGMP) && ((ip_addr_isany(ifaddr) || ip_addr_cmp(&(netif->ip_addr), ifaddr)))) {
60176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman      /* find group */
60276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman      group = igmp_lookfor_group(netif, groupaddr);
60376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
60476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman      if (group != NULL) {
60576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman        /* Only send a leave if the flag is set according to the state diagram */
60676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman        LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: Leaving group: "));
60776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman        ip_addr_debug_print(IGMP_DEBUG, groupaddr);
60876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman        LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
60976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
61076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman        /* If there is no other use of the group */
61176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman        if (group->use <= 1) {
61276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman          /* If we are the last reporter for this group */
61376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman          if (group->last_reporter_flag) {
61476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman            LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: sending leaving group\n"));
61576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman            IGMP_STATS_INC(igmp.tx_leave);
61676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman            igmp_send(group, IGMP_LEAVE_GROUP);
61776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman          }
61876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
61976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman          /* Disable the group at the MAC level */
62076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman          if (netif->igmp_mac_filter != NULL) {
62176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman            LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: igmp_mac_filter(DEL "));
62276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman            ip_addr_debug_print(IGMP_DEBUG, groupaddr);
62376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman            LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif));
62476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman            netif->igmp_mac_filter(netif, groupaddr, IGMP_DEL_MAC_FILTER);
62576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman          }
62676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
62776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman          LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: remove group: "));
62876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman          ip_addr_debug_print(IGMP_DEBUG, groupaddr);
62976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman          LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
63076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
63176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman          /* Free the group */
63276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman          igmp_remove_group(group);
63376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman        } else {
63476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman          /* Decrement group use */
63576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman          group->use--;
63676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman        }
63776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman        /* Leave on this interface */
63876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman        err = ERR_OK;
63976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman      } else {
64076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman        /* It's not a fatal error on "leavegroup" */
64176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman        LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: not member of group\n"));
64276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman      }
64376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    }
64476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    /* proceed to next network interface */
64576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    netif = netif->next;
64676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  }
64776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
64876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  return err;
64976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
65076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
65176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
65276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * The igmp timer function (both for NO_SYS=1 and =0)
65376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Should be called every IGMP_TMR_INTERVAL milliseconds (100 ms is default).
65476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
65576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanvoid
65676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanigmp_tmr(void)
65776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
65876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  struct igmp_group *group = igmp_group_list;
65976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
66076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  while (group != NULL) {
66176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    if (group->timer > 0) {
66276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman      group->timer--;
66376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman      if (group->timer == 0) {
66476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman        igmp_timeout(group);
66576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman      }
66676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    }
66776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    group = group->next;
66876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  }
66976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
67076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
67176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
67276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Called if a timeout for one group is reached.
67376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Sends a report for this group.
67476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
67576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @param group an igmp_group for which a timeout is reached
67676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
67776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic void
67876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanigmp_timeout(struct igmp_group *group)
67976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
68076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  /* If the state is IGMP_GROUP_DELAYING_MEMBER then we send a report for this group */
68176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  if (group->group_state == IGMP_GROUP_DELAYING_MEMBER) {
68276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    LWIP_DEBUGF(IGMP_DEBUG, ("igmp_timeout: report membership for group with address "));
68376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    ip_addr_debug_print(IGMP_DEBUG, &(group->group_address));
68476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", group->netif));
68576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
68676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    IGMP_STATS_INC(igmp.tx_report);
68776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    igmp_send(group, IGMP_V2_MEMB_REPORT);
68876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  }
68976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
69076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
69176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
69276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Start a timer for an igmp group
69376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
69476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @param group the igmp_group for which to start a timer
69576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @param max_time the time in multiples of IGMP_TMR_INTERVAL (decrease with
69676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *        every call to igmp_tmr())
69776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
69876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic void
69976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanigmp_start_timer(struct igmp_group *group, u8_t max_time)
70076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
70176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  /* ensure the input value is > 0 */
70276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  if (max_time == 0) {
70376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    max_time = 1;
70476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  }
70576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  /* ensure the random value is > 0 */
70676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  group->timer = (LWIP_RAND() % (max_time - 1)) + 1;
70776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
70876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
70976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
71076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Stop a timer for an igmp_group
71176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
71276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @param group the igmp_group for which to stop the timer
71376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
71476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic void
71576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanigmp_stop_timer(struct igmp_group *group)
71676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
71776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  group->timer = 0;
71876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
71976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
72076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
72176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Delaying membership report for a group if necessary
72276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
72376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @param group the igmp_group for which "delaying" membership report
72476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @param maxresp query delay
72576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
72676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic void
72776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanigmp_delaying_member(struct igmp_group *group, u8_t maxresp)
72876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
72976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  if ((group->group_state == IGMP_GROUP_IDLE_MEMBER) ||
73076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman     ((group->group_state == IGMP_GROUP_DELAYING_MEMBER) &&
73176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman      ((group->timer == 0) || (maxresp < group->timer)))) {
73276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    igmp_start_timer(group, maxresp);
73376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    group->group_state = IGMP_GROUP_DELAYING_MEMBER;
73476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  }
73576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
73676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
73776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
73876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
73976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Sends an IP packet on a network interface. This function constructs the IP header
74076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * and calculates the IP header checksum. If the source IP address is NULL,
74176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * the IP address of the outgoing network interface is filled in as source address.
74276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
74376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @param p the packet to send (p->payload points to the data, e.g. next
74476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman            protocol header; if dest == IP_HDRINCL, p already includes an IP
74576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman            header and p->payload points to that IP header)
74676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @param src the source IP address to send from (if src == IP_ADDR_ANY, the
74776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *         IP  address of the netif used to send is used as source address)
74876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @param dest the destination IP address to send the packet to
74976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @param ttl the TTL value to be set in the IP header
75076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @param proto the PROTOCOL to be set in the IP header
75176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @param netif the netif on which to send this packet
75276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @return ERR_OK if the packet was sent OK
75376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *         ERR_BUF if p doesn't have enough space for IP/LINK headers
75476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *         returns errors returned by netif->output
75576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
75676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic err_t
75776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanigmp_ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, struct netif *netif)
75876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
75976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  /* This is the "router alert" option */
76076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  u16_t ra[2];
76176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  ra[0] = PP_HTONS(ROUTER_ALERT);
76276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  ra[1] = 0x0000; /* Router shall examine packet */
76376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  IGMP_STATS_INC(igmp.xmit);
76476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  return ip_output_if_opt(p, src, dest, IGMP_TTL, 0, IP_PROTO_IGMP, netif, ra, ROUTER_ALERTLEN);
76576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
76676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
76776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
76876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Send an igmp packet to a specific group.
76976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
77076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @param group the group to which to send the packet
77176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @param type the type of igmp packet to send
77276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
77376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic void
77476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanigmp_send(struct igmp_group *group, u8_t type)
77576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
77676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  struct pbuf*     p    = NULL;
77776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  struct igmp_msg* igmp = NULL;
77876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  ip_addr_t   src  = *IP_ADDR_ANY;
77976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  ip_addr_t*  dest = NULL;
78076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
78176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  /* IP header + "router alert" option + IGMP header */
78276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  p = pbuf_alloc(PBUF_TRANSPORT, IGMP_MINLEN, PBUF_RAM);
78376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
78476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  if (p) {
78576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    igmp = (struct igmp_msg *)p->payload;
78676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    LWIP_ASSERT("igmp_send: check that first pbuf can hold struct igmp_msg",
78776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman               (p->len >= sizeof(struct igmp_msg)));
78876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    ip_addr_copy(src, group->netif->ip_addr);
78976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
79076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    if (type == IGMP_V2_MEMB_REPORT) {
79176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman      dest = &(group->group_address);
79276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman      ip_addr_copy(igmp->igmp_group_address, group->group_address);
79376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman      group->last_reporter_flag = 1; /* Remember we were the last to report */
79476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    } else {
79576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman      if (type == IGMP_LEAVE_GROUP) {
79676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman        dest = &allrouters;
79776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman        ip_addr_copy(igmp->igmp_group_address, group->group_address);
79876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman      }
79976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    }
80076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
80176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    if ((type == IGMP_V2_MEMB_REPORT) || (type == IGMP_LEAVE_GROUP)) {
80276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman      igmp->igmp_msgtype  = type;
80376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman      igmp->igmp_maxresp  = 0;
80476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman      igmp->igmp_checksum = 0;
80576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman      igmp->igmp_checksum = inet_chksum(igmp, IGMP_MINLEN);
80676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
80776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman      igmp_ip_output_if(p, &src, dest, group->netif);
80876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    }
80976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
81076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    pbuf_free(p);
81176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  } else {
81276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    LWIP_DEBUGF(IGMP_DEBUG, ("igmp_send: not enough memory for igmp_send\n"));
81376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman    IGMP_STATS_INC(igmp.memerr);
81476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman  }
81576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
81676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
81776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#endif /* LWIP_IGMP */
818