Rtos API example

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  * 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 */