• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * @file
3  * Multicast listener discovery
4  *
5  * @defgroup mld6 MLD6
6  * @ingroup ip6
7  * Multicast listener discovery for IPv6. Aims to be compliant with RFC 2710.
8  * No support for MLDv2.\n
9  * Note: The allnodes (ff01::1, ff02::1) group is assumed be received by your
10  * netif since it must always be received for correct IPv6 operation (e.g. SLAAC).
11  * Ensure the netif filters are configured accordingly!\n
12  * The netif flags also need NETIF_FLAG_MLD6 flag set to enable MLD6 on a
13  * netif ("netif->flags |= NETIF_FLAG_MLD6;").\n
14  * To be called from TCPIP thread.
15  */
16 
17 /*
18  * Copyright (c) 2010 Inico Technologies Ltd.
19  * All rights reserved.
20  *
21  * Redistribution and use in source and binary forms, with or without modification,
22  * are permitted provided that the following conditions are met:
23  *
24  * 1. Redistributions of source code must retain the above copyright notice,
25  *    this list of conditions and the following disclaimer.
26  * 2. Redistributions in binary form must reproduce the above copyright notice,
27  *    this list of conditions and the following disclaimer in the documentation
28  *    and/or other materials provided with the distribution.
29  * 3. The name of the author may not be used to endorse or promote products
30  *    derived from this software without specific prior written permission.
31  *
32  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
33  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
34  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
35  * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
36  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
37  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
38  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
39  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
40  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
41  * OF SUCH DAMAGE.
42  *
43  * This file is part of the lwIP TCP/IP stack.
44  *
45  * Author: Ivan Delamer <delamer@inicotech.com>
46  *
47  *
48  * Please coordinate changes and requests with Ivan Delamer
49  * <delamer@inicotech.com>
50  */
51 
52 /* Based on igmp.c implementation of igmp v2 protocol */
53 
54 #include "lwip/opt.h"
55 
56 #if LWIP_IPV6 && (LWIP_IPV6_MLD || LWIP_IPV6_MLD_QUERIER)  /* don't build if not configured for use in lwipopts.h */
57 
58 #include "lwip/mld6.h"
59 #include "lwip/prot/mld6.h"
60 #include "lwip/icmp6.h"
61 #include "lwip/ip6.h"
62 #include "lwip/ip6_addr.h"
63 #include "lwip/ip.h"
64 #include "lwip/inet_chksum.h"
65 #include "lwip/pbuf.h"
66 #include "lwip/netif.h"
67 #include "lwip/memp.h"
68 #include "lwip/stats.h"
69 
70 #include <string.h>
71 
72 
73 /*
74  * MLD constants
75  */
76 #define MLD6_HL                           1
77 #define MLD6_JOIN_DELAYING_MEMBER_TMR_MS  (500)
78 
79 #define MLD6_GROUP_NON_MEMBER             0
80 #define MLD6_GROUP_DELAYING_MEMBER        1
81 #define MLD6_GROUP_IDLE_MEMBER            2
82 
83 /* Forward declarations. */
84 static struct mld_group *mld6_new_group(struct netif *ifp, const ip6_addr_t *addr);
85 static err_t mld6_remove_group(struct netif *netif, struct mld_group *group);
86 static void mld6_delayed_report(struct mld_group *group, u16_t maxresp);
87 static void mld6_send(struct netif *netif, struct mld_group *group, u8_t type);
88 
89 #if LWIP_IPV6_MLD_QUERIER
90 #define MLD6_TIMER_SET(TIMER, MS) do { \
91   (TIMER) = (u16_t)((MS) / (MLD6_TMR_INTERVAL)); \
92   if ((TIMER) == 0) { \
93     (TIMER) = 1; \
94   } \
95 } while (0)
96 
97 static void mld6_querier_tmr(void);
98 static void mld6_querier_input(struct mld_header *mld_hdr, struct netif *netif);
99 static void mld6_querier_listener_del(struct netif *netif, struct mld6_listener *listener);
100 #endif /* LWIP_IPV6_MLD_QUERIER */
101 
102 /**
103  * Stop MLD processing on interface
104  *
105  * @param netif network interface on which stop MLD processing
106  */
107 err_t
mld6_stop(struct netif * netif)108 mld6_stop(struct netif *netif)
109 {
110   struct mld_group *group = netif_mld6_data(netif);
111 
112   netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_MLD6, NULL);
113 
114   while (group != NULL) {
115     struct mld_group *next = group->next; /* avoid use-after-free below */
116 
117 #if LWIP_LINK_MCAST_FILTER
118     /* disable the group at the MAC level */
119     if (netif->mld_mac_filter != NULL) {
120       (void)netif->mld_mac_filter(netif, &(group->group_address), NETIF_DEL_MAC_FILTER);
121     }
122 #endif /* LWIP_LINK_MCAST_FILTER */
123 
124     /* free group */
125     memp_free(MEMP_MLD6_GROUP, group);
126 
127     /* move to "next" */
128     group = next;
129   }
130   return ERR_OK;
131 }
132 
133 /**
134  * Report MLD memberships for this interface
135  *
136  * @param netif network interface on which report MLD memberships
137  */
138 void
mld6_report_groups(struct netif * netif)139 mld6_report_groups(struct netif *netif)
140 {
141   struct mld_group *group = netif_mld6_data(netif);
142 
143   while (group != NULL) {
144     /*
145      * RFC 2710:MLD messages are never sent for multicast addresses whose scope is 0   (reserved) or 1 (node-local).
146      * MLD messages ARE sent for multicast addresses whose scope is 2  (link-local), including Solicited-Node
147      *   multicast addresses , except for the link-scope, all-nodes address (FF02::1)
148      */
149     if ((!(ip6_addr_ismulticast_iflocal(&(group->group_address)))) &&
150         (!(ip6_addr_isallnodes_linklocal(&(group->group_address)))) &&
151         (!(ip6_addr_ismulticast_reserved_scope(&(group->group_address))))) {
152       mld6_delayed_report(group, MLD6_JOIN_DELAYING_MEMBER_TMR_MS);
153     }
154     group = group->next;
155   }
156 }
157 
158 /**
159  * Search for a group that is joined on a netif
160  *
161  * @param ifp the network interface for which to look
162  * @param addr the group ipv6 address to search for
163  * @return a struct mld_group* if the group has been found,
164  *         NULL if the group wasn't found.
165  */
166 struct mld_group *
mld6_lookfor_group(struct netif * ifp,const ip6_addr_t * addr)167 mld6_lookfor_group(struct netif *ifp, const ip6_addr_t *addr)
168 {
169   struct mld_group *group = netif_mld6_data(ifp);
170 
171   while (group != NULL) {
172     if (ip6_addr_cmp(&(group->group_address), addr)) {
173       return group;
174     }
175     group = group->next;
176   }
177 
178   return NULL;
179 }
180 
181 
182 /**
183  * create a new group
184  *
185  * @param ifp the network interface for which to create
186  * @param addr the new group ipv6
187  * @return a struct mld_group*,
188  *         NULL on memory error.
189  */
190 static struct mld_group *
mld6_new_group(struct netif * ifp,const ip6_addr_t * addr)191 mld6_new_group(struct netif *ifp, const ip6_addr_t *addr)
192 {
193   struct mld_group *group;
194 
195   group = (struct mld_group *)memp_malloc(MEMP_MLD6_GROUP);
196   if (group != NULL) {
197     ip6_addr_set(&(group->group_address), addr);
198     group->timer              = 0; /* Not running */
199     group->group_state        = MLD6_GROUP_IDLE_MEMBER;
200     group->last_reporter_flag = 0;
201     group->use                = 0;
202     group->next               = netif_mld6_data(ifp);
203 
204     netif_set_client_data(ifp, LWIP_NETIF_CLIENT_DATA_INDEX_MLD6, group);
205   }
206 
207   return group;
208 }
209 
210 /**
211  * Remove a group from the mld_group_list, but do not free it yet
212  *
213  * @param group the group to remove
214  * @return ERR_OK if group was removed from the list, an err_t otherwise
215  */
216 static err_t
mld6_remove_group(struct netif * netif,struct mld_group * group)217 mld6_remove_group(struct netif *netif, struct mld_group *group)
218 {
219   err_t err = ERR_OK;
220 
221   /* Is it the first group? */
222   if (netif_mld6_data(netif) == group) {
223     netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_MLD6, group->next);
224   } else {
225     /* look for group further down the list */
226     struct mld_group *tmpGroup;
227     for (tmpGroup = netif_mld6_data(netif); tmpGroup != NULL; tmpGroup = tmpGroup->next) {
228       if (tmpGroup->next == group) {
229         tmpGroup->next = group->next;
230         break;
231       }
232     }
233     /* Group not find group */
234     if (tmpGroup == NULL) {
235       err = ERR_ARG;
236     }
237   }
238 
239   return err;
240 }
241 
242 
243 /**
244  * Process an input MLD message. Called by icmp6_input.
245  *
246  * @param p the mld packet, p->payload pointing to the icmpv6 header
247  * @param inp the netif on which this packet was received
248  */
249 void
mld6_input(struct pbuf * p,struct netif * inp)250 mld6_input(struct pbuf *p, struct netif *inp)
251 {
252   struct mld_header *mld_hdr;
253   struct mld_group *group;
254 
255   MLD6_STATS_INC(mld6.recv);
256 
257   /* Check that mld header fits in packet. */
258   if (p->len < sizeof(struct mld_header)) {
259     /* @todo debug message */
260     pbuf_free(p);
261     MLD6_STATS_INC(mld6.lenerr);
262     MLD6_STATS_INC(mld6.drop);
263     return;
264   }
265 
266   mld_hdr = (struct mld_header *)p->payload;
267 
268   /**
269       @page RFC-2710 RFC-2710
270       @par Compliant Section
271       Section 5.  Node State Transition Diagram
272       @par Behavior Description
273       MLD messages are never sent for multicast addresses whose scope is 0   (reserved) or 1 (node-local).
274       MLD messages ARE sent for multicast addresses whose scope is 2  (link-local), including Solicited-Node
275       multicast addresses , except for the link-scope, all-nodes address (FF02::1).
276       MLD Report will not be sent for  multicast addresses whose scope is 0   (reserved) or 1 (node-local)
277       and  link-scope, all-nodes address (FF02::1)  \n
278 
279       Send report for the address on the interface.
280       The Report message is sent to the address being reported.
281       Send done for the address on the interface.
282       The Done message is sent to the link-scope all-routers address (FF02::2).
283       Send Report to the multicast address which is being queried to
284       Send Done to the link-scope all-routers address (FF02::2) \n
285 
286       - Report received occurs when the node receives a valid MLD Report message.
287       A Report applies only to the address   identified in the Multicast Address field of the Report, on the
288       interface from which the Report is received.  It is ignored in the  Non-Listener or Idle Listener state.
289       All other events, such as receiving invalid MLD messages or MLD   message types other than Query or Report,
290       are ignored in all states.
291       Behavior:Stack will ignore the Report received from peer if the destination address is not same as the
292       multicast address. \n
293 
294       - Query received  occurs when the node receives either a valid General Query message
295       or a valid Multicast-Address-Specific Query message. To be valid, the Query message MUST
296       come from a link local IPv6 Source Address
297       Report received-occurs when the node receives a valid MLD Report message.
298       To be valid, the Report message MUST come from a link-local IPv6 Source Address
299       Behavior: Stack will validate the source address to be link local for Report and Query message\n
300       For Done message there is no stack validation for source address as Done message is handled by
301       Router. \n
302 
303       */
304 
305   /*  <Query received-  occurs when the node receives either a valid General Query message\n
306    *   or a valid Multicast-Address-Specific Query message. To be valid, the Query message MUST\n
307    *   come from a link local IPv6 Source Address\n
308    *   Report recieved-occurs when the node receives a valid MLD Report message.\n
309    *   To be valid, the Report message MUST come from a link-local IPv6 Source Address\n
310    *   Behavior: Stack will validate the source address to be link local for Report and Query message\n
311    *   For Done message there is no stack validation for source address as Done message is handled by\n
312    *   Router>
313    *
314    */
315   if (!ip6_addr_islinklocal(ip6_current_src_addr())) {
316     (void)pbuf_free(p);
317     MLD6_STATS_INC(mld6.proterr);
318     MLD6_STATS_INC(mld6.drop);
319     return;
320   }
321 
322 #if LWIP_IPV6_MLD_QUERIER
323   mld6_querier_input(mld_hdr, inp);
324 #endif /* LWIP_IPV6_MLD_QUERIER */
325 
326   switch (mld_hdr->type) {
327   case ICMP6_TYPE_MLQ: /* Multicast listener query. */
328     /* Is it a general query? */
329     if (ip6_addr_isallnodes_linklocal(ip6_current_dest_addr()) &&
330         ip6_addr_isany(&(mld_hdr->multicast_address))) {
331       MLD6_STATS_INC(mld6.rx_general);
332       /* Report all groups, except all nodes group, and if-local groups. */
333       group = netif_mld6_data(inp);
334       while (group != NULL) {
335         if ((!(ip6_addr_ismulticast_iflocal(&(group->group_address)))) &&
336             (!(ip6_addr_isallnodes_linklocal(&(group->group_address)))) &&
337             (!(ip6_addr_ismulticast_reserved_scope(&(group->group_address))))) {
338           mld6_delayed_report(group, lwip_ntohs(mld_hdr->max_resp_delay));
339         }
340         group = group->next;
341       }
342     } else {
343       /* Have we joined this group?
344        * We use IP6 destination address to have a memory aligned copy.
345        * mld_hdr->multicast_address should be the same. */
346       /**
347           @page RFC-2710 RFC-2710
348           @par Compliant Section
349           Section 6.  Router State Transition Diagram
350           @par Behavior Description
351           Send multicast-address-specific query" (Router) for the address on the  link.
352           The Multicast-Address-Specific Query is sent to the address being queried
353           and has a Maximum Response Delay of [Last Listener Query Interval].\n
354           Behavior: Stack will ignore the Multicast specific query from peer if the Query destination address
355           is not the multicast address which is being queried>
356           */
357       /**
358           @page RFC-2710 RFC-2710
359           @par Compliant Section
360           Section 8.  Message Destinations
361           @par Behavior Description
362           @verbatim
363           Message Type                                     IPv6 Destination Address
364           ------------                                     ------------------------
365           General Query                                    link-scope all-nodes (FF02::1)
366           Multicast-Address-Specific Query                 the multicast address being queried
367           Report                                           the multicast address being reported
368           Done                                             link-scope all-routers (FF02::2)
369           @endverbatim
370           Behavior: When stack receives Query (General or Specific) stack will validate the destination address
371           to be same as the multicast address, if not the message will be ignored.
372           When sending out the Report or Done , the destination address is verified and send to the
373           multicast address and link-scope all-routers respectively
374           */
375       if (ip6_addr_cmp_zoneless(ip6_current_dest_addr(), &(mld_hdr->multicast_address))) {
376         MLD6_STATS_INC(mld6.rx_group);
377         group = mld6_lookfor_group(inp, ip6_current_dest_addr());
378         if (group != NULL) {
379           /*
380            * MLD messages are never sent for multicast addresses whose scope is 0 (reserved) or
381            * 1 (node-local).
382            * MLD report is not sent for the link-scope, all-nodes address (FF02::1).
383            */
384           if ((!(ip6_addr_ismulticast_iflocal(&(group->group_address)))) &&
385               (!(ip6_addr_isallnodes_linklocal(&(group->group_address)))) &&
386               (!(ip6_addr_ismulticast_reserved_scope(&(group->group_address))))) {
387             /* Schedule a report. */
388             mld6_delayed_report(group, lwip_ntohs(mld_hdr->max_resp_delay));
389           }
390         }
391       }
392     }
393     break; /* ICMP6_TYPE_MLQ */
394   case ICMP6_TYPE_MLR: /* Multicast listener report. */
395     /* Have we joined this group?
396      * We use IP6 destination address to have a memory aligned copy.
397      * mld_hdr->multicast_address should be the same. */
398     if (ip6_addr_cmp_zoneless(ip6_current_dest_addr(), &(mld_hdr->multicast_address))) {
399       MLD6_STATS_INC(mld6.rx_report);
400       group = mld6_lookfor_group(inp, ip6_current_dest_addr());
401       if (group != NULL) {
402         /* If we are waiting to report, cancel it. */
403         if (group->group_state == MLD6_GROUP_DELAYING_MEMBER) {
404           group->timer = 0; /* stopped */
405           group->group_state = MLD6_GROUP_IDLE_MEMBER;
406           group->last_reporter_flag = 0;
407         }
408       }
409     }
410     break; /* ICMP6_TYPE_MLR */
411   case ICMP6_TYPE_MLD: /* Multicast listener done. */
412     /* Do nothing, router will query us. */
413     break; /* ICMP6_TYPE_MLD */
414   default:
415     MLD6_STATS_INC(mld6.proterr);
416     MLD6_STATS_INC(mld6.drop);
417     break;
418   }
419 
420   pbuf_free(p);
421 }
422 
423 /**
424  * @ingroup mld6
425  * Join a group on one or all network interfaces.
426  *
427  * If the group is to be joined on all interfaces, the given group address must
428  * not have a zone set (i.e., it must have its zone index set to IP6_NO_ZONE).
429  * If the group is to be joined on one particular interface, the given group
430  * address may or may not have a zone set.
431  *
432  * @param srcaddr ipv6 address (zoned) of the network interface which should
433  *                join a new group. If IP6_ADDR_ANY6, join on all netifs
434  * @param groupaddr the ipv6 address of the group to join (possibly but not
435  *                  necessarily zoned)
436  * @return ERR_OK if group was joined on the netif(s), an err_t otherwise
437  */
438 err_t
mld6_joingroup(const ip6_addr_t * srcaddr,const ip6_addr_t * groupaddr)439 mld6_joingroup(const ip6_addr_t *srcaddr, const ip6_addr_t *groupaddr)
440 {
441   err_t         err = ERR_VAL; /* no matching interface */
442   struct netif *netif;
443 
444   LWIP_ASSERT_CORE_LOCKED();
445 
446   /* loop through netif's */
447   NETIF_FOREACH(netif) {
448     /* Should we join this interface ? */
449     if (ip6_addr_isany(srcaddr) ||
450         netif_get_ip6_addr_match(netif, srcaddr) >= 0) {
451       err = mld6_joingroup_netif(netif, groupaddr);
452       if (err != ERR_OK) {
453         return err;
454       }
455     }
456   }
457 
458   return err;
459 }
460 
461 /**
462  * @ingroup mld6
463  * Join a group on a network interface.
464  *
465  * @param netif the network interface which should join a new group.
466  * @param groupaddr the ipv6 address of the group to join (possibly but not
467  *                  necessarily zoned)
468  * @return ERR_OK if group was joined on the netif, an err_t otherwise
469  */
470 err_t
mld6_join_staticgroup_netif(struct netif * netif,const ip6_addr_t * groupaddr)471 mld6_join_staticgroup_netif(struct netif *netif, const ip6_addr_t *groupaddr)
472 {
473   struct mld_group *group = NULL;
474 
475   /* Just find group */
476   group = mld6_lookfor_group(netif, groupaddr);
477 
478   if (group == NULL) {
479     /* Joining a new group. Create a new group entry. */
480     group = mld6_new_group(netif, groupaddr);
481     if (group == NULL) {
482       return ERR_MEM;
483     }
484 
485 #if LWIP_LINK_MCAST_FILTER
486     /* Activate this address on the MAC layer. */
487     if (netif->mld_mac_filter != NULL) {
488       (void)netif->mld_mac_filter(netif, groupaddr, NETIF_ADD_MAC_FILTER);
489     }
490 #endif /* LWIP_LINK_MCAST_FILTER */
491   }
492 
493   /* Increment group use */
494   group->use++;
495   return ERR_OK;
496 }
497 
498 /**
499  * @ingroup mld6
500  * Join a group on a network interface.
501  *
502  * @param netif the network interface which should join a new group.
503  * @param groupaddr the ipv6 address of the group to join (possibly but not
504  *                  necessarily zoned)
505  * @return ERR_OK if group was joined on the netif, an err_t otherwise
506  */
507 err_t
mld6_joingroup_netif(struct netif * netif,const ip6_addr_t * groupaddr)508 mld6_joingroup_netif(struct netif *netif, const ip6_addr_t *groupaddr)
509 {
510   struct mld_group *group;
511 #if LWIP_IPV6_SCOPES
512   ip6_addr_t ip6addr;
513 
514   /* If the address has a particular scope but no zone set, use the netif to
515    * set one now. Within the mld6 module, all addresses are properly zoned. */
516   if (ip6_addr_lacks_zone(groupaddr, IP6_MULTICAST)) {
517     ip6_addr_set(&ip6addr, groupaddr);
518     ip6_addr_assign_zone(&ip6addr, IP6_MULTICAST, netif);
519     groupaddr = &ip6addr;
520   }
521   IP6_ADDR_ZONECHECK_NETIF(groupaddr, netif);
522 #endif /* LWIP_IPV6_SCOPES */
523 
524   LWIP_ASSERT_CORE_LOCKED();
525 
526   /* find group or create a new one if not found */
527   group = mld6_lookfor_group(netif, groupaddr);
528 
529   if (group == NULL) {
530     /* Joining a new group. Create a new group entry. */
531     group = mld6_new_group(netif, groupaddr);
532     if (group == NULL) {
533       return ERR_MEM;
534     }
535 
536 #if LWIP_LINK_MCAST_FILTER
537     /* Activate this address on the MAC layer. */
538     if (netif->mld_mac_filter != NULL) {
539       (void)netif->mld_mac_filter(netif, groupaddr, NETIF_ADD_MAC_FILTER);
540     }
541 #endif /* LWIP_LINK_MCAST_FILTER */
542     /*
543      * RFC 2710:MLD messages are never sent for multicast addresses whose scope is 0   (reserved) or 1 (node-local).
544      * MLD messages ARE sent for multicast addresses whose scope is 2  (link-local), including Solicited-Node
545      * multicast addresses , except for the link-scope, all-nodes address (FF02::1)
546      */
547     if ((!(ip6_addr_ismulticast_iflocal(&(group->group_address)))) &&
548         (!(ip6_addr_isallnodes_linklocal(&(group->group_address)))) &&
549         (!(ip6_addr_ismulticast_reserved_scope(&(group->group_address))))) {
550       /* Report our membership. */
551       MLD6_STATS_INC(mld6.tx_report);
552       mld6_send(netif, group, ICMP6_TYPE_MLR);
553       mld6_delayed_report(group, MLD6_JOIN_DELAYING_MEMBER_TMR_MS);
554 #if LWIP_MPL
555       LWIP_CONF_MLD6_JOIN_GRP(groupaddr);
556 #endif
557     }
558   }
559 
560   /* Increment group use */
561   group->use++;
562   return ERR_OK;
563 }
564 
565 /**
566  * @ingroup mld6
567  * Leave a group on a network interface.
568  *
569  * Zoning of address follows the same rules as @ref mld6_joingroup.
570  *
571  * @param srcaddr ipv6 address (zoned) of the network interface which should
572  *                leave the group. If IP6_ADDR_ANY6, leave on all netifs
573  * @param groupaddr the ipv6 address of the group to leave (possibly, but not
574  *                  necessarily zoned)
575  * @return ERR_OK if group was left on the netif(s), an err_t otherwise
576  */
577 err_t
mld6_leavegroup(const ip6_addr_t * srcaddr,const ip6_addr_t * groupaddr)578 mld6_leavegroup(const ip6_addr_t *srcaddr, const ip6_addr_t *groupaddr)
579 {
580   err_t         err = ERR_VAL; /* no matching interface */
581   struct netif *netif;
582 
583   LWIP_ASSERT_CORE_LOCKED();
584 
585   /* loop through netif's */
586   NETIF_FOREACH(netif) {
587     /* Should we leave this interface ? */
588     if (ip6_addr_isany(srcaddr) ||
589         netif_get_ip6_addr_match(netif, srcaddr) >= 0) {
590       err_t res = mld6_leavegroup_netif(netif, groupaddr);
591       if (err != ERR_OK) {
592         /* Store this result if we have not yet gotten a success */
593         err = res;
594       }
595     }
596   }
597 
598   return err;
599 }
600 
601 /**
602  * @ingroup mld6
603  * Leave a group on a network interface.
604  *
605  * @param netif the network interface which should leave the group.
606  * @param groupaddr the ipv6 address of the group to leave (possibly, but not
607  *                  necessarily zoned)
608  * @return ERR_OK if group was left on the netif, an err_t otherwise
609  */
610 err_t
mld6_leavegroup_netif(struct netif * netif,const ip6_addr_t * groupaddr)611 mld6_leavegroup_netif(struct netif *netif, const ip6_addr_t *groupaddr)
612 {
613   struct mld_group *group;
614   u8_t ulast_reporter;
615 #if LWIP_IPV6_SCOPES
616   ip6_addr_t ip6addr;
617 
618   if (ip6_addr_lacks_zone(groupaddr, IP6_MULTICAST)) {
619     ip6_addr_set(&ip6addr, groupaddr);
620     ip6_addr_assign_zone(&ip6addr, IP6_MULTICAST, netif);
621     groupaddr = &ip6addr;
622   }
623   IP6_ADDR_ZONECHECK_NETIF(groupaddr, netif);
624 #endif /* LWIP_IPV6_SCOPES */
625 
626   LWIP_ASSERT_CORE_LOCKED();
627 
628   /* find group */
629   group = mld6_lookfor_group(netif, groupaddr);
630 
631   if (group != NULL) {
632     /* Leave if there is no other use of the group */
633     if (group->use <= 1) {
634       /* Remove the group from the list */
635       mld6_remove_group(netif, group);
636 
637       /* If we are the last reporter for this group */
638       /**
639       @page RFC-2710 RFC-2710
640 
641       @par Compliant Section
642       Section 4.  Protocol Description
643       @par Behavior Description
644       If the node's most   recent Report message was suppressed by hearing another Report
645       message, it MAY send nothing, as it is highly likely that there is   another listener for that
646       address still present on the same link.  If   this optimization is implemented, it MUST be able
647       to be turned off but SHOULD default to on.\n
648       Behavior: Stack will not send Done if the recent Report message was suppressed by hearing another Report
649       message, as it is highly likely that there is   another listener. This option is configurable, by default ON.
650       */
651 #if LWIP_MLD6_DONE_ONLYFOR_LAST_REPORTER
652       ulast_reporter =  group->last_reporter_flag;
653 #else
654       /*
655        * If group is not the last reporter
656        * Done message will not be sent. By disabling the configuration, stack will send Done irrespective of the
657        * last reporter flag
658        */
659       ulast_reporter = 1;
660 #endif
661 
662       if (ulast_reporter) {
663         /*
664          * MLD messages (host side )are never sent for multicast addresses whose scope is 0 (reserved) or
665          * 1 (node-local).
666          *  MLD messages is not sent for the link-scope, all-nodes address (FF02::1)
667          */
668         if ((!(ip6_addr_ismulticast_iflocal(&(group->group_address)))) &&
669             (!(ip6_addr_isallnodes_linklocal(&(group->group_address)))) &&
670             (!(ip6_addr_ismulticast_reserved_scope(&(group->group_address))))) {
671           MLD6_STATS_INC(mld6.tx_leave);
672           mld6_send(netif, group, ICMP6_TYPE_MLD);
673         }
674       }
675 
676 #if LWIP_LINK_MCAST_FILTER
677       /* Disable the group at the MAC level */
678       if (netif->mld_mac_filter != NULL) {
679         (void)netif->mld_mac_filter(netif, groupaddr, NETIF_DEL_MAC_FILTER);
680       }
681 #endif /* LWIP_LINK_MCAST_FILTER */
682 
683       /* free group struct */
684       memp_free(MEMP_MLD6_GROUP, group);
685     } else {
686       /* Decrement group use */
687       group->use--;
688     }
689 
690     /* Left group */
691     return ERR_OK;
692   }
693 
694   /* Group not found */
695   return ERR_VAL;
696 }
697 
698 
699 /**
700  * Periodic timer for mld processing. Must be called every
701  * MLD6_TMR_INTERVAL milliseconds (100).
702  *
703  * When a delaying member expires, a membership report is sent.
704  */
705 void
mld6_tmr(void)706 mld6_tmr(void)
707 {
708   struct netif *netif;
709 
710   NETIF_FOREACH(netif) {
711     struct mld_group *group = netif_mld6_data(netif);
712 
713     while (group != NULL) {
714       if (group->timer > 0) {
715         group->timer--;
716         if (group->timer == 0) {
717           /* If the state is MLD6_GROUP_DELAYING_MEMBER then we send a report for this group */
718           if (group->group_state == MLD6_GROUP_DELAYING_MEMBER) {
719             MLD6_STATS_INC(mld6.tx_report);
720             mld6_send(netif, group, ICMP6_TYPE_MLR);
721             group->group_state = MLD6_GROUP_IDLE_MEMBER;
722           }
723         }
724       }
725       group = group->next;
726     }
727   }
728 #if LWIP_IPV6_MLD_QUERIER
729   mld6_querier_tmr();
730 #endif /* LWIP_IPV6_MLD_QUERIER */
731 }
732 
733 #if LWIP_LOWPOWER
734 #include "lwip/lowpower.h"
735 
736 #if LWIP_IPV6_MLD_QUERIER
737 static u32_t
get_mld6_querier_tmr_listener(struct mld6_listener * listener)738 get_mld6_querier_tmr_listener(struct mld6_listener *listener)
739 {
740   u32_t tick = 0;
741   u32_t val;
742 
743   if (listener->timer < 1) {
744     return 0;
745   }
746   val = listener->timer;
747   SET_TMR_TICK(tick, val);
748 
749   /* here timer is greater than 0 */
750   if (listener->state != MLD6_LISTENER_STATE_CHECK) {
751     return tick;
752   }
753 
754   /* here state is MLD6_LISTENER_STATE_CHECK */
755   if (listener->rexmt_timer > 0) {
756     SET_TMR_TICK(tick, listener->rexmt_timer);
757     return tick;
758   }
759 
760   if (listener->query_count < MLD6_LAST_LISTENER_QUERY_COUNT) {
761     val = (u32_t)MLD6_LAST_LISTENER_QUERY_COUNT - listener->query_count;
762     SET_TMR_TICK(tick, val);
763   }
764   return tick;
765 }
766 #endif /* #if LWIP_IPV6_MLD_QUERIER */
767 
768 u32_t
mld6_tmr_tick(void)769 mld6_tmr_tick(void)
770 {
771   struct netif *netif = netif_list;
772   struct mld6_listener *listener = NULL;
773   u32_t val = 0;
774   u32_t tick = 0;
775 
776   while (netif != NULL) {
777     struct mld_group *group = netif_mld6_data(netif);
778     while (group != NULL) {
779       if (group->timer > 0) {
780         SET_TMR_TICK(tick, group->timer);
781       }
782       group = group->next;
783     }
784 
785 #if LWIP_IPV6_MLD_QUERIER
786     struct mld6_querier *querier = netif_mld6_querier_data(netif);
787     if (querier == NULL) {
788       netif = netif->next;
789       continue;
790     }
791     if (querier->timer > 0) {
792       SET_TMR_TICK(tick, querier->timer);
793     }
794 
795     listener = querier->listeners;
796     while (listener != NULL) {
797       val = get_mld6_querier_tmr_listener(listener);
798       SET_TMR_TICK(tick, val);
799       listener = listener->next;
800     }
801 #endif /* LWIP_IPV6_MLD_QUERIER */
802     netif = netif->next;
803   }
804 
805   LOWPOWER_DEBUG(("%s tmr tick: %u\n", __func__, tick));
806   return tick;
807 }
808 #endif
809 
810 /**
811  * Schedule a delayed membership report for a group
812  *
813  * @param group the mld_group for which "delaying" membership report
814  *              should be sent
815  * @param maxresp_in the max resp delay provided in the query
816  */
817 static void
mld6_delayed_report(struct mld_group * group,u16_t maxresp_in)818 mld6_delayed_report(struct mld_group *group, u16_t maxresp_in)
819 {
820   /* Convert maxresp from milliseconds to tmr ticks */
821   u16_t maxresp = maxresp_in / MLD6_TMR_INTERVAL;
822   if (maxresp == 0) {
823     maxresp = 1;
824   }
825 
826 #ifdef LWIP_RAND
827   /* Randomize maxresp. (if LWIP_RAND is supported) */
828   maxresp = (u16_t)(LWIP_RAND() % maxresp);
829   if (maxresp == 0) {
830     maxresp = 1;
831   }
832 #endif /* LWIP_RAND */
833 
834   /* Apply timer value if no report has been scheduled already. */
835   if ((group->group_state == MLD6_GROUP_IDLE_MEMBER) ||
836      ((group->group_state == MLD6_GROUP_DELAYING_MEMBER) &&
837       ((group->timer == 0) || (maxresp < group->timer)))) {
838     group->timer = maxresp;
839     group->group_state = MLD6_GROUP_DELAYING_MEMBER;
840   }
841 }
842 
843 /**
844  * Send a MLD message (report or done).
845  *
846  * An IPv6 hop-by-hop options header with a router alert option
847  * is prepended.
848  *
849  * @param group the group to report or quit
850  * @param type ICMP6_TYPE_MLR (report) or ICMP6_TYPE_MLD (done)
851  */
852 static void
mld6_send(struct netif * netif,struct mld_group * group,u8_t type)853 mld6_send(struct netif *netif, struct mld_group *group, u8_t type)
854 {
855   struct mld_header *mld_hdr;
856   struct pbuf *p;
857   const ip6_addr_t *src_addr;
858   ip6_addr_t dst_addr;
859 
860   /* Allocate a packet. Size is MLD header + IPv6 Hop-by-hop options header. */
861   p = pbuf_alloc(PBUF_IP, sizeof(struct mld_header) + MLD6_HBH_HLEN, PBUF_RAM);
862   if (p == NULL) {
863     MLD6_STATS_INC(mld6.memerr);
864     return;
865   }
866 
867   /* Move to make room for Hop-by-hop options header. */
868   if (pbuf_remove_header(p, MLD6_HBH_HLEN)) {
869     pbuf_free(p);
870     MLD6_STATS_INC(mld6.lenerr);
871     return;
872   }
873 
874   /* Select our source address. */
875   if (!ip6_addr_isvalid(netif_ip6_addr_state(netif, 0))) {
876     /* This is a special case, when we are performing duplicate address detection.
877      * We must join the multicast group, but we don't have a valid address yet. */
878     src_addr = IP6_ADDR_ANY6;
879   } else {
880     /* Use link-local address as source address. */
881     src_addr = netif_ip6_addr(netif, 0);
882   }
883 
884   /* MLD message header pointer. */
885   mld_hdr = (struct mld_header *)p->payload;
886 
887   /* Set fields. */
888   mld_hdr->type = type;
889   mld_hdr->code = 0;
890   mld_hdr->chksum = 0;
891   mld_hdr->max_resp_delay = 0;
892   mld_hdr->reserved = 0;
893   ip6_addr_copy_to_packed(mld_hdr->multicast_address, group->group_address);
894 
895   /*
896    * "send report" for the address on the interface.
897    * The Report message is sent to the address being reported.
898    * - "send done" for the address on the interface.
899    * The Done message is sent to the link-scope all-routers address (FF02::2).
900    * Behavior:Send Report to the multicast address which is being queried to
901    * Send Done to the link-scope all-routers address (FF02::2)
902    */
903   if (type == ICMP6_TYPE_MLD) {
904     /* Generate the all routers target address to send the done message . */
905     ip6_addr_set_allrouters_linklocal(&dst_addr);
906   } else {
907     ip6_addr_copy(dst_addr, group->group_address);
908   }
909 
910 #if CHECKSUM_GEN_ICMP6
911   IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_ICMP6) {
912     mld_hdr->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->len,
913                                         src_addr, &dst_addr);
914   }
915 #endif /* CHECKSUM_GEN_ICMP6 */
916 
917   /* Add hop-by-hop headers options: router alert with MLD value. */
918   ip6_options_add_hbh_ra(p, IP6_NEXTH_ICMP6, IP6_ROUTER_ALERT_VALUE_MLD);
919 
920   if (type == ICMP6_TYPE_MLR) {
921     /* Remember we were the last to report */
922     group->last_reporter_flag = 1;
923   }
924 
925   /* Send the packet out. */
926   MLD6_STATS_INC(mld6.xmit);
927   ip6_output_if(p, (ip6_addr_isany(src_addr)) ? NULL : src_addr, &dst_addr,
928       MLD6_HL, 0, IP6_NEXTH_HOPBYHOP, netif);
929   pbuf_free(p);
930 }
931 
932 #if LWIP_IPV6_MLD_QUERIER
933 static void
mld6_query_send(struct netif * netif,ip6_addr_t * group_address,u16_t max_resp_delay)934 mld6_query_send(struct netif *netif, ip6_addr_t *group_address, u16_t max_resp_delay)
935 {
936   struct mld_header *mld_hdr = NULL;
937   struct pbuf *p = NULL;
938   const ip6_addr_t *src_addr = NULL;
939   ip6_addr_t dst_addr = {0};
940 
941   /* check link-local address */
942   if (!ip6_addr_isvalid(netif_ip6_addr_state(netif, 0))) {
943     LWIP_DEBUGF(MLD6_DEBUG | LWIP_DBG_TRACE, ("mld6_query_send: link-local address not ready\n"));
944     return;
945   }
946   src_addr = netif_ip6_addr(netif, 0);
947 
948   /* Allocate a packet. Size is MLD header + IPv6 Hop-by-hop options header. */
949   p = pbuf_alloc(PBUF_IP, sizeof(struct mld_header) + sizeof(struct ip6_hbh_hdr), PBUF_RAM);
950   if (p == NULL) {
951     MLD6_STATS_INC(mld6.memerr);
952     return;
953   }
954 
955   /* Move to make room for Hop-by-hop options header. */
956   if (pbuf_header(p, -IP6_HBH_HLEN)) {
957     (void)pbuf_free(p);
958     MLD6_STATS_INC(mld6.lenerr);
959     return;
960   }
961 
962   /* MLD message header pointer. */
963   mld_hdr = (struct mld_header *)p->payload;
964 
965   /* Set fields. */
966   mld_hdr->type = ICMP6_TYPE_MLQ;
967   mld_hdr->code = 0;
968   mld_hdr->chksum = 0;
969   mld_hdr->max_resp_delay = lwip_htons(max_resp_delay);
970   mld_hdr->reserved = 0;
971   if (group_address != NULL) {
972     ip6_addr_copy_to_packed(mld_hdr->multicast_address, *group_address);
973     ip6_addr_copy_to_packed(dst_addr, *group_address);
974   } else {
975     ip6_addr_copy_to_packed(mld_hdr->multicast_address, *IP6_ADDR_ANY6);
976     ip6_addr_set_allnodes_linklocal(&dst_addr);
977   }
978 
979 #if CHECKSUM_GEN_ICMP6
980   IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_ICMP6) {
981     mld_hdr->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->len,
982                                         src_addr, &dst_addr);
983   }
984 #endif /* CHECKSUM_GEN_ICMP6 */
985 
986   /* Add hop-by-hop headers options: router alert with MLD value. */
987   (void)ip6_options_add_hbh_ra(p, IP6_NEXTH_ICMP6, IP6_ROUTER_ALERT_VALUE_MLD);
988 
989   /* Send the packet out. */
990   MLD6_STATS_INC(mld6.xmit);
991   (void)ip6_output_if(p, src_addr, &dst_addr, MLD6_HL, 0, IP6_NEXTH_HOPBYHOP, netif);
992   (void)pbuf_free(p);
993 
994   return;
995 }
996 
997 static struct mld6_querier *
mld6_querier_get_struct(struct netif * netif)998 mld6_querier_get_struct(struct netif *netif)
999 {
1000   struct mld6_querier *querier = netif_mld6_querier_data(netif);
1001 
1002   if (querier != NULL) {
1003     LWIP_DEBUGF(MLD6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
1004                 ("mld6_querier_get_struct: using existing mld6 querier\n"));
1005     return querier;
1006   }
1007 
1008   querier = (struct mld6_querier *)mem_malloc(sizeof(struct mld6_querier));
1009   if (querier == NULL) {
1010     DHCP6_STATS_INC(mld6.memerr);
1011     LWIP_DEBUGF(MLD6_DEBUG | LWIP_DBG_TRACE, ("mld6_querier_get_struct: could not allocate mld6 querier\n"));
1012     return NULL;
1013   }
1014 
1015   (void)memset_s(querier, sizeof(struct mld6_querier), 0, sizeof(struct mld6_querier));
1016 
1017   netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_MLD6_QUERIER, querier);
1018 
1019   querier->querier_state = MLD6_QUERIER_STATE_STARTUP;
1020   MLD6_TIMER_SET(querier->timer, ((MLD6_STARTUP_QUERY_INTERVAL) * MLD6_MS_PER_SECOND));
1021   mld6_query_send(netif, NULL, MLD6_QUERY_RESPONSE_INTERVAL);
1022   querier->query_count = 0;
1023 
1024   return querier;
1025 }
1026 
1027 err_t
mld6_querier_start(struct netif * netif)1028 mld6_querier_start(struct netif *netif)
1029 {
1030   struct mld6_querier *querier = NULL;
1031 
1032   LWIP_ERROR("mld6_querier_start: nul netif", (netif != NULL), return ERR_ARG);
1033 
1034   LWIP_DEBUGF(MLD6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("mld6_querier_start(netif=%p) %s%"U16_F"\n", (void *)netif,
1035                                                              netif->name, (u16_t)netif->num));
1036 
1037   /* check link-local address */
1038   if (!ip6_addr_isvalid(netif_ip6_addr_state(netif, 0))) {
1039     LWIP_DEBUGF(MLD6_DEBUG | LWIP_DBG_TRACE, ("mld6_querier_start: link-local address not ready\n"));
1040     return ERR_VAL;
1041   }
1042 
1043   querier = mld6_querier_get_struct(netif);
1044   if (querier == NULL) {
1045     LWIP_DEBUGF(MLD6_DEBUG | LWIP_DBG_TRACE, ("mld6_querier_start(): get querier struct failed\n"));
1046     return ERR_MEM;
1047   }
1048 
1049   LWIP_DEBUGF(MLD6_DEBUG | LWIP_DBG_TRACE, ("mld6_querier_start(): querier start success\n"));
1050   return ERR_OK;
1051 }
1052 
1053 static void
mld6_querier_listeners_free(struct mld6_querier * querier)1054 mld6_querier_listeners_free(struct mld6_querier *querier)
1055 {
1056   struct mld6_listener *listener = querier->listeners;
1057   struct mld6_listener *listener_next = NULL;
1058 
1059   while (listener != NULL) {
1060     listener_next = listener->next;
1061     mem_free(listener);
1062     listener = listener_next;
1063   }
1064 
1065   querier->listeners = NULL;
1066   querier->listener_num = 0;
1067 
1068   return;
1069 }
1070 
1071 void
mld6_querier_stop(struct netif * netif)1072 mld6_querier_stop(struct netif *netif)
1073 {
1074   struct mld6_querier *querier = NULL;
1075 
1076   LWIP_ERROR("mld6_querier_stop: nul netif", (netif != NULL), return);
1077 
1078   LWIP_DEBUGF(MLD6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("mld6_querier_stop(netif=%p) %s%"U16_F"\n", (void *)netif,
1079                                                              netif->name, (u16_t)netif->num));
1080 
1081   querier = netif_mld6_querier_data(netif);
1082   if (querier == NULL) {
1083     LWIP_DEBUGF(MLD6_DEBUG | LWIP_DBG_TRACE, ("mld6_querier_stop(): querier not start\n"));
1084     return;
1085   }
1086 
1087   netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_MLD6_QUERIER, NULL);
1088   mld6_querier_listeners_free(querier);
1089   mem_free(querier);
1090   return;
1091 }
1092 
1093 static void
mld6_querier_startup_done(struct mld6_querier * querier)1094 mld6_querier_startup_done(struct mld6_querier *querier)
1095 {
1096   if (querier->listeners == NULL) {
1097     querier->querier_state = MLD6_QUERIER_STATE_NON_LISTENER;
1098     querier->query_count = 0;
1099     querier->timer = 0;
1100   } else {
1101     querier->querier_state = MLD6_QUERIER_STATE_HAVE_LISTENER;
1102     querier->query_count = 0;
1103     MLD6_TIMER_SET(querier->timer, ((MLD6_QUERY_INTERVAL) * MLD6_MS_PER_SECOND));
1104   }
1105 
1106   return;
1107 }
1108 
1109 static void
mld6_querier_tmr_query(struct netif * netif,struct mld6_querier * querier)1110 mld6_querier_tmr_query(struct netif *netif, struct mld6_querier *querier)
1111 {
1112   if (querier->timer == 0) {
1113     return;
1114   }
1115 
1116   querier->timer--;
1117   if (querier->timer > 0) {
1118     return;
1119   }
1120 
1121   /* here timer is 0, send General Query */
1122   if (querier->querier_state == MLD6_QUERIER_STATE_STARTUP) {
1123     querier->query_count++;
1124     if (querier->query_count >= MLD6_STARTUP_QUERY_COUNT) {
1125       mld6_querier_startup_done(querier);
1126     } else {
1127       mld6_query_send(netif, NULL, MLD6_QUERY_RESPONSE_INTERVAL);
1128       MLD6_TIMER_SET(querier->timer, ((MLD6_STARTUP_QUERY_INTERVAL) * MLD6_MS_PER_SECOND));
1129     }
1130   } else if (querier->querier_state == MLD6_QUERIER_STATE_HAVE_LISTENER) {
1131     mld6_query_send(netif, NULL, MLD6_QUERY_RESPONSE_INTERVAL);
1132     MLD6_TIMER_SET(querier->timer, ((MLD6_QUERY_INTERVAL) * MLD6_MS_PER_SECOND));
1133   }
1134 
1135   return;
1136 }
1137 
1138 static void
mld6_querier_tmr_listener(struct netif * netif,struct mld6_listener * listener)1139 mld6_querier_tmr_listener(struct netif *netif, struct mld6_listener *listener)
1140 {
1141 #ifdef LWIP_DEBUG
1142   char buf[IP6ADDR_STRLEN_MAX];
1143 #endif
1144   if (listener->timer <= 1) {
1145 #ifdef LWIP_DEBUG
1146     (void)ip6addr_ntoa_r(&(listener->group_address), buf, IP6ADDR_STRLEN_MAX);
1147 #endif
1148     LWIP_DEBUGF(MLD6_DEBUG | LWIP_DBG_TRACE, ("mld6_querier_tmr_listener: del listener %s\n",
1149                                               buf));
1150     mld6_querier_listener_del(netif, listener);
1151     mem_free(listener);
1152     listener = NULL;
1153     return;
1154   }
1155 
1156   listener->timer--;
1157 
1158   /* here timer is greater than 0 */
1159   if (listener->state != MLD6_LISTENER_STATE_CHECK) {
1160     return;
1161   }
1162 
1163   /* here state is MLD6_LISTENER_STATE_CHECK */
1164   listener->rexmt_timer--;
1165   if (listener->rexmt_timer > 0) {
1166     return;
1167   }
1168   listener->query_count++;
1169 #ifdef LWIP_DEBUG
1170   (void)ip6addr_ntoa_r(&(listener->group_address), buf, IP6ADDR_STRLEN_MAX);
1171 #endif
1172   LWIP_DEBUGF(MLD6_DEBUG | LWIP_DBG_TRACE, ("mld6_querier_tmr_listener: check listener %s %hhu\n",
1173                                             buf, listener->query_count));
1174   if (listener->query_count < MLD6_LAST_LISTENER_QUERY_COUNT) {
1175     mld6_query_send(netif, &(listener->group_address), MLD6_LAST_LISTENER_QUERY_INTERVAL);
1176   }
1177 
1178   return;
1179 }
1180 
1181 static void
mld6_querier_tmr_listeners(struct netif * netif,struct mld6_querier * querier)1182 mld6_querier_tmr_listeners(struct netif *netif, struct mld6_querier *querier)
1183 {
1184   struct mld6_listener *listener = querier->listeners;
1185   struct mld6_listener *listener_next = NULL;
1186 
1187   while (listener != NULL) {
1188     listener_next = listener->next;
1189     mld6_querier_tmr_listener(netif, listener);
1190     listener = listener_next;
1191   }
1192 
1193   return;
1194 }
1195 
1196 static void
mld6_querier_tmr(void)1197 mld6_querier_tmr(void)
1198 {
1199   struct netif *netif = netif_list;
1200 
1201   while (netif != NULL) {
1202     struct mld6_querier *querier = netif_mld6_querier_data(netif);
1203     if (querier == NULL) {
1204       netif = netif->next;
1205       continue;
1206     }
1207     mld6_querier_tmr_query(netif, querier);
1208     mld6_querier_tmr_listeners(netif, querier);
1209     netif = netif->next;
1210   }
1211 }
1212 
1213 static struct mld6_listener *
mld6_querier_listener_new(ip6_addr_t * addr)1214 mld6_querier_listener_new(ip6_addr_t *addr)
1215 {
1216   struct mld6_listener *listener = NULL;
1217 
1218   listener = (struct mld6_listener *)mem_malloc(sizeof(struct mld6_listener));
1219   if (listener == NULL) {
1220     LWIP_DEBUGF(MLD6_DEBUG | LWIP_DBG_TRACE, ("mld6_querier_listener_new: listener alloc failed\n"));
1221     return NULL;
1222   }
1223 
1224   listener->next = NULL;
1225   ip6_addr_copy_ptr(&(listener->group_address), addr);
1226   listener->state = MLD6_LISTENER_STATE_PRESENT;
1227   MLD6_TIMER_SET(listener->timer, (MLD6_LISTENER_INTERVAL * MLD6_MS_PER_SECOND));
1228   listener->rexmt_timer = 0;
1229   listener->query_count = 0;
1230 
1231   return listener;
1232 }
1233 
1234 static struct mld6_listener *
mld6_querier_listener_find(struct mld6_listener * listeners,ip6_addr_t * addr)1235 mld6_querier_listener_find(struct mld6_listener *listeners, ip6_addr_t *addr)
1236 {
1237   struct mld6_listener *listener = listeners;
1238 
1239   while (listener != NULL) {
1240     if (ip6_addr_cmp(&(listener->group_address), addr)) {
1241       break;
1242     }
1243     listener = listener->next;
1244   }
1245 
1246   return listener;
1247 }
1248 
1249 struct mld6_listener *
mld6_querier_listener_lookfor(struct netif * netif,const ip6_addr_t * addr)1250 mld6_querier_listener_lookfor(struct netif *netif, const ip6_addr_t *addr)
1251 {
1252   struct mld6_querier *querier = NULL;
1253   struct mld6_listener *listener = NULL;
1254 
1255   if ((netif == NULL) || (addr == NULL)) {
1256     return NULL;
1257   }
1258   querier = netif_mld6_querier_data(netif);
1259   if (querier == NULL) {
1260     return NULL;
1261   }
1262   listener = querier->listeners;
1263   while (listener != NULL) {
1264     if (ip6_addr_cmp(&(listener->group_address), addr)) {
1265       break;
1266     }
1267     listener = listener->next;
1268   }
1269 
1270   return listener;
1271 }
1272 
1273 static void
mld6_querier_listener_del(struct netif * netif,struct mld6_listener * listener)1274 mld6_querier_listener_del(struct netif *netif, struct mld6_listener *listener)
1275 {
1276   struct mld6_querier *querier = netif_mld6_querier_data(netif);
1277   struct mld6_listener *listener_p = NULL;
1278 
1279   /* Is it the first listener? */
1280   if (querier->listeners == listener) {
1281     querier->listeners = listener->next;
1282     querier->listener_num--;
1283     if (querier->listeners == NULL) {
1284       querier->querier_state = MLD6_QUERIER_STATE_NON_LISTENER;
1285       querier->query_count = 0;
1286       querier->timer = 0;
1287     }
1288   } else {
1289     /* look for listener further down the list */
1290     for (listener_p = querier->listeners; listener_p != NULL; listener_p = listener_p->next) {
1291       if (listener_p->next == listener) {
1292         querier->listener_num--;
1293         listener_p->next = listener->next;
1294         break;
1295       }
1296     }
1297   }
1298 
1299   return;
1300 }
1301 
1302 static void
mld6_querier_handle_report(struct mld_header * mld_hdr,struct netif * netif)1303 mld6_querier_handle_report(struct mld_header *mld_hdr, struct netif *netif)
1304 {
1305   struct mld6_querier *querier = netif_mld6_querier_data(netif);
1306   struct mld6_listener *listener = NULL;
1307   ip6_addr_t group_address;
1308 
1309   if (querier == NULL) {
1310     MLD6_STATS_INC(mld6.drop);
1311     LWIP_DEBUGF(MLD6_DEBUG | LWIP_DBG_TRACE, ("mld6_querier_handle_report: querier not start\n"));
1312     return;
1313   }
1314 
1315   if ((ip6_addr_isany_val(mld_hdr->multicast_address)) ||
1316       !(ip6_addr_cmp_zoneless(ip6_current_dest_addr(), &(mld_hdr->multicast_address)))) {
1317     MLD6_STATS_INC(mld6.drop);
1318     LWIP_DEBUGF(MLD6_DEBUG | LWIP_DBG_TRACE, ("mld6_querier_handle_report: invalid report\n"));
1319     return;
1320   }
1321 
1322   ip6_addr_copy_from_packed(group_address, mld_hdr->multicast_address);
1323 #if LWIP_MPL
1324   if (ip6_addr_multicast_scope(&group_address) <= IP6_MULTICAST_SCOPE_LINK_LOCAL) {
1325     LWIP_DEBUGF(MLD6_DEBUG | LWIP_DBG_TRACE, ("mld6_querier_handle_report: not fwd scope\n"));
1326     return;
1327   }
1328 #endif /* LWIP_MPL */
1329   listener = mld6_querier_listener_find(querier->listeners,  &(group_address));
1330   if (listener == NULL) {
1331     LWIP_ERROR("mld6_querier_handle_report: LISTENER_MAX_NUM", (querier->listener_num < MLD6_QUERIER_LISTENER_MAX_NUM),
1332                return);
1333     listener = mld6_querier_listener_new(&(group_address));
1334     if (listener == NULL) {
1335       LWIP_DEBUGF(MLD6_DEBUG | LWIP_DBG_TRACE, ("mld6_querier_handle_report: new listener failed\n"));
1336       return;
1337     }
1338     if ((querier->listeners == NULL) && (querier->querier_state == MLD6_QUERIER_STATE_NON_LISTENER)) {
1339       querier->querier_state = MLD6_QUERIER_STATE_HAVE_LISTENER;
1340       MLD6_TIMER_SET(querier->timer, ((MLD6_QUERY_INTERVAL) * MLD6_MS_PER_SECOND));
1341     }
1342     listener->next = querier->listeners;
1343     querier->listeners = listener;
1344     querier->listener_num++;
1345 #if LWIP_MPL
1346     LWIP_CONF_MLD6_JOIN_GRP((const ip6_addr_t *)(&group_address));
1347 #endif
1348   } else {
1349     listener->state = MLD6_LISTENER_STATE_PRESENT;
1350     MLD6_TIMER_SET(listener->timer, (MLD6_LISTENER_INTERVAL * MLD6_MS_PER_SECOND));
1351     listener->rexmt_timer = 0;
1352     listener->query_count = 0;
1353   }
1354 
1355   return;
1356 }
1357 
1358 static void
mld6_querier_handle_done(struct mld_header * mld_hdr,struct netif * netif)1359 mld6_querier_handle_done(struct mld_header *mld_hdr, struct netif *netif)
1360 {
1361   struct mld6_querier *querier = netif_mld6_querier_data(netif);
1362   struct mld6_listener *listener = NULL;
1363   u16_t new_timer;
1364   ip6_addr_t group_address;
1365 
1366   if ((querier == NULL) || (querier->listeners == NULL)) {
1367     MLD6_STATS_INC(mld6.drop);
1368     LWIP_DEBUGF(MLD6_DEBUG | LWIP_DBG_TRACE, ("mld6_querier_handle_done: querier not start\n"));
1369     return;
1370   }
1371 
1372   if ((ip6_addr_isany_val(mld_hdr->multicast_address)) ||
1373       !(ip6_addr_isallrouters_linklocal(ip6_current_dest_addr()))) {
1374     MLD6_STATS_INC(mld6.drop);
1375     LWIP_DEBUGF(MLD6_DEBUG | LWIP_DBG_TRACE, ("mld6_querier_handle_done: invalid done\n"));
1376     return;
1377   }
1378 
1379   ip6_addr_copy_from_packed(group_address, mld_hdr->multicast_address);
1380   listener = mld6_querier_listener_find(querier->listeners,  &(group_address));
1381   if (listener == NULL) {
1382     LWIP_DEBUGF(MLD6_DEBUG | LWIP_DBG_TRACE, ("mld6_querier_handle_done: no such listener\n"));
1383     return;
1384   }
1385   if (listener->state == MLD6_LISTENER_STATE_CHECK) {
1386     LWIP_DEBUGF(MLD6_DEBUG | LWIP_DBG_TRACE, ("mld6_querier_handle_done: checking listener\n"));
1387     return;
1388   }
1389 
1390   listener->state = MLD6_LISTENER_STATE_CHECK;
1391   /* Multicast-Address-Specific Query */
1392   mld6_query_send(netif, &(listener->group_address), MLD6_LAST_LISTENER_QUERY_INTERVAL);
1393   /*
1394    * [RFC 2710] Section 6
1395    * sets the timer to the minimum of its current value and either
1396    * [Last Listener Query Interval] * [Last Listener Query Count] if
1397    * this router is a Querier
1398    */
1399   MLD6_TIMER_SET(new_timer, MLD6_LAST_LISTENER_QUERY_INTERVAL * MLD6_LAST_LISTENER_QUERY_COUNT);
1400   listener->timer = LWIP_MIN(listener->timer, new_timer);
1401   /*
1402    * [RFC 2710] Section 6
1403    * "start retransmit timer" for the address on the link [Last Listener
1404    * Query Interval]
1405    */
1406   MLD6_TIMER_SET(listener->rexmt_timer, MLD6_LAST_LISTENER_QUERY_INTERVAL);
1407   listener->query_count = 0;
1408 
1409   return;
1410 }
1411 
1412 static void
mld6_querier_input(struct mld_header * mld_hdr,struct netif * netif)1413 mld6_querier_input(struct mld_header *mld_hdr, struct netif *netif)
1414 {
1415   /* here we not support Non-Querier, so not handle ICMP6_TYPE_MLQ */
1416   if (mld_hdr->type == ICMP6_TYPE_MLR) {
1417     mld6_querier_handle_report(mld_hdr, netif);
1418   } else if (mld_hdr->type == ICMP6_TYPE_MLD) {
1419     mld6_querier_handle_done(mld_hdr, netif);
1420   }
1421 
1422   return;
1423 }
1424 #endif /* LWIP_IPV6_MLD_QUERIER */
1425 
1426 #endif /* LWIP_IPV6 && (LWIP_IPV6_MLD || LWIP_IPV6_MLD_QUERIER) */
1427