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