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