Version of http://mbed.org/cookbook/NetServicesTribute with setting set the same for LPC2368
Dependents: UDPSocketExample 24LCxx_I2CApp WeatherPlatform_pachube HvZServerLib ... more
igmp.c
00001 #pragma diag_remark 177 00002 /** 00003 * @file 00004 * IGMP - Internet Group Management Protocol 00005 * 00006 */ 00007 00008 /* 00009 * Copyright (c) 2002 CITEL Technologies Ltd. 00010 * All rights reserved. 00011 * 00012 * Redistribution and use in source and binary forms, with or without 00013 * modification, are permitted provided that the following conditions 00014 * are met: 00015 * 1. Redistributions of source code must retain the above copyright 00016 * notice, this list of conditions and the following disclaimer. 00017 * 2. Redistributions in binary form must reproduce the above copyright 00018 * notice, this list of conditions and the following disclaimer in the 00019 * documentation and/or other materials provided with the distribution. 00020 * 3. Neither the name of CITEL Technologies Ltd nor the names of its contributors 00021 * may be used to endorse or promote products derived from this software 00022 * without specific prior written permission. 00023 * 00024 * THIS SOFTWARE IS PROVIDED BY CITEL TECHNOLOGIES AND CONTRIBUTORS ``AS IS'' 00025 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 00026 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 00027 * ARE DISCLAIMED. IN NO EVENT SHALL CITEL TECHNOLOGIES OR CONTRIBUTORS BE LIABLE 00028 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 00029 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 00030 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 00031 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 00032 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 00033 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 00034 * SUCH DAMAGE. 00035 * 00036 * This file is a contribution to the lwIP TCP/IP stack. 00037 * The Swedish Institute of Computer Science and Adam Dunkels 00038 * are specifically granted permission to redistribute this 00039 * source code. 00040 */ 00041 00042 /*------------------------------------------------------------- 00043 Note 1) 00044 Although the rfc requires V1 AND V2 capability 00045 we will only support v2 since now V1 is very old (August 1989) 00046 V1 can be added if required 00047 00048 a debug print and statistic have been implemented to 00049 show this up. 00050 ------------------------------------------------------------- 00051 ------------------------------------------------------------- 00052 Note 2) 00053 A query for a specific group address (as opposed to ALLHOSTS) 00054 has now been implemented as I am unsure if it is required 00055 00056 a debug print and statistic have been implemented to 00057 show this up. 00058 ------------------------------------------------------------- 00059 ------------------------------------------------------------- 00060 Note 3) 00061 The router alert rfc 2113 is implemented in outgoing packets 00062 but not checked rigorously incoming 00063 ------------------------------------------------------------- 00064 Steve Reynolds 00065 ------------------------------------------------------------*/ 00066 00067 /*----------------------------------------------------------------------------- 00068 * RFC 988 - Host extensions for IP multicasting - V0 00069 * RFC 1054 - Host extensions for IP multicasting - 00070 * RFC 1112 - Host extensions for IP multicasting - V1 00071 * RFC 2236 - Internet Group Management Protocol, Version 2 - V2 <- this code is based on this RFC (it's the "de facto" standard) 00072 * RFC 3376 - Internet Group Management Protocol, Version 3 - V3 00073 * RFC 4604 - Using Internet Group Management Protocol Version 3... - V3+ 00074 * RFC 2113 - IP Router Alert Option - 00075 *----------------------------------------------------------------------------*/ 00076 00077 /*----------------------------------------------------------------------------- 00078 * Includes 00079 *----------------------------------------------------------------------------*/ 00080 00081 #include "lwip/opt.h" 00082 00083 #if LWIP_IGMP /* don't build if not configured for use in lwipopts.h */ 00084 00085 #include "lwip/igmp.h" 00086 #include "lwip/debug.h" 00087 #include "lwip/def.h" 00088 #include "lwip/mem.h" 00089 #include "lwip/ip.h" 00090 #include "lwip/inet_chksum.h" 00091 #include "lwip/netif.h" 00092 #include "lwip/icmp.h" 00093 #include "lwip/udp.h" 00094 #include "lwip/tcp.h" 00095 #include "lwip/stats.h" 00096 00097 #include "string.h" 00098 00099 /* 00100 * IGMP constants 00101 */ 00102 #define IGMP_TTL 1 00103 #define IGMP_MINLEN 8 00104 #define ROUTER_ALERT 0x9404 00105 #define ROUTER_ALERTLEN 4 00106 00107 /* 00108 * IGMP message types, including version number. 00109 */ 00110 #define IGMP_MEMB_QUERY 0x11 /* Membership query */ 00111 #define IGMP_V1_MEMB_REPORT 0x12 /* Ver. 1 membership report */ 00112 #define IGMP_V2_MEMB_REPORT 0x16 /* Ver. 2 membership report */ 00113 #define IGMP_LEAVE_GROUP 0x17 /* Leave-group message */ 00114 00115 /* Group membership states */ 00116 #define IGMP_GROUP_NON_MEMBER 0 00117 #define IGMP_GROUP_DELAYING_MEMBER 1 00118 #define IGMP_GROUP_IDLE_MEMBER 2 00119 00120 /** 00121 * IGMP packet format. 00122 */ 00123 #ifdef PACK_STRUCT_USE_INCLUDES 00124 # include "arch/bpstruct.h" 00125 #endif 00126 PACK_STRUCT_BEGIN 00127 struct igmp_msg { 00128 PACK_STRUCT_FIELD(u8_t igmp_msgtype); 00129 PACK_STRUCT_FIELD(u8_t igmp_maxresp); 00130 PACK_STRUCT_FIELD(u16_t igmp_checksum); 00131 PACK_STRUCT_FIELD(ip_addr_p_t igmp_group_address); 00132 } PACK_STRUCT_STRUCT; 00133 PACK_STRUCT_END 00134 #ifdef PACK_STRUCT_USE_INCLUDES 00135 # include "arch/epstruct.h" 00136 #endif 00137 00138 00139 static struct igmp_group *igmp_lookup_group(struct netif *ifp, ip_addr_t *addr); 00140 static err_t igmp_remove_group(struct igmp_group *group); 00141 static void igmp_timeout( struct igmp_group *group); 00142 static void igmp_start_timer(struct igmp_group *group, u8_t max_time); 00143 static void igmp_stop_timer(struct igmp_group *group); 00144 static void igmp_delaying_member(struct igmp_group *group, u8_t maxresp); 00145 static err_t igmp_ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, struct netif *netif); 00146 static void igmp_send(struct igmp_group *group, u8_t type); 00147 00148 00149 static struct igmp_group* igmp_group_list; 00150 static ip_addr_t allsystems; 00151 static ip_addr_t allrouters; 00152 00153 00154 /** 00155 * Initialize the IGMP module 00156 */ 00157 void 00158 igmp_init(void) 00159 { 00160 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_init: initializing\n")); 00161 00162 IP4_ADDR(&allsystems, 224, 0, 0, 1); 00163 IP4_ADDR(&allrouters, 224, 0, 0, 2); 00164 } 00165 00166 #ifdef LWIP_DEBUG 00167 /** 00168 * Dump global IGMP groups list 00169 */ 00170 void 00171 igmp_dump_group_list() 00172 { 00173 struct igmp_group *group = igmp_group_list; 00174 00175 while (group != NULL) { 00176 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_dump_group_list: [%"U32_F"] ", (u32_t)(group->group_state))); 00177 ip_addr_debug_print(IGMP_DEBUG, &group->group_address); 00178 LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", group->netif)); 00179 group = group->next; 00180 } 00181 LWIP_DEBUGF(IGMP_DEBUG, ("\n")); 00182 } 00183 #else 00184 #define igmp_dump_group_list() 00185 #endif /* LWIP_DEBUG */ 00186 00187 /** 00188 * Start IGMP processing on interface 00189 * 00190 * @param netif network interface on which start IGMP processing 00191 */ 00192 err_t 00193 igmp_start(struct netif *netif) 00194 { 00195 struct igmp_group* group; 00196 00197 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: starting IGMP processing on if %p\n", netif)); 00198 00199 group = igmp_lookup_group(netif, &allsystems); 00200 00201 if (group != NULL) { 00202 group->group_state = IGMP_GROUP_IDLE_MEMBER; 00203 group->use++; 00204 00205 /* Allow the igmp messages at the MAC level */ 00206 if (netif->igmp_mac_filter != NULL) { 00207 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: igmp_mac_filter(ADD ")); 00208 ip_addr_debug_print(IGMP_DEBUG, &allsystems); 00209 LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif)); 00210 netif->igmp_mac_filter(netif, &allsystems, IGMP_ADD_MAC_FILTER); 00211 } 00212 00213 return ERR_OK; 00214 } 00215 00216 return ERR_MEM; 00217 } 00218 00219 /** 00220 * Stop IGMP processing on interface 00221 * 00222 * @param netif network interface on which stop IGMP processing 00223 */ 00224 err_t 00225 igmp_stop(struct netif *netif) 00226 { 00227 struct igmp_group *group = igmp_group_list; 00228 struct igmp_group *prev = NULL; 00229 struct igmp_group *next; 00230 00231 /* look for groups joined on this interface further down the list */ 00232 while (group != NULL) { 00233 next = group->next; 00234 /* is it a group joined on this interface? */ 00235 if (group->netif == netif) { 00236 /* is it the first group of the list? */ 00237 if (group == igmp_group_list) { 00238 igmp_group_list = next; 00239 } 00240 /* is there a "previous" group defined? */ 00241 if (prev != NULL) { 00242 prev->next = next; 00243 } 00244 /* disable the group at the MAC level */ 00245 if (netif->igmp_mac_filter != NULL) { 00246 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_stop: igmp_mac_filter(DEL ")); 00247 ip_addr_debug_print(IGMP_DEBUG, &group->group_address); 00248 LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif)); 00249 netif->igmp_mac_filter(netif, &(group->group_address), IGMP_DEL_MAC_FILTER); 00250 } 00251 /* free group */ 00252 memp_free(MEMP_IGMP_GROUP, group); 00253 } else { 00254 /* change the "previous" */ 00255 prev = group; 00256 } 00257 /* move to "next" */ 00258 group = next; 00259 } 00260 return ERR_OK; 00261 } 00262 00263 /** 00264 * Report IGMP memberships for this interface 00265 * 00266 * @param netif network interface on which report IGMP memberships 00267 */ 00268 void 00269 igmp_report_groups(struct netif *netif) 00270 { 00271 struct igmp_group *group = igmp_group_list; 00272 00273 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_report_groups: sending IGMP reports on if %p\n", netif)); 00274 00275 while (group != NULL) { 00276 if (group->netif == netif) { 00277 igmp_delaying_member(group, IGMP_JOIN_DELAYING_MEMBER_TMR); 00278 } 00279 group = group->next; 00280 } 00281 } 00282 00283 /** 00284 * Search for a group in the global igmp_group_list 00285 * 00286 * @param ifp the network interface for which to look 00287 * @param addr the group ip address to search for 00288 * @return a struct igmp_group* if the group has been found, 00289 * NULL if the group wasn't found. 00290 */ 00291 struct igmp_group * 00292 igmp_lookfor_group(struct netif *ifp, ip_addr_t *addr) 00293 { 00294 struct igmp_group *group = igmp_group_list; 00295 00296 while (group != NULL) { 00297 if ((group->netif == ifp) && (ip_addr_cmp(&(group->group_address), addr))) { 00298 return group; 00299 } 00300 group = group->next; 00301 } 00302 00303 /* to be clearer, we return NULL here instead of 00304 * 'group' (which is also NULL at this point). 00305 */ 00306 return NULL; 00307 } 00308 00309 /** 00310 * Search for a specific igmp group and create a new one if not found- 00311 * 00312 * @param ifp the network interface for which to look 00313 * @param addr the group ip address to search 00314 * @return a struct igmp_group*, 00315 * NULL on memory error. 00316 */ 00317 struct igmp_group * 00318 igmp_lookup_group(struct netif *ifp, ip_addr_t *addr) 00319 { 00320 struct igmp_group *group = igmp_group_list; 00321 00322 /* Search if the group already exists */ 00323 group = igmp_lookfor_group(ifp, addr); 00324 if (group != NULL) { 00325 /* Group already exists. */ 00326 return group; 00327 } 00328 00329 /* Group doesn't exist yet, create a new one */ 00330 group = (struct igmp_group *)memp_malloc(MEMP_IGMP_GROUP); 00331 if (group != NULL) { 00332 group->netif = ifp; 00333 ip_addr_set(&(group->group_address), addr); 00334 group->timer = 0; /* Not running */ 00335 group->group_state = IGMP_GROUP_NON_MEMBER; 00336 group->last_reporter_flag = 0; 00337 group->use = 0; 00338 group->next = igmp_group_list; 00339 00340 igmp_group_list = group; 00341 } 00342 00343 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_lookup_group: %sallocated a new group with address ", (group?"":"impossible to "))); 00344 ip_addr_debug_print(IGMP_DEBUG, addr); 00345 LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", ifp)); 00346 00347 return group; 00348 } 00349 00350 /** 00351 * Remove a group in the global igmp_group_list 00352 * 00353 * @param group the group to remove from the global igmp_group_list 00354 * @return ERR_OK if group was removed from the list, an err_t otherwise 00355 */ 00356 static err_t 00357 igmp_remove_group(struct igmp_group *group) 00358 { 00359 err_t err = ERR_OK; 00360 00361 /* Is it the first group? */ 00362 if (igmp_group_list == group) { 00363 igmp_group_list = group->next; 00364 } else { 00365 /* look for group further down the list */ 00366 struct igmp_group *tmpGroup; 00367 for (tmpGroup = igmp_group_list; tmpGroup != NULL; tmpGroup = tmpGroup->next) { 00368 if (tmpGroup->next == group) { 00369 tmpGroup->next = group->next; 00370 break; 00371 } 00372 } 00373 /* Group not found in the global igmp_group_list */ 00374 if (tmpGroup == NULL) 00375 err = ERR_ARG; 00376 } 00377 /* free group */ 00378 memp_free(MEMP_IGMP_GROUP, group); 00379 00380 return err; 00381 } 00382 00383 /** 00384 * Called from ip_input() if a new IGMP packet is received. 00385 * 00386 * @param p received igmp packet, p->payload pointing to the ip header 00387 * @param inp network interface on which the packet was received 00388 * @param dest destination ip address of the igmp packet 00389 */ 00390 void 00391 igmp_input(struct pbuf *p, struct netif *inp, ip_addr_t *dest) 00392 { 00393 struct ip_hdr * iphdr; 00394 struct igmp_msg* igmp; 00395 struct igmp_group* group; 00396 struct igmp_group* groupref; 00397 00398 IGMP_STATS_INC(igmp.recv); 00399 00400 /* Note that the length CAN be greater than 8 but only 8 are used - All are included in the checksum */ 00401 iphdr = (struct ip_hdr *)p->payload; 00402 if (pbuf_header(p, -(s16_t)(IPH_HL(iphdr) * 4)) || (p->len < IGMP_MINLEN)) { 00403 pbuf_free(p); 00404 IGMP_STATS_INC(igmp.lenerr); 00405 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: length error\n")); 00406 return; 00407 } 00408 00409 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: message from ")); 00410 ip_addr_debug_print(IGMP_DEBUG, &(iphdr->src)); 00411 LWIP_DEBUGF(IGMP_DEBUG, (" to address ")); 00412 ip_addr_debug_print(IGMP_DEBUG, &(iphdr->dest)); 00413 LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", inp)); 00414 00415 /* Now calculate and check the checksum */ 00416 igmp = (struct igmp_msg *)p->payload; 00417 if (inet_chksum(igmp, p->len)) { 00418 pbuf_free(p); 00419 IGMP_STATS_INC(igmp.chkerr); 00420 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: checksum error\n")); 00421 return; 00422 } 00423 00424 /* Packet is ok so find an existing group */ 00425 group = igmp_lookfor_group(inp, dest); /* use the destination IP address of incoming packet */ 00426 00427 /* If group can be found or create... */ 00428 if (!group) { 00429 pbuf_free(p); 00430 IGMP_STATS_INC(igmp.drop); 00431 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP frame not for us\n")); 00432 return; 00433 } 00434 00435 /* NOW ACT ON THE INCOMING MESSAGE TYPE... */ 00436 switch (igmp->igmp_msgtype) { 00437 case IGMP_MEMB_QUERY: { 00438 /* IGMP_MEMB_QUERY to the "all systems" address ? */ 00439 if ((ip_addr_cmp(dest, &allsystems)) && ip_addr_isany(&igmp->igmp_group_address)) { 00440 /* THIS IS THE GENERAL QUERY */ 00441 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))); 00442 00443 if (igmp->igmp_maxresp == 0) { 00444 IGMP_STATS_INC(igmp.rx_v1); 00445 LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: got an all hosts query with time== 0 - this is V1 and not implemented - treat as v2\n")); 00446 igmp->igmp_maxresp = IGMP_V1_DELAYING_MEMBER_TMR; 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 ip_addr_t groupaddr; 00466 LWIP_DEBUGF(IGMP_DEBUG, (" using \"ALL SYSTEMS\" address (224.0.0.1) [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp))); 00467 /* we first need to re-look for the group since we used dest last time */ 00468 ip_addr_copy(groupaddr, igmp->igmp_group_address); 00469 group = igmp_lookfor_group(inp, &groupaddr); 00470 } else { 00471 LWIP_DEBUGF(IGMP_DEBUG, (" with the group address as destination [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp))); 00472 } 00473 00474 if (group != NULL) { 00475 IGMP_STATS_INC(igmp.rx_group); 00476 igmp_delaying_member(group, igmp->igmp_maxresp); 00477 } else { 00478 IGMP_STATS_INC(igmp.drop); 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] = PP_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 = (struct igmp_msg *)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 14:29:23 by 1.7.2