ON Semiconductor / mbed-os

Dependents:   mbed-TFT-example-NCS36510 mbed-Accelerometer-example-NCS36510 mbed-Accelerometer-example-NCS36510

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers lwip_mld6.c Source File

lwip_mld6.c

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