Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependents: TYBLE16_simple_data_logger TYBLE16_MP3_Air
lwip_igmp.c
00001 /** 00002 * @file 00003 * IGMP - Internet Group Management Protocol 00004 * 00005 * @defgroup igmp IGMP 00006 * @ingroup ip4 00007 * To be called from TCPIP thread 00008 */ 00009 00010 /* 00011 * Copyright (c) 2002 CITEL Technologies Ltd. 00012 * All rights reserved. 00013 * 00014 * Redistribution and use in source and binary forms, with or without 00015 * modification, are permitted provided that the following conditions 00016 * are met: 00017 * 1. Redistributions of source code must retain the above copyright 00018 * notice, this list of conditions and the following disclaimer. 00019 * 2. Redistributions in binary form must reproduce the above copyright 00020 * notice, this list of conditions and the following disclaimer in the 00021 * documentation and/or other materials provided with the distribution. 00022 * 3. Neither the name of CITEL Technologies Ltd nor the names of its contributors 00023 * may be used to endorse or promote products derived from this software 00024 * without specific prior written permission. 00025 * 00026 * THIS SOFTWARE IS PROVIDED BY CITEL TECHNOLOGIES AND CONTRIBUTORS ``AS IS'' 00027 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 00028 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 00029 * ARE DISCLAIMED. IN NO EVENT SHALL CITEL TECHNOLOGIES OR CONTRIBUTORS BE LIABLE 00030 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 00031 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 00032 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 00033 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 00034 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 00035 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 00036 * SUCH DAMAGE. 00037 * 00038 * This file is a contribution to the lwIP TCP/IP stack. 00039 * The Swedish Institute of Computer Science and Adam Dunkels 00040 * are specifically granted permission to redistribute this 00041 * source code. 00042 */ 00043 00044 /*------------------------------------------------------------- 00045 Note 1) 00046 Although the rfc requires V1 AND V2 capability 00047 we will only support v2 since now V1 is very old (August 1989) 00048 V1 can be added if required 00049 00050 a debug print and statistic have been implemented to 00051 show this up. 00052 ------------------------------------------------------------- 00053 ------------------------------------------------------------- 00054 Note 2) 00055 A query for a specific group address (as opposed to ALLHOSTS) 00056 has now been implemented as I am unsure if it is required 00057 00058 a debug print and statistic have been implemented to 00059 show this up. 00060 ------------------------------------------------------------- 00061 ------------------------------------------------------------- 00062 Note 3) 00063 The router alert rfc 2113 is implemented in outgoing packets 00064 but not checked rigorously incoming 00065 ------------------------------------------------------------- 00066 Steve Reynolds 00067 ------------------------------------------------------------*/ 00068 00069 /*----------------------------------------------------------------------------- 00070 * RFC 988 - Host extensions for IP multicasting - V0 00071 * RFC 1054 - Host extensions for IP multicasting - 00072 * RFC 1112 - Host extensions for IP multicasting - V1 00073 * RFC 2236 - Internet Group Management Protocol, Version 2 - V2 <- this code is based on this RFC (it's the "de facto" standard) 00074 * RFC 3376 - Internet Group Management Protocol, Version 3 - V3 00075 * RFC 4604 - Using Internet Group Management Protocol Version 3... - V3+ 00076 * RFC 2113 - IP Router Alert Option - 00077 *----------------------------------------------------------------------------*/ 00078 00079 /*----------------------------------------------------------------------------- 00080 * Includes 00081 *----------------------------------------------------------------------------*/ 00082 00083 #include "lwip/opt.h" 00084 00085 #if LWIP_IPV4 && LWIP_IGMP /* don't build if not configured for use in lwipopts.h */ 00086 00087 #include "lwip/igmp.h" 00088 #include "lwip/debug.h" 00089 #include "lwip/def.h" 00090 #include "lwip/mem.h" 00091 #include "lwip/ip.h" 00092 #include "lwip/inet_chksum.h" 00093 #include "lwip/netif.h" 00094 #include "lwip/stats.h" 00095 #include "lwip/prot/igmp.h" 00096 00097 #include <string.h> 00098 00099 static struct igmp_group *igmp_lookup_group(struct netif *ifp, const ip4_addr_t *addr); 00100 static err_t igmp_remove_group(struct netif *netif, struct igmp_group *group); 00101 static void igmp_timeout(struct netif *netif, struct igmp_group *group); 00102 static void igmp_start_timer(struct igmp_group *group, u8_t max_time); 00103 static void igmp_delaying_member(struct igmp_group *group, u8_t maxresp); 00104 static err_t igmp_ip_output_if(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest, struct netif *netif); 00105 static void igmp_send(struct netif *netif, struct igmp_group *group, u8_t type); 00106 00107 static ip4_addr_t allsystems; 00108 static ip4_addr_t allrouters; 00109 00110 /** 00111 * Initialize the IGMP module 00112 */ 00113 void 00114 igmp_init(void) 00115 { 00116 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_init: initializing\n")); 00117 00118 IP4_ADDR(&allsystems, 224, 0, 0, 1); 00119 IP4_ADDR(&allrouters, 224, 0, 0, 2); 00120 } 00121 00122 /** 00123 * Start IGMP processing on interface 00124 * 00125 * @param netif network interface on which start IGMP processing 00126 */ 00127 err_t 00128 igmp_start(struct netif *netif) 00129 { 00130 struct igmp_group *group; 00131 00132 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: starting IGMP processing on if %p\n", (void *)netif)); 00133 00134 group = igmp_lookup_group(netif, &allsystems); 00135 00136 if (group != NULL) { 00137 group->group_state = IGMP_GROUP_IDLE_MEMBER; 00138 group->use++; 00139 00140 /* Allow the igmp messages at the MAC level */ 00141 if (netif->igmp_mac_filter != NULL) { 00142 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: igmp_mac_filter(ADD ")); 00143 ip4_addr_debug_print_val(IGMP_DEBUG, allsystems); 00144 LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", (void *)netif)); 00145 netif->igmp_mac_filter(netif, &allsystems, NETIF_ADD_MAC_FILTER); 00146 } 00147 00148 return ERR_OK; 00149 } 00150 00151 return ERR_MEM; 00152 } 00153 00154 /** 00155 * Stop IGMP processing on interface 00156 * 00157 * @param netif network interface on which stop IGMP processing 00158 */ 00159 err_t 00160 igmp_stop(struct netif *netif) 00161 { 00162 struct igmp_group *group = netif_igmp_data(netif); 00163 00164 netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_IGMP, NULL); 00165 00166 while (group != NULL) { 00167 struct igmp_group *next = group->next; /* avoid use-after-free below */ 00168 00169 /* disable the group at the MAC level */ 00170 if (netif->igmp_mac_filter != NULL) { 00171 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_stop: igmp_mac_filter(DEL ")); 00172 ip4_addr_debug_print_val(IGMP_DEBUG, group->group_address); 00173 LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", (void *)netif)); 00174 netif->igmp_mac_filter(netif, &(group->group_address), NETIF_DEL_MAC_FILTER); 00175 } 00176 00177 /* free group */ 00178 memp_free(MEMP_IGMP_GROUP, group); 00179 00180 /* move to "next" */ 00181 group = next; 00182 } 00183 return ERR_OK; 00184 } 00185 00186 /** 00187 * Report IGMP memberships for this interface 00188 * 00189 * @param netif network interface on which report IGMP memberships 00190 */ 00191 void 00192 igmp_report_groups(struct netif *netif) 00193 { 00194 struct igmp_group *group = netif_igmp_data(netif); 00195 00196 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_report_groups: sending IGMP reports on if %p\n", (void *)netif)); 00197 00198 /* Skip the first group in the list, it is always the allsystems group added in igmp_start() */ 00199 if (group != NULL) { 00200 group = group->next; 00201 } 00202 00203 while (group != NULL) { 00204 igmp_delaying_member(group, IGMP_JOIN_DELAYING_MEMBER_TMR); 00205 group = group->next; 00206 } 00207 } 00208 00209 /** 00210 * Search for a group in the netif's igmp group list 00211 * 00212 * @param ifp the network interface for which to look 00213 * @param addr the group ip address to search for 00214 * @return a struct igmp_group* if the group has been found, 00215 * NULL if the group wasn't found. 00216 */ 00217 struct igmp_group * 00218 igmp_lookfor_group(struct netif *ifp, const ip4_addr_t *addr) 00219 { 00220 struct igmp_group *group = netif_igmp_data(ifp); 00221 00222 while (group != NULL) { 00223 if (ip4_addr_cmp(&(group->group_address), addr)) { 00224 return group; 00225 } 00226 group = group->next; 00227 } 00228 00229 /* to be clearer, we return NULL here instead of 00230 * 'group' (which is also NULL at this point). 00231 */ 00232 return NULL; 00233 } 00234 00235 /** 00236 * Search for a specific igmp group and create a new one if not found- 00237 * 00238 * @param ifp the network interface for which to look 00239 * @param addr the group ip address to search 00240 * @return a struct igmp_group*, 00241 * NULL on memory error. 00242 */ 00243 static struct igmp_group * 00244 igmp_lookup_group(struct netif *ifp, const ip4_addr_t *addr) 00245 { 00246 struct igmp_group *group; 00247 struct igmp_group *list_head = netif_igmp_data(ifp); 00248 00249 /* Search if the group already exists */ 00250 group = igmp_lookfor_group(ifp, addr); 00251 if (group != NULL) { 00252 /* Group already exists. */ 00253 return group; 00254 } 00255 00256 /* Group doesn't exist yet, create a new one */ 00257 group = (struct igmp_group *)memp_malloc(MEMP_IGMP_GROUP); 00258 if (group != NULL) { 00259 ip4_addr_set(&(group->group_address), addr); 00260 group->timer = 0; /* Not running */ 00261 group->group_state = IGMP_GROUP_NON_MEMBER; 00262 group->last_reporter_flag = 0; 00263 group->use = 0; 00264 00265 /* Ensure allsystems group is always first in list */ 00266 if (list_head == NULL) { 00267 /* this is the first entry in linked list */ 00268 LWIP_ASSERT("igmp_lookup_group: first group must be allsystems", 00269 (ip4_addr_cmp(addr, &allsystems) != 0)); 00270 group->next = NULL; 00271 netif_set_client_data(ifp, LWIP_NETIF_CLIENT_DATA_INDEX_IGMP, group); 00272 } else { 00273 /* append _after_ first entry */ 00274 LWIP_ASSERT("igmp_lookup_group: all except first group must not be allsystems", 00275 (ip4_addr_cmp(addr, &allsystems) == 0)); 00276 group->next = list_head->next; 00277 list_head->next = group; 00278 } 00279 } 00280 00281 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_lookup_group: %sallocated a new group with address ", (group ? "" : "impossible to "))); 00282 ip4_addr_debug_print(IGMP_DEBUG, addr); 00283 LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", (void *)ifp)); 00284 00285 return group; 00286 } 00287 00288 /** 00289 * Remove a group from netif's igmp group list, but don't free it yet 00290 * 00291 * @param group the group to remove from the netif's igmp group list 00292 * @return ERR_OK if group was removed from the list, an err_t otherwise 00293 */ 00294 static err_t 00295 igmp_remove_group(struct netif *netif, struct igmp_group *group) 00296 { 00297 err_t err = ERR_OK; 00298 struct igmp_group *tmp_group; 00299 00300 /* Skip the first group in the list, it is always the allsystems group added in igmp_start() */ 00301 for (tmp_group = netif_igmp_data(netif); tmp_group != NULL; tmp_group = tmp_group->next) { 00302 if (tmp_group->next == group) { 00303 tmp_group->next = group->next; 00304 break; 00305 } 00306 } 00307 /* Group not found in netif's igmp group list */ 00308 if (tmp_group == NULL) { 00309 err = ERR_ARG; 00310 } 00311 00312 return err; 00313 } 00314 00315 /** 00316 * Called from ip_input() if a new IGMP packet is received. 00317 * 00318 * @param p received igmp packet, p->payload pointing to the igmp header 00319 * @param inp network interface on which the packet was received 00320 * @param dest destination ip address of the igmp packet 00321 */ 00322 void 00323 igmp_input(struct pbuf *p, struct netif *inp, const ip4_addr_t *dest) 00324 { 00325 struct igmp_msg *igmp; 00326 struct igmp_group *group; 00327 struct igmp_group *groupref; 00328 00329 IGMP_STATS_INC(igmp.recv); 00330 00331 /* Note that the length CAN be greater than 8 but only 8 are used - All are included in the checksum */ 00332 if (p->len < IGMP_MINLEN) { 00333 pbuf_free(p); 00334 IGMP_STATS_INC(igmp.lenerr); 00335 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: length error\n")); 00336 return; 00337 } 00338 00339 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: message from ")); 00340 ip4_addr_debug_print_val(IGMP_DEBUG, ip4_current_header()->src); 00341 LWIP_DEBUGF(IGMP_DEBUG, (" to address ")); 00342 ip4_addr_debug_print_val(IGMP_DEBUG, ip4_current_header()->dest); 00343 LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", (void *)inp)); 00344 00345 /* Now calculate and check the checksum */ 00346 igmp = (struct igmp_msg *)p->payload; 00347 if (inet_chksum(igmp, p->len)) { 00348 pbuf_free(p); 00349 IGMP_STATS_INC(igmp.chkerr); 00350 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: checksum error\n")); 00351 return; 00352 } 00353 00354 /* Packet is ok so find an existing group */ 00355 group = igmp_lookfor_group(inp, dest); /* use the destination IP address of incoming packet */ 00356 00357 /* If group can be found or create... */ 00358 if (!group) { 00359 pbuf_free(p); 00360 IGMP_STATS_INC(igmp.drop); 00361 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP frame not for us\n")); 00362 return; 00363 } 00364 00365 /* NOW ACT ON THE INCOMING MESSAGE TYPE... */ 00366 switch (igmp->igmp_msgtype) { 00367 case IGMP_MEMB_QUERY: 00368 /* IGMP_MEMB_QUERY to the "all systems" address ? */ 00369 if ((ip4_addr_cmp(dest, &allsystems)) && ip4_addr_isany(&igmp->igmp_group_address)) { 00370 /* THIS IS THE GENERAL QUERY */ 00371 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))); 00372 00373 if (igmp->igmp_maxresp == 0) { 00374 IGMP_STATS_INC(igmp.rx_v1); 00375 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: got an all hosts query with time== 0 - this is V1 and not implemented - treat as v2\n")); 00376 igmp->igmp_maxresp = IGMP_V1_DELAYING_MEMBER_TMR; 00377 } else { 00378 IGMP_STATS_INC(igmp.rx_general); 00379 } 00380 00381 groupref = netif_igmp_data(inp); 00382 00383 /* Do not send messages on the all systems group address! */ 00384 /* Skip the first group in the list, it is always the allsystems group added in igmp_start() */ 00385 if (groupref != NULL) { 00386 groupref = groupref->next; 00387 } 00388 00389 while (groupref) { 00390 igmp_delaying_member(groupref, igmp->igmp_maxresp); 00391 groupref = groupref->next; 00392 } 00393 } else { 00394 /* IGMP_MEMB_QUERY to a specific group ? */ 00395 if (!ip4_addr_isany(&igmp->igmp_group_address)) { 00396 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_MEMB_QUERY to a specific group ")); 00397 ip4_addr_debug_print_val(IGMP_DEBUG, igmp->igmp_group_address); 00398 if (ip4_addr_cmp(dest, &allsystems)) { 00399 ip4_addr_t groupaddr; 00400 LWIP_DEBUGF(IGMP_DEBUG, (" using \"ALL SYSTEMS\" address (224.0.0.1) [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp))); 00401 /* we first need to re-look for the group since we used dest last time */ 00402 ip4_addr_copy(groupaddr, igmp->igmp_group_address); 00403 group = igmp_lookfor_group(inp, &groupaddr); 00404 } else { 00405 LWIP_DEBUGF(IGMP_DEBUG, (" with the group address as destination [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp))); 00406 } 00407 00408 if (group != NULL) { 00409 IGMP_STATS_INC(igmp.rx_group); 00410 igmp_delaying_member(group, igmp->igmp_maxresp); 00411 } else { 00412 IGMP_STATS_INC(igmp.drop); 00413 } 00414 } else { 00415 IGMP_STATS_INC(igmp.proterr); 00416 } 00417 } 00418 break; 00419 case IGMP_V2_MEMB_REPORT: 00420 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_V2_MEMB_REPORT\n")); 00421 IGMP_STATS_INC(igmp.rx_report); 00422 if (group->group_state == IGMP_GROUP_DELAYING_MEMBER) { 00423 /* This is on a specific group we have already looked up */ 00424 group->timer = 0; /* stopped */ 00425 group->group_state = IGMP_GROUP_IDLE_MEMBER; 00426 group->last_reporter_flag = 0; 00427 } 00428 break; 00429 default: 00430 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: unexpected msg %d in state %d on group %p on if %p\n", 00431 igmp->igmp_msgtype, group->group_state, (void *)&group, (void *)inp)); 00432 IGMP_STATS_INC(igmp.proterr); 00433 break; 00434 } 00435 00436 pbuf_free(p); 00437 return; 00438 } 00439 00440 /** 00441 * @ingroup igmp 00442 * Join a group on one network interface. 00443 * 00444 * @param ifaddr ip address of the network interface which should join a new group 00445 * @param groupaddr the ip address of the group which to join 00446 * @return ERR_OK if group was joined on the netif(s), an err_t otherwise 00447 */ 00448 err_t 00449 igmp_joingroup(const ip4_addr_t *ifaddr, const ip4_addr_t *groupaddr) 00450 { 00451 err_t err = ERR_VAL; /* no matching interface */ 00452 struct netif *netif; 00453 00454 LWIP_ASSERT_CORE_LOCKED(); 00455 00456 /* make sure it is multicast address */ 00457 LWIP_ERROR("igmp_joingroup: attempt to join non-multicast address", ip4_addr_ismulticast(groupaddr), return ERR_VAL;); 00458 LWIP_ERROR("igmp_joingroup: attempt to join allsystems address", (!ip4_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;); 00459 00460 /* loop through netif's */ 00461 NETIF_FOREACH(netif) { 00462 /* Should we join this interface ? */ 00463 if ((netif->flags & NETIF_FLAG_IGMP) && ((ip4_addr_isany(ifaddr) || ip4_addr_cmp(netif_ip4_addr(netif), ifaddr)))) { 00464 err = igmp_joingroup_netif(netif, groupaddr); 00465 if (err != ERR_OK) { 00466 /* Return an error even if some network interfaces are joined */ 00467 /** @todo undo any other netif already joined */ 00468 return err; 00469 } 00470 } 00471 } 00472 00473 return err; 00474 } 00475 00476 /** 00477 * @ingroup igmp 00478 * Join a group on one network interface. 00479 * 00480 * @param netif the network interface which should join a new group 00481 * @param groupaddr the ip address of the group which to join 00482 * @return ERR_OK if group was joined on the netif, an err_t otherwise 00483 */ 00484 err_t 00485 igmp_joingroup_netif(struct netif *netif, const ip4_addr_t *groupaddr) 00486 { 00487 struct igmp_group *group; 00488 00489 LWIP_ASSERT_CORE_LOCKED(); 00490 00491 /* make sure it is multicast address */ 00492 LWIP_ERROR("igmp_joingroup_netif: attempt to join non-multicast address", ip4_addr_ismulticast(groupaddr), return ERR_VAL;); 00493 LWIP_ERROR("igmp_joingroup_netif: attempt to join allsystems address", (!ip4_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;); 00494 00495 /* make sure it is an igmp-enabled netif */ 00496 LWIP_ERROR("igmp_joingroup_netif: attempt to join on non-IGMP netif", netif->flags & NETIF_FLAG_IGMP, return ERR_VAL;); 00497 00498 /* find group or create a new one if not found */ 00499 group = igmp_lookup_group(netif, groupaddr); 00500 00501 if (group != NULL) { 00502 /* This should create a new group, check the state to make sure */ 00503 if (group->group_state != IGMP_GROUP_NON_MEMBER) { 00504 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup_netif: join to group not in state IGMP_GROUP_NON_MEMBER\n")); 00505 } else { 00506 /* OK - it was new group */ 00507 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup_netif: join to new group: ")); 00508 ip4_addr_debug_print(IGMP_DEBUG, groupaddr); 00509 LWIP_DEBUGF(IGMP_DEBUG, ("\n")); 00510 00511 /* If first use of the group, allow the group at the MAC level */ 00512 if ((group->use == 0) && (netif->igmp_mac_filter != NULL)) { 00513 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup_netif: igmp_mac_filter(ADD ")); 00514 ip4_addr_debug_print(IGMP_DEBUG, groupaddr); 00515 LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", (void *)netif)); 00516 netif->igmp_mac_filter(netif, groupaddr, NETIF_ADD_MAC_FILTER); 00517 } 00518 00519 IGMP_STATS_INC(igmp.tx_join); 00520 igmp_send(netif, group, IGMP_V2_MEMB_REPORT); 00521 00522 igmp_start_timer(group, IGMP_JOIN_DELAYING_MEMBER_TMR); 00523 00524 /* Need to work out where this timer comes from */ 00525 group->group_state = IGMP_GROUP_DELAYING_MEMBER; 00526 } 00527 /* Increment group use */ 00528 group->use++; 00529 /* Join on this interface */ 00530 return ERR_OK; 00531 } else { 00532 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup_netif: Not enough memory to join to group\n")); 00533 return ERR_MEM; 00534 } 00535 } 00536 00537 /** 00538 * @ingroup igmp 00539 * Leave a group on one network interface. 00540 * 00541 * @param ifaddr ip address of the network interface which should leave a group 00542 * @param groupaddr the ip address of the group which to leave 00543 * @return ERR_OK if group was left on the netif(s), an err_t otherwise 00544 */ 00545 err_t 00546 igmp_leavegroup(const ip4_addr_t *ifaddr, const ip4_addr_t *groupaddr) 00547 { 00548 err_t err = ERR_VAL; /* no matching interface */ 00549 struct netif *netif; 00550 00551 LWIP_ASSERT_CORE_LOCKED(); 00552 00553 /* make sure it is multicast address */ 00554 LWIP_ERROR("igmp_leavegroup: attempt to leave non-multicast address", ip4_addr_ismulticast(groupaddr), return ERR_VAL;); 00555 LWIP_ERROR("igmp_leavegroup: attempt to leave allsystems address", (!ip4_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;); 00556 00557 /* loop through netif's */ 00558 NETIF_FOREACH(netif) { 00559 /* Should we leave this interface ? */ 00560 if ((netif->flags & NETIF_FLAG_IGMP) && ((ip4_addr_isany(ifaddr) || ip4_addr_cmp(netif_ip4_addr(netif), ifaddr)))) { 00561 err_t res = igmp_leavegroup_netif(netif, groupaddr); 00562 if (err != ERR_OK) { 00563 /* Store this result if we have not yet gotten a success */ 00564 err = res; 00565 } 00566 } 00567 } 00568 00569 return err; 00570 } 00571 00572 /** 00573 * @ingroup igmp 00574 * Leave a group on one network interface. 00575 * 00576 * @param netif the network interface which should leave a group 00577 * @param groupaddr the ip address of the group which to leave 00578 * @return ERR_OK if group was left on the netif, an err_t otherwise 00579 */ 00580 err_t 00581 igmp_leavegroup_netif(struct netif *netif, const ip4_addr_t *groupaddr) 00582 { 00583 struct igmp_group *group; 00584 00585 LWIP_ASSERT_CORE_LOCKED(); 00586 00587 /* make sure it is multicast address */ 00588 LWIP_ERROR("igmp_leavegroup_netif: attempt to leave non-multicast address", ip4_addr_ismulticast(groupaddr), return ERR_VAL;); 00589 LWIP_ERROR("igmp_leavegroup_netif: attempt to leave allsystems address", (!ip4_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;); 00590 00591 /* make sure it is an igmp-enabled netif */ 00592 LWIP_ERROR("igmp_leavegroup_netif: attempt to leave on non-IGMP netif", netif->flags & NETIF_FLAG_IGMP, return ERR_VAL;); 00593 00594 /* find group */ 00595 group = igmp_lookfor_group(netif, groupaddr); 00596 00597 if (group != NULL) { 00598 /* Only send a leave if the flag is set according to the state diagram */ 00599 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup_netif: Leaving group: ")); 00600 ip4_addr_debug_print(IGMP_DEBUG, groupaddr); 00601 LWIP_DEBUGF(IGMP_DEBUG, ("\n")); 00602 00603 /* If there is no other use of the group */ 00604 if (group->use <= 1) { 00605 /* Remove the group from the list */ 00606 igmp_remove_group(netif, group); 00607 00608 /* If we are the last reporter for this group */ 00609 if (group->last_reporter_flag) { 00610 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup_netif: sending leaving group\n")); 00611 IGMP_STATS_INC(igmp.tx_leave); 00612 igmp_send(netif, group, IGMP_LEAVE_GROUP); 00613 } 00614 00615 /* Disable the group at the MAC level */ 00616 if (netif->igmp_mac_filter != NULL) { 00617 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup_netif: igmp_mac_filter(DEL ")); 00618 ip4_addr_debug_print(IGMP_DEBUG, groupaddr); 00619 LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", (void *)netif)); 00620 netif->igmp_mac_filter(netif, groupaddr, NETIF_DEL_MAC_FILTER); 00621 } 00622 00623 /* Free group struct */ 00624 memp_free(MEMP_IGMP_GROUP, group); 00625 } else { 00626 /* Decrement group use */ 00627 group->use--; 00628 } 00629 return ERR_OK; 00630 } else { 00631 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup_netif: not member of group\n")); 00632 return ERR_VAL; 00633 } 00634 } 00635 00636 /** 00637 * The igmp timer function (both for NO_SYS=1 and =0) 00638 * Should be called every IGMP_TMR_INTERVAL milliseconds (100 ms is default). 00639 */ 00640 void 00641 igmp_tmr(void) 00642 { 00643 struct netif *netif; 00644 00645 NETIF_FOREACH(netif) { 00646 struct igmp_group *group = netif_igmp_data(netif); 00647 00648 while (group != NULL) { 00649 if (group->timer > 0) { 00650 group->timer--; 00651 if (group->timer == 0) { 00652 igmp_timeout(netif, group); 00653 } 00654 } 00655 group = group->next; 00656 } 00657 } 00658 } 00659 00660 /** 00661 * Called if a timeout for one group is reached. 00662 * Sends a report for this group. 00663 * 00664 * @param group an igmp_group for which a timeout is reached 00665 */ 00666 static void 00667 igmp_timeout(struct netif *netif, struct igmp_group *group) 00668 { 00669 /* If the state is IGMP_GROUP_DELAYING_MEMBER then we send a report for this group 00670 (unless it is the allsystems group) */ 00671 if ((group->group_state == IGMP_GROUP_DELAYING_MEMBER) && 00672 (!(ip4_addr_cmp(&(group->group_address), &allsystems)))) { 00673 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_timeout: report membership for group with address ")); 00674 ip4_addr_debug_print_val(IGMP_DEBUG, group->group_address); 00675 LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", (void *)netif)); 00676 00677 group->group_state = IGMP_GROUP_IDLE_MEMBER; 00678 00679 IGMP_STATS_INC(igmp.tx_report); 00680 igmp_send(netif, group, IGMP_V2_MEMB_REPORT); 00681 } 00682 } 00683 00684 /** 00685 * Start a timer for an igmp group 00686 * 00687 * @param group the igmp_group for which to start a timer 00688 * @param max_time the time in multiples of IGMP_TMR_INTERVAL (decrease with 00689 * every call to igmp_tmr()) 00690 */ 00691 static void 00692 igmp_start_timer(struct igmp_group *group, u8_t max_time) 00693 { 00694 #ifdef LWIP_RAND 00695 group->timer = (u16_t)(max_time > 2 ? (LWIP_RAND() % max_time) : 1); 00696 #else /* LWIP_RAND */ 00697 /* ATTENTION: use this only if absolutely necessary! */ 00698 group->timer = max_time / 2; 00699 #endif /* LWIP_RAND */ 00700 00701 if (group->timer == 0) { 00702 group->timer = 1; 00703 } 00704 } 00705 00706 /** 00707 * Delaying membership report for a group if necessary 00708 * 00709 * @param group the igmp_group for which "delaying" membership report 00710 * @param maxresp query delay 00711 */ 00712 static void 00713 igmp_delaying_member(struct igmp_group *group, u8_t maxresp) 00714 { 00715 if ((group->group_state == IGMP_GROUP_IDLE_MEMBER) || 00716 ((group->group_state == IGMP_GROUP_DELAYING_MEMBER) && 00717 ((group->timer == 0) || (maxresp < group->timer)))) { 00718 igmp_start_timer(group, maxresp); 00719 group->group_state = IGMP_GROUP_DELAYING_MEMBER; 00720 } 00721 } 00722 00723 00724 /** 00725 * Sends an IP packet on a network interface. This function constructs the IP header 00726 * and calculates the IP header checksum. If the source IP address is NULL, 00727 * the IP address of the outgoing network interface is filled in as source address. 00728 * 00729 * @param p the packet to send (p->payload points to the data, e.g. next 00730 protocol header; if dest == LWIP_IP_HDRINCL, p already includes an 00731 IP header and p->payload points to that IP header) 00732 * @param src the source IP address to send from (if src == IP4_ADDR_ANY, the 00733 * IP address of the netif used to send is used as source address) 00734 * @param dest the destination IP address to send the packet to 00735 * @param netif the netif on which to send this packet 00736 * @return ERR_OK if the packet was sent OK 00737 * ERR_BUF if p doesn't have enough space for IP/LINK headers 00738 * returns errors returned by netif->output 00739 */ 00740 static err_t 00741 igmp_ip_output_if(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest, struct netif *netif) 00742 { 00743 /* This is the "router alert" option */ 00744 u16_t ra[2]; 00745 ra[0] = PP_HTONS(ROUTER_ALERT); 00746 ra[1] = 0x0000; /* Router shall examine packet */ 00747 IGMP_STATS_INC(igmp.xmit); 00748 return ip4_output_if_opt(p, src, dest, IGMP_TTL, 0, IP_PROTO_IGMP, netif, ra, ROUTER_ALERTLEN); 00749 } 00750 00751 /** 00752 * Send an igmp packet to a specific group. 00753 * 00754 * @param group the group to which to send the packet 00755 * @param type the type of igmp packet to send 00756 */ 00757 static void 00758 igmp_send(struct netif *netif, struct igmp_group *group, u8_t type) 00759 { 00760 struct pbuf *p = NULL; 00761 struct igmp_msg *igmp = NULL; 00762 ip4_addr_t src = *IP4_ADDR_ANY4; 00763 ip4_addr_t *dest = NULL; 00764 00765 /* IP header + "router alert" option + IGMP header */ 00766 p = pbuf_alloc(PBUF_TRANSPORT, IGMP_MINLEN, PBUF_RAM); 00767 00768 if (p) { 00769 igmp = (struct igmp_msg *)p->payload; 00770 LWIP_ASSERT("igmp_send: check that first pbuf can hold struct igmp_msg", 00771 (p->len >= sizeof(struct igmp_msg))); 00772 ip4_addr_copy(src, *netif_ip4_addr(netif)); 00773 00774 if (type == IGMP_V2_MEMB_REPORT) { 00775 dest = &(group->group_address); 00776 ip4_addr_copy(igmp->igmp_group_address, group->group_address); 00777 group->last_reporter_flag = 1; /* Remember we were the last to report */ 00778 } else { 00779 if (type == IGMP_LEAVE_GROUP) { 00780 dest = &allrouters; 00781 ip4_addr_copy(igmp->igmp_group_address, group->group_address); 00782 } 00783 } 00784 00785 if ((type == IGMP_V2_MEMB_REPORT) || (type == IGMP_LEAVE_GROUP)) { 00786 igmp->igmp_msgtype = type; 00787 igmp->igmp_maxresp = 0; 00788 igmp->igmp_checksum = 0; 00789 igmp->igmp_checksum = inet_chksum(igmp, IGMP_MINLEN); 00790 00791 igmp_ip_output_if(p, &src, dest, netif); 00792 } 00793 00794 pbuf_free(p); 00795 } else { 00796 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_send: not enough memory for igmp_send\n")); 00797 IGMP_STATS_INC(igmp.memerr); 00798 } 00799 } 00800 00801 #endif /* LWIP_IPV4 && LWIP_IGMP */
Generated on Tue Jul 12 2022 13:54:28 by
