NetServices Stack source
Dependents: HelloWorld ServoInterfaceBoardExample1 4180_Lab4
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 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_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_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 = (struct igmp_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 = (struct ip_hdr *)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 } else { 00447 IGMP_STATS_INC(igmp.rx_general); 00448 } 00449 00450 groupref = igmp_group_list; 00451 while (groupref) { 00452 /* Do not send messages on the all systems group address! */ 00453 if ((groupref->netif == inp) && (!(ip_addr_cmp(&(groupref->group_address), &allsystems)))) { 00454 igmp_delaying_member(groupref, igmp->igmp_maxresp); 00455 } 00456 groupref = groupref->next; 00457 } 00458 } else { 00459 /* IGMP_MEMB_QUERY to a specific group ? */ 00460 if (!ip_addr_isany(&igmp->igmp_group_address)) { 00461 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_MEMB_QUERY to a specific group ")); 00462 ip_addr_debug_print(IGMP_DEBUG, &igmp->igmp_group_address); 00463 if (ip_addr_cmp(dest, &allsystems)) { 00464 ip_addr_t groupaddr; 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-look for the group since we used dest last time */ 00467 ip_addr_copy(groupaddr, igmp->igmp_group_address); 00468 group = igmp_lookfor_group(inp, &groupaddr); 00469 } else { 00470 LWIP_DEBUGF(IGMP_DEBUG, (" with the group address as destination [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp))); 00471 } 00472 00473 if (group != NULL) { 00474 IGMP_STATS_INC(igmp.rx_group); 00475 igmp_delaying_member(group, igmp->igmp_maxresp); 00476 } else { 00477 IGMP_STATS_INC(igmp.drop); 00478 } 00479 } else { 00480 IGMP_STATS_INC(igmp.proterr); 00481 } 00482 } 00483 break; 00484 } 00485 case IGMP_V2_MEMB_REPORT: { 00486 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_V2_MEMB_REPORT\n")); 00487 IGMP_STATS_INC(igmp.rx_report); 00488 if (group->group_state == IGMP_GROUP_DELAYING_MEMBER) { 00489 /* This is on a specific group we have already looked up */ 00490 group->timer = 0; /* stopped */ 00491 group->group_state = IGMP_GROUP_IDLE_MEMBER; 00492 group->last_reporter_flag = 0; 00493 } 00494 break; 00495 } 00496 default: { 00497 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: unexpected msg %d in state %d on group %p on if %p\n", 00498 igmp->igmp_msgtype, group->group_state, &group, group->netif)); 00499 IGMP_STATS_INC(igmp.proterr); 00500 break; 00501 } 00502 } 00503 00504 pbuf_free(p); 00505 return; 00506 } 00507 00508 /** 00509 * Join a group on one network interface. 00510 * 00511 * @param ifaddr ip address of the network interface which should join a new group 00512 * @param groupaddr the ip address of the group which to join 00513 * @return ERR_OK if group was joined on the netif(s), an err_t otherwise 00514 */ 00515 err_t 00516 igmp_joingroup(ip_addr_t *ifaddr, ip_addr_t *groupaddr) 00517 { 00518 err_t err = ERR_VAL; /* no matching interface */ 00519 struct igmp_group *group; 00520 struct netif *netif; 00521 00522 /* make sure it is multicast address */ 00523 LWIP_ERROR("igmp_joingroup: attempt to join non-multicast address", ip_addr_ismulticast(groupaddr), return ERR_VAL;); 00524 LWIP_ERROR("igmp_joingroup: attempt to join allsystems address", (!ip_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;); 00525 00526 /* loop through netif's */ 00527 netif = netif_list; 00528 while (netif != NULL) { 00529 /* Should we join this interface ? */ 00530 if ((netif->flags & NETIF_FLAG_IGMP) && ((ip_addr_isany(ifaddr) || ip_addr_cmp(&(netif->ip_addr), ifaddr)))) { 00531 /* find group or create a new one if not found */ 00532 group = igmp_lookup_group(netif, groupaddr); 00533 00534 if (group != NULL) { 00535 /* This should create a new group, check the state to make sure */ 00536 if (group->group_state != IGMP_GROUP_NON_MEMBER) { 00537 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: join to group not in state IGMP_GROUP_NON_MEMBER\n")); 00538 } else { 00539 /* OK - it was new group */ 00540 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: join to new group: ")); 00541 ip_addr_debug_print(IGMP_DEBUG, groupaddr); 00542 LWIP_DEBUGF(IGMP_DEBUG, ("\n")); 00543 00544 /* If first use of the group, allow the group at the MAC level */ 00545 if ((group->use==0) && (netif->igmp_mac_filter != NULL)) { 00546 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: igmp_mac_filter(ADD ")); 00547 ip_addr_debug_print(IGMP_DEBUG, groupaddr); 00548 LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif)); 00549 netif->igmp_mac_filter(netif, groupaddr, IGMP_ADD_MAC_FILTER); 00550 } 00551 00552 IGMP_STATS_INC(igmp.tx_join); 00553 igmp_send(group, IGMP_V2_MEMB_REPORT); 00554 00555 igmp_start_timer(group, IGMP_JOIN_DELAYING_MEMBER_TMR); 00556 00557 /* Need to work out where this timer comes from */ 00558 group->group_state = IGMP_GROUP_DELAYING_MEMBER; 00559 } 00560 /* Increment group use */ 00561 group->use++; 00562 /* Join on this interface */ 00563 err = ERR_OK; 00564 } else { 00565 /* Return an error even if some network interfaces are joined */ 00566 /** @todo undo any other netif already joined */ 00567 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: Not enought memory to join to group\n")); 00568 return ERR_MEM; 00569 } 00570 } 00571 /* proceed to next network interface */ 00572 netif = netif->next; 00573 } 00574 00575 return err; 00576 } 00577 00578 /** 00579 * Leave a group on one network interface. 00580 * 00581 * @param ifaddr ip address of the network interface which should leave a group 00582 * @param groupaddr the ip address of the group which to leave 00583 * @return ERR_OK if group was left on the netif(s), an err_t otherwise 00584 */ 00585 err_t 00586 igmp_leavegroup(ip_addr_t *ifaddr, ip_addr_t *groupaddr) 00587 { 00588 err_t err = ERR_VAL; /* no matching interface */ 00589 struct igmp_group *group; 00590 struct netif *netif; 00591 00592 /* make sure it is multicast address */ 00593 LWIP_ERROR("igmp_leavegroup: attempt to leave non-multicast address", ip_addr_ismulticast(groupaddr), return ERR_VAL;); 00594 LWIP_ERROR("igmp_leavegroup: attempt to leave allsystems address", (!ip_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;); 00595 00596 /* loop through netif's */ 00597 netif = netif_list; 00598 while (netif != NULL) { 00599 /* Should we leave this interface ? */ 00600 if ((netif->flags & NETIF_FLAG_IGMP) && ((ip_addr_isany(ifaddr) || ip_addr_cmp(&(netif->ip_addr), ifaddr)))) { 00601 /* find group */ 00602 group = igmp_lookfor_group(netif, groupaddr); 00603 00604 if (group != NULL) { 00605 /* Only send a leave if the flag is set according to the state diagram */ 00606 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: Leaving group: ")); 00607 ip_addr_debug_print(IGMP_DEBUG, groupaddr); 00608 LWIP_DEBUGF(IGMP_DEBUG, ("\n")); 00609 00610 /* If there is no other use of the group */ 00611 if (group->use <= 1) { 00612 /* If we are the last reporter for this group */ 00613 if (group->last_reporter_flag) { 00614 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: sending leaving group\n")); 00615 IGMP_STATS_INC(igmp.tx_leave); 00616 igmp_send(group, IGMP_LEAVE_GROUP); 00617 } 00618 00619 /* Disable the group at the MAC level */ 00620 if (netif->igmp_mac_filter != NULL) { 00621 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: igmp_mac_filter(DEL ")); 00622 ip_addr_debug_print(IGMP_DEBUG, groupaddr); 00623 LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif)); 00624 netif->igmp_mac_filter(netif, groupaddr, IGMP_DEL_MAC_FILTER); 00625 } 00626 00627 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: remove group: ")); 00628 ip_addr_debug_print(IGMP_DEBUG, groupaddr); 00629 LWIP_DEBUGF(IGMP_DEBUG, ("\n")); 00630 00631 /* Free the group */ 00632 igmp_remove_group(group); 00633 } else { 00634 /* Decrement group use */ 00635 group->use--; 00636 } 00637 /* Leave on this interface */ 00638 err = ERR_OK; 00639 } else { 00640 /* It's not a fatal error on "leavegroup" */ 00641 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: not member of group\n")); 00642 } 00643 } 00644 /* proceed to next network interface */ 00645 netif = netif->next; 00646 } 00647 00648 return err; 00649 } 00650 00651 /** 00652 * The igmp timer function (both for NO_SYS=1 and =0) 00653 * Should be called every IGMP_TMR_INTERVAL milliseconds (100 ms is default). 00654 */ 00655 void 00656 igmp_tmr(void) 00657 { 00658 struct igmp_group *group = igmp_group_list; 00659 00660 while (group != NULL) { 00661 if (group->timer > 0) { 00662 group->timer--; 00663 if (group->timer == 0) { 00664 igmp_timeout(group); 00665 } 00666 } 00667 group = group->next; 00668 } 00669 } 00670 00671 /** 00672 * Called if a timeout for one group is reached. 00673 * Sends a report for this group. 00674 * 00675 * @param group an igmp_group for which a timeout is reached 00676 */ 00677 static void 00678 igmp_timeout(struct igmp_group *group) 00679 { 00680 /* If the state is IGMP_GROUP_DELAYING_MEMBER then we send a report for this group */ 00681 if (group->group_state == IGMP_GROUP_DELAYING_MEMBER) { 00682 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_timeout: report membership for group with address ")); 00683 ip_addr_debug_print(IGMP_DEBUG, &(group->group_address)); 00684 LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", group->netif)); 00685 00686 IGMP_STATS_INC(igmp.tx_report); 00687 igmp_send(group, IGMP_V2_MEMB_REPORT); 00688 } 00689 } 00690 00691 /** 00692 * Start a timer for an igmp group 00693 * 00694 * @param group the igmp_group for which to start a timer 00695 * @param max_time the time in multiples of IGMP_TMR_INTERVAL (decrease with 00696 * every call to igmp_tmr()) 00697 */ 00698 static void 00699 igmp_start_timer(struct igmp_group *group, u8_t max_time) 00700 { 00701 /* ensure the input value is > 0 */ 00702 if (max_time == 0) { 00703 max_time = 1; 00704 } 00705 /* ensure the random value is > 0 */ 00706 group->timer = (LWIP_RAND() % (max_time - 1)) + 1; 00707 } 00708 00709 /** 00710 * Stop a timer for an igmp_group 00711 * 00712 * @param group the igmp_group for which to stop the timer 00713 */ 00714 static void 00715 igmp_stop_timer(struct igmp_group *group) 00716 { 00717 group->timer = 0; 00718 } 00719 00720 /** 00721 * Delaying membership report for a group if necessary 00722 * 00723 * @param group the igmp_group for which "delaying" membership report 00724 * @param maxresp query delay 00725 */ 00726 static void 00727 igmp_delaying_member(struct igmp_group *group, u8_t maxresp) 00728 { 00729 if ((group->group_state == IGMP_GROUP_IDLE_MEMBER) || 00730 ((group->group_state == IGMP_GROUP_DELAYING_MEMBER) && 00731 ((group->timer == 0) || (maxresp < group->timer)))) { 00732 igmp_start_timer(group, maxresp); 00733 group->group_state = IGMP_GROUP_DELAYING_MEMBER; 00734 } 00735 } 00736 00737 00738 /** 00739 * Sends an IP packet on a network interface. This function constructs the IP header 00740 * and calculates the IP header checksum. If the source IP address is NULL, 00741 * the IP address of the outgoing network interface is filled in as source address. 00742 * 00743 * @param p the packet to send (p->payload points to the data, e.g. next 00744 protocol header; if dest == IP_HDRINCL, p already includes an IP 00745 header and p->payload points to that IP header) 00746 * @param src the source IP address to send from (if src == IP_ADDR_ANY, the 00747 * IP address of the netif used to send is used as source address) 00748 * @param dest the destination IP address to send the packet to 00749 * @param ttl the TTL value to be set in the IP header 00750 * @param proto the PROTOCOL to be set in the IP header 00751 * @param netif the netif on which to send this packet 00752 * @return ERR_OK if the packet was sent OK 00753 * ERR_BUF if p doesn't have enough space for IP/LINK headers 00754 * returns errors returned by netif->output 00755 */ 00756 static err_t 00757 igmp_ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, struct netif *netif) 00758 { 00759 /* This is the "router alert" option */ 00760 u16_t ra[2]; 00761 ra[0] = PP_HTONS(ROUTER_ALERT); 00762 ra[1] = 0x0000; /* Router shall examine packet */ 00763 IGMP_STATS_INC(igmp.xmit); 00764 return ip_output_if_opt(p, src, dest, IGMP_TTL, 0, IP_PROTO_IGMP, netif, ra, ROUTER_ALERTLEN); 00765 } 00766 00767 /** 00768 * Send an igmp packet to a specific group. 00769 * 00770 * @param group the group to which to send the packet 00771 * @param type the type of igmp packet to send 00772 */ 00773 static void 00774 igmp_send(struct igmp_group *group, u8_t type) 00775 { 00776 struct pbuf* p = NULL; 00777 struct igmp_msg* igmp = NULL; 00778 ip_addr_t src = *IP_ADDR_ANY; 00779 ip_addr_t* dest = NULL; 00780 00781 /* IP header + "router alert" option + IGMP header */ 00782 p = pbuf_alloc(PBUF_TRANSPORT, IGMP_MINLEN, PBUF_RAM); 00783 00784 if (p) { 00785 igmp = (struct igmp_msg *)p->payload; 00786 LWIP_ASSERT("igmp_send: check that first pbuf can hold struct igmp_msg", 00787 (p->len >= sizeof(struct igmp_msg))); 00788 ip_addr_copy(src, group->netif->ip_addr); 00789 00790 if (type == IGMP_V2_MEMB_REPORT) { 00791 dest = &(group->group_address); 00792 ip_addr_copy(igmp->igmp_group_address, group->group_address); 00793 group->last_reporter_flag = 1; /* Remember we were the last to report */ 00794 } else { 00795 if (type == IGMP_LEAVE_GROUP) { 00796 dest = &allrouters; 00797 ip_addr_copy(igmp->igmp_group_address, group->group_address); 00798 } 00799 } 00800 00801 if ((type == IGMP_V2_MEMB_REPORT) || (type == IGMP_LEAVE_GROUP)) { 00802 igmp->igmp_msgtype = type; 00803 igmp->igmp_maxresp = 0; 00804 igmp->igmp_checksum = 0; 00805 igmp->igmp_checksum = inet_chksum(igmp, IGMP_MINLEN); 00806 00807 igmp_ip_output_if(p, &src, dest, group->netif); 00808 } 00809 00810 pbuf_free(p); 00811 } else { 00812 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_send: not enough memory for igmp_send\n")); 00813 IGMP_STATS_INC(igmp.memerr); 00814 } 00815 } 00816 00817 #endif /* LWIP_IGMP */
Generated on Tue Jul 12 2022 11:52:57 by
![doxygen](doxygen.png)