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