Deprecated fork of old network stack source from github. Please use official library instead: https://mbed.org/users/mbed_official/code/EthernetInterface/

Committer:
AdamGreen
Date:
Sat Sep 07 21:38:42 2013 +0000
Revision:
0:3b00827bb0b7
Sync to latest network stack source from github.

Who changed what in which revision?

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