Marco Zecchini
/
Example_RTOS
Rtos API example
Embed:
(wiki syntax)
Show/hide line numbers
lwip_mld6.c
Go to the documentation of this file.
00001 /** 00002 * @file 00003 * Multicast listener discovery 00004 * 00005 * @defgroup mld6 MLD6 00006 * @ingroup ip6 00007 * Multicast listener discovery for IPv6. Aims to be compliant with RFC 2710. 00008 * No support for MLDv2.\n 00009 * To be called from TCPIP thread 00010 */ 00011 00012 /* 00013 * Copyright (c) 2010 Inico Technologies Ltd. 00014 * All rights reserved. 00015 * 00016 * Redistribution and use in source and binary forms, with or without modification, 00017 * are permitted provided that the following conditions are met: 00018 * 00019 * 1. Redistributions of source code must retain the above copyright notice, 00020 * this list of conditions and the following disclaimer. 00021 * 2. Redistributions in binary form must reproduce the above copyright notice, 00022 * this list of conditions and the following disclaimer in the documentation 00023 * and/or other materials provided with the distribution. 00024 * 3. The name of the author may not be used to endorse or promote products 00025 * derived from this software without specific prior written permission. 00026 * 00027 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 00028 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 00029 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 00030 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 00031 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 00032 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 00033 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 00034 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 00035 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 00036 * OF SUCH DAMAGE. 00037 * 00038 * This file is part of the lwIP TCP/IP stack. 00039 * 00040 * Author: Ivan Delamer <delamer@inicotech.com> 00041 * 00042 * 00043 * Please coordinate changes and requests with Ivan Delamer 00044 * <delamer@inicotech.com> 00045 */ 00046 00047 /* Based on igmp.c implementation of igmp v2 protocol */ 00048 00049 #include "lwip/opt.h" 00050 00051 #if LWIP_IPV6 && LWIP_IPV6_MLD /* don't build if not configured for use in lwipopts.h */ 00052 00053 #include "lwip/mld6.h" 00054 #include "lwip/prot/mld6.h" 00055 #include "lwip/icmp6.h" 00056 #include "lwip/ip6.h" 00057 #include "lwip/ip6_addr.h" 00058 #include "lwip/ip.h" 00059 #include "lwip/inet_chksum.h" 00060 #include "lwip/pbuf.h" 00061 #include "lwip/netif.h" 00062 #include "lwip/memp.h" 00063 #include "lwip/stats.h" 00064 00065 #include <string.h> 00066 00067 00068 /* 00069 * MLD constants 00070 */ 00071 #define MLD6_HL 1 00072 #define MLD6_JOIN_DELAYING_MEMBER_TMR_MS (500) 00073 00074 #define MLD6_GROUP_NON_MEMBER 0 00075 #define MLD6_GROUP_DELAYING_MEMBER 1 00076 #define MLD6_GROUP_IDLE_MEMBER 2 00077 00078 /* Forward declarations. */ 00079 static struct mld_group *mld6_new_group(struct netif *ifp, const ip6_addr_t *addr); 00080 static err_t mld6_remove_group(struct netif *netif, struct mld_group *group); 00081 static void mld6_delayed_report(struct mld_group *group, u16_t maxresp); 00082 static void mld6_send(struct netif *netif, struct mld_group *group, u8_t type); 00083 00084 00085 /** 00086 * Stop MLD processing on interface 00087 * 00088 * @param netif network interface on which stop MLD processing 00089 */ 00090 err_t 00091 mld6_stop(struct netif *netif) 00092 { 00093 struct mld_group *group = netif_mld6_data(netif); 00094 00095 netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_MLD6, NULL); 00096 00097 while (group != NULL) { 00098 struct mld_group *next = group->next; /* avoid use-after-free below */ 00099 00100 /* disable the group at the MAC level */ 00101 if (netif->mld_mac_filter != NULL) { 00102 netif->mld_mac_filter(netif, &(group->group_address), NETIF_DEL_MAC_FILTER); 00103 } 00104 00105 /* free group */ 00106 memp_free(MEMP_MLD6_GROUP, group); 00107 00108 /* move to "next" */ 00109 group = next; 00110 } 00111 return ERR_OK; 00112 } 00113 00114 /** 00115 * Report MLD memberships for this interface 00116 * 00117 * @param netif network interface on which report MLD memberships 00118 */ 00119 void 00120 mld6_report_groups(struct netif *netif) 00121 { 00122 struct mld_group *group = netif_mld6_data(netif); 00123 00124 while (group != NULL) { 00125 mld6_delayed_report(group, MLD6_JOIN_DELAYING_MEMBER_TMR_MS); 00126 group = group->next; 00127 } 00128 } 00129 00130 /** 00131 * Search for a group that is joined on a netif 00132 * 00133 * @param ifp the network interface for which to look 00134 * @param addr the group ipv6 address to search for 00135 * @return a struct mld_group* if the group has been found, 00136 * NULL if the group wasn't found. 00137 */ 00138 struct mld_group * 00139 mld6_lookfor_group(struct netif *ifp, const ip6_addr_t *addr) 00140 { 00141 struct mld_group *group = netif_mld6_data(ifp); 00142 00143 while (group != NULL) { 00144 if (ip6_addr_cmp(&(group->group_address), addr)) { 00145 return group; 00146 } 00147 group = group->next; 00148 } 00149 00150 return NULL; 00151 } 00152 00153 00154 /** 00155 * create a new group 00156 * 00157 * @param ifp the network interface for which to create 00158 * @param addr the new group ipv6 00159 * @return a struct mld_group*, 00160 * NULL on memory error. 00161 */ 00162 static struct mld_group * 00163 mld6_new_group(struct netif *ifp, const ip6_addr_t *addr) 00164 { 00165 struct mld_group *group; 00166 00167 group = (struct mld_group *)memp_malloc(MEMP_MLD6_GROUP); 00168 if (group != NULL) { 00169 ip6_addr_set(&(group->group_address), addr); 00170 group->timer = 0; /* Not running */ 00171 group->group_state = MLD6_GROUP_IDLE_MEMBER; 00172 group->last_reporter_flag = 0; 00173 group->use = 0; 00174 group->next = netif_mld6_data(ifp); 00175 00176 netif_set_client_data(ifp, LWIP_NETIF_CLIENT_DATA_INDEX_MLD6, group); 00177 } 00178 00179 return group; 00180 } 00181 00182 /** 00183 * Remove a group from the mld_group_list, but do not free it yet 00184 * 00185 * @param group the group to remove 00186 * @return ERR_OK if group was removed from the list, an err_t otherwise 00187 */ 00188 static err_t 00189 mld6_remove_group(struct netif *netif, struct mld_group *group) 00190 { 00191 err_t err = ERR_OK; 00192 00193 /* Is it the first group? */ 00194 if (netif_mld6_data(netif) == group) { 00195 netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_MLD6, group->next); 00196 } else { 00197 /* look for group further down the list */ 00198 struct mld_group *tmpGroup; 00199 for (tmpGroup = netif_mld6_data(netif); tmpGroup != NULL; tmpGroup = tmpGroup->next) { 00200 if (tmpGroup->next == group) { 00201 tmpGroup->next = group->next; 00202 break; 00203 } 00204 } 00205 /* Group not find group */ 00206 if (tmpGroup == NULL) { 00207 err = ERR_ARG; 00208 } 00209 } 00210 00211 return err; 00212 } 00213 00214 00215 /** 00216 * Process an input MLD message. Called by icmp6_input. 00217 * 00218 * @param p the mld packet, p->payload pointing to the icmpv6 header 00219 * @param inp the netif on which this packet was received 00220 */ 00221 void 00222 mld6_input(struct pbuf *p, struct netif *inp) 00223 { 00224 struct mld_header *mld_hdr; 00225 struct mld_group *group; 00226 00227 MLD6_STATS_INC(mld6.recv); 00228 00229 /* Check that mld header fits in packet. */ 00230 if (p->len < sizeof(struct mld_header)) { 00231 /* @todo debug message */ 00232 pbuf_free(p); 00233 MLD6_STATS_INC(mld6.lenerr); 00234 MLD6_STATS_INC(mld6.drop); 00235 return; 00236 } 00237 00238 mld_hdr = (struct mld_header *)p->payload; 00239 00240 switch (mld_hdr->type) { 00241 case ICMP6_TYPE_MLQ: /* Multicast listener query. */ 00242 /* Is it a general query? */ 00243 if (ip6_addr_isallnodes_linklocal(ip6_current_dest_addr()) && 00244 ip6_addr_isany(&(mld_hdr->multicast_address))) { 00245 MLD6_STATS_INC(mld6.rx_general); 00246 /* Report all groups, except all nodes group, and if-local groups. */ 00247 group = netif_mld6_data(inp); 00248 while (group != NULL) { 00249 if ((!(ip6_addr_ismulticast_iflocal(&(group->group_address)))) && 00250 (!(ip6_addr_isallnodes_linklocal(&(group->group_address))))) { 00251 mld6_delayed_report(group, mld_hdr->max_resp_delay); 00252 } 00253 group = group->next; 00254 } 00255 } else { 00256 /* Have we joined this group? 00257 * We use IP6 destination address to have a memory aligned copy. 00258 * mld_hdr->multicast_address should be the same. */ 00259 MLD6_STATS_INC(mld6.rx_group); 00260 group = mld6_lookfor_group(inp, ip6_current_dest_addr()); 00261 if (group != NULL) { 00262 /* Schedule a report. */ 00263 mld6_delayed_report(group, mld_hdr->max_resp_delay); 00264 } 00265 } 00266 break; /* ICMP6_TYPE_MLQ */ 00267 case ICMP6_TYPE_MLR: /* Multicast listener report. */ 00268 /* Have we joined this group? 00269 * We use IP6 destination address to have a memory aligned copy. 00270 * mld_hdr->multicast_address should be the same. */ 00271 MLD6_STATS_INC(mld6.rx_report); 00272 group = mld6_lookfor_group(inp, ip6_current_dest_addr()); 00273 if (group != NULL) { 00274 /* If we are waiting to report, cancel it. */ 00275 if (group->group_state == MLD6_GROUP_DELAYING_MEMBER) { 00276 group->timer = 0; /* stopped */ 00277 group->group_state = MLD6_GROUP_IDLE_MEMBER; 00278 group->last_reporter_flag = 0; 00279 } 00280 } 00281 break; /* ICMP6_TYPE_MLR */ 00282 case ICMP6_TYPE_MLD: /* Multicast listener done. */ 00283 /* Do nothing, router will query us. */ 00284 break; /* ICMP6_TYPE_MLD */ 00285 default: 00286 MLD6_STATS_INC(mld6.proterr); 00287 MLD6_STATS_INC(mld6.drop); 00288 break; 00289 } 00290 00291 pbuf_free(p); 00292 } 00293 00294 /** 00295 * @ingroup mld6 00296 * Join a group on a network interface. 00297 * 00298 * @param srcaddr ipv6 address of the network interface which should 00299 * join a new group. If IP6_ADDR_ANY, join on all netifs 00300 * @param groupaddr the ipv6 address of the group to join 00301 * @return ERR_OK if group was joined on the netif(s), an err_t otherwise 00302 */ 00303 err_t 00304 mld6_joingroup(const ip6_addr_t *srcaddr, const ip6_addr_t *groupaddr) 00305 { 00306 err_t err = ERR_VAL; /* no matching interface */ 00307 struct netif *netif; 00308 00309 /* loop through netif's */ 00310 netif = netif_list; 00311 while (netif != NULL) { 00312 /* Should we join this interface ? */ 00313 if (ip6_addr_isany(srcaddr) || 00314 netif_get_ip6_addr_match(netif, srcaddr) >= 0) { 00315 err = mld6_joingroup_netif(netif, groupaddr); 00316 if (err != ERR_OK) { 00317 return err; 00318 } 00319 } 00320 00321 /* proceed to next network interface */ 00322 netif = netif->next; 00323 } 00324 00325 return err; 00326 } 00327 00328 /** 00329 * @ingroup mld6 00330 * Join a group on a network interface. 00331 * 00332 * @param netif the network interface which should join a new group. 00333 * @param groupaddr the ipv6 address of the group to join 00334 * @return ERR_OK if group was joined on the netif, an err_t otherwise 00335 */ 00336 err_t 00337 mld6_joingroup_netif(struct netif *netif, const ip6_addr_t *groupaddr) 00338 { 00339 struct mld_group *group; 00340 00341 /* find group or create a new one if not found */ 00342 group = mld6_lookfor_group(netif, groupaddr); 00343 00344 if (group == NULL) { 00345 /* Joining a new group. Create a new group entry. */ 00346 group = mld6_new_group(netif, groupaddr); 00347 if (group == NULL) { 00348 return ERR_MEM; 00349 } 00350 00351 /* Activate this address on the MAC layer. */ 00352 if (netif->mld_mac_filter != NULL) { 00353 netif->mld_mac_filter(netif, groupaddr, NETIF_ADD_MAC_FILTER); 00354 } 00355 00356 /* Report our membership. */ 00357 MLD6_STATS_INC(mld6.tx_report); 00358 mld6_send(netif, group, ICMP6_TYPE_MLR); 00359 mld6_delayed_report(group, MLD6_JOIN_DELAYING_MEMBER_TMR_MS); 00360 } 00361 00362 /* Increment group use */ 00363 group->use++; 00364 return ERR_OK; 00365 } 00366 00367 /** 00368 * @ingroup mld6 00369 * Leave a group on a network interface. 00370 * 00371 * @param srcaddr ipv6 address of the network interface which should 00372 * leave the group. If IP6_ISANY, leave on all netifs 00373 * @param groupaddr the ipv6 address of the group to leave 00374 * @return ERR_OK if group was left on the netif(s), an err_t otherwise 00375 */ 00376 err_t 00377 mld6_leavegroup(const ip6_addr_t *srcaddr, const ip6_addr_t *groupaddr) 00378 { 00379 err_t err = ERR_VAL; /* no matching interface */ 00380 struct netif *netif; 00381 00382 /* loop through netif's */ 00383 netif = netif_list; 00384 while (netif != NULL) { 00385 /* Should we leave this interface ? */ 00386 if (ip6_addr_isany(srcaddr) || 00387 netif_get_ip6_addr_match(netif, srcaddr) >= 0) { 00388 err_t res = mld6_leavegroup_netif(netif, groupaddr); 00389 if (err != ERR_OK) { 00390 /* Store this result if we have not yet gotten a success */ 00391 err = res; 00392 } 00393 } 00394 /* proceed to next network interface */ 00395 netif = netif->next; 00396 } 00397 00398 return err; 00399 } 00400 00401 /** 00402 * @ingroup mld6 00403 * Leave a group on a network interface. 00404 * 00405 * @param netif the network interface which should leave the group. 00406 * @param groupaddr the ipv6 address of the group to leave 00407 * @return ERR_OK if group was left on the netif, an err_t otherwise 00408 */ 00409 err_t 00410 mld6_leavegroup_netif(struct netif *netif, const ip6_addr_t *groupaddr) 00411 { 00412 struct mld_group *group; 00413 00414 /* find group */ 00415 group = mld6_lookfor_group(netif, groupaddr); 00416 00417 if (group != NULL) { 00418 /* Leave if there is no other use of the group */ 00419 if (group->use <= 1) { 00420 /* Remove the group from the list */ 00421 mld6_remove_group(netif, group); 00422 00423 /* If we are the last reporter for this group */ 00424 if (group->last_reporter_flag) { 00425 MLD6_STATS_INC(mld6.tx_leave); 00426 mld6_send(netif, group, ICMP6_TYPE_MLD); 00427 } 00428 00429 /* Disable the group at the MAC level */ 00430 if (netif->mld_mac_filter != NULL) { 00431 netif->mld_mac_filter(netif, groupaddr, NETIF_DEL_MAC_FILTER); 00432 } 00433 00434 /* free group struct */ 00435 memp_free(MEMP_MLD6_GROUP, group); 00436 } else { 00437 /* Decrement group use */ 00438 group->use--; 00439 } 00440 00441 /* Left group */ 00442 return ERR_OK; 00443 } 00444 00445 /* Group not found */ 00446 return ERR_VAL; 00447 } 00448 00449 00450 /** 00451 * Periodic timer for mld processing. Must be called every 00452 * MLD6_TMR_INTERVAL milliseconds (100). 00453 * 00454 * When a delaying member expires, a membership report is sent. 00455 */ 00456 void 00457 mld6_tmr(void) 00458 { 00459 struct netif *netif = netif_list; 00460 00461 while (netif != NULL) { 00462 struct mld_group *group = netif_mld6_data(netif); 00463 00464 while (group != NULL) { 00465 if (group->timer > 0) { 00466 group->timer--; 00467 if (group->timer == 0) { 00468 /* If the state is MLD6_GROUP_DELAYING_MEMBER then we send a report for this group */ 00469 if (group->group_state == MLD6_GROUP_DELAYING_MEMBER) { 00470 MLD6_STATS_INC(mld6.tx_report); 00471 mld6_send(netif, group, ICMP6_TYPE_MLR); 00472 group->group_state = MLD6_GROUP_IDLE_MEMBER; 00473 } 00474 } 00475 } 00476 group = group->next; 00477 } 00478 netif = netif->next; 00479 } 00480 } 00481 00482 /** 00483 * Schedule a delayed membership report for a group 00484 * 00485 * @param group the mld_group for which "delaying" membership report 00486 * should be sent 00487 * @param maxresp the max resp delay provided in the query 00488 */ 00489 static void 00490 mld6_delayed_report(struct mld_group *group, u16_t maxresp) 00491 { 00492 /* Convert maxresp from milliseconds to tmr ticks */ 00493 maxresp = maxresp / MLD6_TMR_INTERVAL; 00494 if (maxresp == 0) { 00495 maxresp = 1; 00496 } 00497 00498 #ifdef LWIP_RAND 00499 /* Randomize maxresp. (if LWIP_RAND is supported) */ 00500 maxresp = LWIP_RAND() % maxresp; 00501 if (maxresp == 0) { 00502 maxresp = 1; 00503 } 00504 #endif /* LWIP_RAND */ 00505 00506 /* Apply timer value if no report has been scheduled already. */ 00507 if ((group->group_state == MLD6_GROUP_IDLE_MEMBER) || 00508 ((group->group_state == MLD6_GROUP_DELAYING_MEMBER) && 00509 ((group->timer == 0) || (maxresp < group->timer)))) { 00510 group->timer = maxresp; 00511 group->group_state = MLD6_GROUP_DELAYING_MEMBER; 00512 } 00513 } 00514 00515 /** 00516 * Send a MLD message (report or done). 00517 * 00518 * An IPv6 hop-by-hop options header with a router alert option 00519 * is prepended. 00520 * 00521 * @param group the group to report or quit 00522 * @param type ICMP6_TYPE_MLR (report) or ICMP6_TYPE_MLD (done) 00523 */ 00524 static void 00525 mld6_send(struct netif *netif, struct mld_group *group, u8_t type) 00526 { 00527 struct mld_header *mld_hdr; 00528 struct pbuf *p; 00529 const ip6_addr_t *src_addr; 00530 00531 /* Allocate a packet. Size is MLD header + IPv6 Hop-by-hop options header. */ 00532 p = pbuf_alloc(PBUF_IP, sizeof(struct mld_header) + sizeof(struct ip6_hbh_hdr), PBUF_RAM); 00533 if (p == NULL) { 00534 MLD6_STATS_INC(mld6.memerr); 00535 return; 00536 } 00537 00538 /* Move to make room for Hop-by-hop options header. */ 00539 if (pbuf_header(p, -IP6_HBH_HLEN)) { 00540 pbuf_free(p); 00541 MLD6_STATS_INC(mld6.lenerr); 00542 return; 00543 } 00544 00545 /* Select our source address. */ 00546 if (!ip6_addr_isvalid(netif_ip6_addr_state(netif, 0))) { 00547 /* This is a special case, when we are performing duplicate address detection. 00548 * We must join the multicast group, but we don't have a valid address yet. */ 00549 src_addr = IP6_ADDR_ANY6; 00550 } else { 00551 /* Use link-local address as source address. */ 00552 src_addr = netif_ip6_addr(netif, 0); 00553 } 00554 00555 /* MLD message header pointer. */ 00556 mld_hdr = (struct mld_header *)p->payload; 00557 00558 /* Set fields. */ 00559 mld_hdr->type = type; 00560 mld_hdr->code = 0; 00561 mld_hdr->chksum = 0; 00562 mld_hdr->max_resp_delay = 0; 00563 mld_hdr->reserved = 0; 00564 ip6_addr_set(&(mld_hdr->multicast_address), &(group->group_address)); 00565 00566 #if CHECKSUM_GEN_ICMP6 00567 IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_ICMP6) { 00568 mld_hdr->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->len, 00569 src_addr, &(group->group_address)); 00570 } 00571 #endif /* CHECKSUM_GEN_ICMP6 */ 00572 00573 /* Add hop-by-hop headers options: router alert with MLD value. */ 00574 ip6_options_add_hbh_ra(p, IP6_NEXTH_ICMP6, IP6_ROUTER_ALERT_VALUE_MLD); 00575 00576 if (type == ICMP6_TYPE_MLR) { 00577 /* Remember we were the last to report */ 00578 group->last_reporter_flag = 1; 00579 } 00580 00581 /* Send the packet out. */ 00582 MLD6_STATS_INC(mld6.xmit); 00583 ip6_output_if(p, (ip6_addr_isany(src_addr)) ? NULL : src_addr, &(group->group_address), 00584 MLD6_HL, 0, IP6_NEXTH_HOPBYHOP, netif); 00585 pbuf_free(p); 00586 } 00587 00588 #endif /* LWIP_IPV6 */
Generated on Sun Jul 17 2022 08:25:24 by 1.7.2