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