mld_node.c
Go to the documentation of this file.
1 /**
2  * @file mld_node.c
3  * @brief MLD node (Multicast Listener Discovery for IPv6)
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2010-2026 Oryx Embedded SARL. All rights reserved.
10  *
11  * This file is part of CycloneTCP Open.
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26  *
27  * @section Description
28  *
29  * MLD is used by an IPv6 router to discover the presence of multicast
30  * listeners on its directly attached links, and to discover specifically
31  * which multicast addresses are of interest to those neighboring nodes.
32  * Refer to the following RFCs for complete details:
33  * - RFC 2710: Multicast Listener Discovery (MLD) for IPv6
34  * - RFC 3590: Source Address Selection for MLD Protocol
35  * - RFC 3810: Multicast Listener Discovery Version 2 (MLDv2) for IPv6
36  * - RFC 9777: Multicast Listener Discovery Version 2 (MLDv2) for IPv6
37  *
38  * @author Oryx Embedded SARL (www.oryx-embedded.com)
39  * @version 2.6.0
40  **/
41 
42 //Switch to the appropriate trace level
43 #define TRACE_LEVEL MLD_TRACE_LEVEL
44 
45 //Dependencies
46 #include "core/net.h"
47 #include "ipv6/ipv6.h"
48 #include "ipv6/ipv6_multicast.h"
49 #include "ipv6/ipv6_misc.h"
50 #include "mld/mld_node.h"
51 #include "mld/mld_node_misc.h"
52 #include "debug.h"
53 
54 //Check TCP/IP stack configuration
55 #if (IPV6_SUPPORT == ENABLED && MLD_NODE_SUPPORT == ENABLED)
56 
57 
58 /**
59  * @brief MLD node initialization
60  * @param[in] interface Underlying network interface
61  * @return Error code
62  **/
63 
65 {
66  MldNodeContext *context;
67 
68  //Point to the MLD node context
69  context = &interface->mldNodeContext;
70 
71  //Clear the MLD node context
72  osMemset(context, 0, sizeof(MldNodeContext));
73 
74  //Pointer to the TCP/IP stack context
75  context->netContext = interface->netContext;
76  //Underlying network interface
77  context->interface = interface;
78  //The default host compatibility mode is MLDv2
80 
81  //In order to ensure interoperability, hosts maintain an Older Version
82  //Querier Present timer per interface
84 
85  //A timer per interface is used for scheduling responses to General Queries
87 
88  //A timer is used to retransmit State-Change reports
90 
91  //Successful initialization
92  return NO_ERROR;
93 }
94 
95 
96 /**
97  * @brief MLD node timer handler
98  *
99  * This routine must be periodically called by the TCP/IP stack to
100  * handle MLD related timers
101  *
102  * @param[in] context Pointer to the MLD node context
103  **/
104 
106 {
107  uint_t i;
108  systime_t delay;
109  MldNodeGroup *group;
110  NetInterface *interface;
111 
112  //Point to the underlying network interface
113  interface = context->interface;
114 
115  //In order to be compatible with MLDv1 routers, MLDv2 hosts must operate in
116  //version 1 compatibility mode (refer to RFC 3810, section 8.2.1)
118  {
119  //Stop Older Version Querier Present timer
121 
122  //If the Older Version Querier Present timer expires, the host switches
123  //back to Host Compatibility Mode of MLDv2
125  }
126 
127  //Check host compatibility mode
128  if(context->compatibilityMode == MLD_VERSION_1)
129  {
130  //Loop through multicast groups
131  for(i = 0; i < IPV6_MULTICAST_FILTER_SIZE; i++)
132  {
133  //Point to the current group
134  group = &context->groups[i];
135 
136  //Check group state
138  {
139  //Valid link-local address assigned to the interface?
140  if(interface->linkState &&
142  {
143  //When a node starts listening to a multicast address on an
144  //interface, it should immediately transmit an unsolicited Report
145  //for that address on that interface
146  mldNodeSendListenerReport(context, &group->addr);
147 
148  //Start delay timer
150 
151  //Set flag
152  group->flag = TRUE;
153  //Enter the Delaying Listener state
155  }
156  }
158  {
159  //Delay timer expired?
160  if(netTimerExpired(&group->timer))
161  {
162  //Send a Multicast Listener Report message for the group on the
163  //interface
164  mldNodeSendListenerReport(context, &group->addr);
165 
166  //Stop delay timer
167  netStopTimer(&group->timer);
168 
169  //Set flag
170  group->flag = TRUE;
171  //Switch to the Idle Listener state
173  }
174  }
175  else
176  {
177  //Just for sanity
178  }
179  }
180  }
181  else
182  {
183  //If the expired timer is the interface timer, then one Current-State
184  //Record is sent for each multicast address for which the specified
185  //interface has reception state
186  if(netTimerExpired(&context->generalQueryTimer))
187  {
188  //Send Current-State report message
190 
191  //Stop interface timer
192  netStopTimer(&context->generalQueryTimer);
193  }
194 
195  //If the expired timer is a group timer, then a single Current-State
196  //Record is sent for the corresponding group address
197  for(i = 0; i < IPV6_MULTICAST_FILTER_SIZE; i++)
198  {
199  //Point to the current group
200  group = &context->groups[i];
201 
202  //Check group state
204  {
205  //Valid link-local address assigned to the interface?
206  if(interface->linkState &&
208  {
209 #if (IPV6_MAX_MULTICAST_SOURCES > 0)
210  //Once a valid link-local address is available, a node should
211  //generate new MLD Report messages for all multicast addresses
212  //joined on the interface (refer to RFC 3590, section 4)
213  if(group->filterMode == IP_FILTER_MODE_INCLUDE &&
214  group->filter.numSources > 0)
215  {
216  uint_t j;
217 
218  //The State-Change report will include an ALLOW record
219  group->retransmitCount = 0;
220  group->allow.numSources = group->filter.numSources;
221  group->block.numSources = 0;
222 
223  //List of the sources that the system wishes to hear from
224  for(j = 0; j < group->filter.numSources; j++)
225  {
226  group->allow.sources[j].addr = group->filter.sources[j];
227  group->allow.sources[j].retransmitCount = MLD_ROBUSTNESS_VARIABLE;
228  }
229 
230  //Send a State-Change report immediately
232  }
233  else if(group->filterMode == IP_FILTER_MODE_EXCLUDE)
234  {
235  //The State-Change report will include a TO_EX record
237  group->allow.numSources = 0;
238  group->block.numSources = 0;
239 
240  //Send a State-Change report immediately
242  }
243  else
244  {
245  //Just for sanity
246  }
247 #else
248  //Once a valid link-local address is available, a node should
249  //generate new MLD Report messages for all multicast addresses
250  //joined on the interface (refer to RFC 3590, section 4)
251  if(group->filterMode == IP_FILTER_MODE_EXCLUDE)
252  {
253  //The State-Change report will include a TO_EX record
255  //Send a State-Change report immediately
257  }
258 #endif
259  //Enter the Idle Listener state
261  }
262  }
263  else if(group->state == MLD_NODE_GROUP_STATE_IDLE_LISTENER)
264  {
265  //Check whether the group timer has expired
266  if(netTimerExpired(&group->timer))
267  {
268  //Send Current-State report message
269  mldNodeSendCurrentStateReport(context, &group->addr);
270 
271  //Stop group timer
272  netStopTimer(&group->timer);
273  }
274  }
275  else
276  {
277  //Just for sanity
278  }
279  }
280 
281  //If the expired timer is the retransmission timer, then the State-Change
282  //report is retransmitted
284  {
285  //Retransmit the State-Change report message
287 
288  //Retransmission state needs to be maintained until [Robustness
289  //Variable] State-Change reports have been sent by the host
290  if(mldNodeGetRetransmitStatus(context))
291  {
292  //Select a value in the range 0 - Unsolicited Report Interval
293  delay = mldGetRandomDelay(context->netContext,
295 
296  //Restart retransmission timer
297  netStartTimer(&context->stateChangeReportTimer, delay);
298  }
299  else
300  {
301  //[Robustness Variable] State-Change reports have been sent by the
302  //host
304  }
305 
306  //Delete groups in "non-existent" state
307  mldNodeFlushUnusedGroups(context);
308  }
309  }
310 }
311 
312 
313 /**
314  * @brief Process multicast reception state change
315  * @param[in] context Pointer to the MLD node context
316  * @param[in] groupAddr Multicast group address
317  * @param[in] newFilterMode New filter mode for the affected group
318  * @param[in] newFilter New interface state for the affected group
319  **/
320 
322  IpFilterMode newFilterMode, const Ipv6SrcAddrList *newFilter)
323 {
324  systime_t delay;
325  MldNodeGroup *group;
326  NetInterface *interface;
327 
328  //Point to the underlying network interface
329  interface = context->interface;
330 
331  //Search the list of groups for the specified multicast address
332  group = mldNodeFindGroup(context, groupAddr);
333 
334  //Check whether the interface has reception state for that group address
335  if(newFilterMode == IP_FILTER_MODE_EXCLUDE || newFilter->numSources > 0)
336  {
337  //No matching group found?
338  if(group == NULL)
339  {
340  //Create a new group
341  group = mldNodeCreateGroup(context, groupAddr);
342 
343  //Entry successfully created?
344  if(group != NULL)
345  {
346  //Valid link-local address assigned to the interface?
347  if(interface->linkState &&
349  {
350  //Check host compatibility mode
351  if(context->compatibilityMode == MLD_VERSION_1)
352  {
353  //When a node starts listening to a multicast address on an
354  //interface, it should immediately transmit an unsolicited
355  //Report for that address on that interface
356  mldNodeSendListenerReport(context, &group->addr);
357 
358  //Start delay timer
360 
361  //Set flag
362  group->flag = TRUE;
363  //Enter the Delaying Listener state
365  }
366  else
367  {
368  //Enter the Idle Listener state
369  group->state = MLD_NODE_GROUP_STATE_IDLE_LISTENER;
370  }
371  }
372  else
373  {
374  //Clear flag
375  group->flag = FALSE;
376  //Enter the Init Listener state
377  group->state = MLD_NODE_GROUP_STATE_INIT_LISTENER;
378  }
379  }
380  }
381  }
382 
383  //Valid group?
384  if(group != NULL)
385  {
386  //Any state change detected?
387  if(group->filterMode != newFilterMode ||
388  !ipv6CompareSrcAddrLists(&group->filter, newFilter))
389  {
390  //Merge the difference report resulting from the state change and the
391  //pending report
392  mldNodeMergeReports(group, newFilterMode, newFilter);
393 
394  //Save the new state
395  group->filterMode = newFilterMode;
396  group->filter = *newFilter;
397 
398  //Check host compatibility mode
399  if(context->compatibilityMode == MLD_VERSION_1)
400  {
401  //The "non-existent" state is considered to have a filter mode of
402  //INCLUDE and an empty source list
403  if(group->filterMode == IP_FILTER_MODE_INCLUDE &&
404  group->filter.numSources == 0)
405  {
406  //Send a Multicast Listener Done message if the flag is set
407  if(group->flag)
408  {
409  mldNodeSendListenerDone(context, &group->addr);
410  }
411 
412  //Delete the group
413  mldNodeDeleteGroup(group);
414  }
415  }
416  else
417  {
418  //Check group state
419  if(group->state == MLD_NODE_GROUP_STATE_INIT_LISTENER)
420  {
421  //The "non-existent" state is considered to have a filter mode
422  //of INCLUDE and an empty source list
423  if(group->filterMode == IP_FILTER_MODE_INCLUDE &&
424  group->filter.numSources == 0)
425  {
426  //Delete the group
427  mldNodeDeleteGroup(group);
428  }
429  }
430  else
431  {
432  //Send a State-Change report message
434 
435  //To cover the possibility of the State-Change report being
436  //missed by one or more multicast routers, it is retransmitted
437  //[Robustness Variable] - 1 more times
438  if(mldNodeGetRetransmitStatus(context))
439  {
440  //Select a value in the range 0 - Unsolicited Report Interval
441  delay = mldGetRandomDelay(context->netContext,
443 
444  //Start retransmission timer
445  netStartTimer(&context->stateChangeReportTimer, delay);
446  }
447  else
448  {
449  //[Robustness Variable] State-Change reports have been sent
450  //by the host
452  }
453 
454  //Delete groups in "non-existent" state
455  mldNodeFlushUnusedGroups(context);
456  }
457  }
458  }
459  }
460 }
461 
462 
463 /**
464  * @brief Callback function for link change event
465  * @param[in] context Pointer to the MLD node context
466  **/
467 
469 {
470  uint_t i;
471  MldNodeGroup *group;
472 
473  //The default host compatibility mode is MLDv2
474  context->compatibilityMode = MLD_VERSION_2;
475 
476  //Stop timers
478  netStopTimer(&context->generalQueryTimer);
480 
481  //Loop through multicast groups
482  for(i = 0; i < IPV6_MULTICAST_FILTER_SIZE; i++)
483  {
484  //Point to the current group
485  group = &context->groups[i];
486 
487  //Valid group?
489  {
490  //Reset parameters
491  group->flag = FALSE;
492  group->retransmitCount = 0;
493 
494 #if (IPV6_MAX_MULTICAST_SOURCES > 0)
495  //Clear source lists
496  group->allow.numSources = 0;
497  group->block.numSources = 0;
498  group->queriedSources.numSources = 0;
499 #endif
500  //Stop delay timer
501  netStopTimer(&group->timer);
502 
503  //Enter the Init Listener state
505  }
506  }
507 
508  //Delete groups in "non-existent" state
509  mldNodeFlushUnusedGroups(context);
510 }
511 
512 #endif
systime_t mldGetRandomDelay(NetContext *context, systime_t maxDelay)
Generate a random delay.
Definition: mld_common.c:267
IpFilterMode
Multicast filter mode.
Definition: ip.h:67
IPv6 (Internet Protocol Version 6)
void netStartTimer(NetTimer *timer, systime_t interval)
Start timer.
Definition: net_misc.c:798
void mldNodeChangeCompatibilityMode(MldNodeContext *context, MldVersion compatibilityMode)
Change host compatibility mode.
Definition: mld_node_misc.c:53
Source address list.
Definition: ipv6.h:489
bool_t ipv6CompareSrcAddrLists(const Ipv6SrcAddrList *list1, const Ipv6SrcAddrList *list2)
Compare lists of sources.
NetTimer stateChangeReportTimer
Retransmission timer for state-change reports.
Definition: mld_node.h:120
Ipv6SrcAddrList filter
Current-state record.
Definition: mld_node.h:100
bool_t flag
We are the last host to send a report for this group.
Definition: mld_node.h:96
#define TRUE
Definition: os_port.h:50
MldNodeGroup * mldNodeFindGroup(MldNodeContext *context, const Ipv6Addr *groupAddr)
Search the list of multicast groups for a given group address.
NetTimer timer
Report delay timer.
Definition: mld_node.h:98
Ipv6Addr
Definition: ipv6.h:280
@ IP_FILTER_MODE_EXCLUDE
Definition: ip.h:68
@ MLD_NODE_GROUP_STATE_DELAYING_LISTENER
Definition: mld_node.h:59
void mldNodeMergeReports(MldNodeGroup *group, IpFilterMode newFilterMode, const Ipv6SrcAddrList *newFilter)
Merge the difference report and the pending report.
MLD node context.
Definition: mld_node.h:114
error_t mldNodeInit(NetInterface *interface)
MLD node initialization.
Definition: mld_node.c:64
MldVersion compatibilityMode
Host compatibility mode.
Definition: mld_node.h:117
#define MLD_UNSOLICITED_REPORT_INTERVAL
Definition: mld_common.h:53
void mldNodeSendListenerDone(MldNodeContext *context, const Ipv6Addr *groupAddr)
Send Multicast Listener Done message.
@ MLD_VERSION_1
Definition: mld_common.h:94
void mldNodeTick(MldNodeContext *context)
MLD node timer handler.
Definition: mld_node.c:105
MldNodeGroup groups[IPV6_MULTICAST_FILTER_SIZE]
Multicast groups.
Definition: mld_node.h:121
#define MLD_ROBUSTNESS_VARIABLE
Definition: mld_common.h:46
void mldNodeStateChangeEvent(MldNodeContext *context, const Ipv6Addr *groupAddr, IpFilterMode newFilterMode, const Ipv6SrcAddrList *newFilter)
Process multicast reception state change.
Definition: mld_node.c:321
IPv6 multicast filtering.
#define FALSE
Definition: os_port.h:46
void mldNodeSendStateChangeReport(MldNodeContext *context, const Ipv6Addr *groupAddr)
Send State-Change Report message.
NetContext * netContext
TCP/IP stack context.
Definition: mld_node.h:115
error_t
Error codes.
Definition: error.h:43
void netStopTimer(NetTimer *timer)
Stop timer.
Definition: net_misc.c:812
IpFilterMode filterMode
Filter mode.
Definition: mld_node.h:99
MldNodeGroup * mldNodeCreateGroup(MldNodeContext *context, const Ipv6Addr *groupAddr)
Create a new multicast group.
uint_t numSources
Number of source addresses.
Definition: ipv6.h:490
#define NetInterface
Definition: net.h:40
Helper functions for IPv6.
void mldNodeFlushUnusedGroups(MldNodeContext *context)
Delete groups in "non-existent" state.
@ MLD_NODE_GROUP_STATE_IDLE_LISTENER
Definition: mld_node.h:60
#define IPV6_MULTICAST_FILTER_SIZE
Definition: ipv6.h:107
const Ipv6Addr IPV6_UNSPECIFIED_ADDR
Definition: ipv6.c:65
NetTimer olderVersionQuerierPresentTimer
Older version querier present timer.
Definition: mld_node.h:118
NetInterface * interface
Underlying network interface.
Definition: mld_node.h:116
Ipv6Addr addr
Multicast group address.
Definition: mld_node.h:95
uint_t retransmitCount
Filter mode retransmission counter.
Definition: mld_node.h:97
uint32_t systime_t
System time.
void mldNodeLinkChangeEvent(MldNodeContext *context)
Callback function for link change event.
Definition: mld_node.c:468
MLD node (Multicast Listener Discovery for IPv6)
void mldNodeSendCurrentStateReport(MldNodeContext *context, const Ipv6Addr *groupAddr)
Send Current-State Report message.
bool_t mldNodeGetRetransmitStatus(MldNodeContext *context)
Get the retransmission status of the State-Change report.
void mldNodeSendListenerReport(MldNodeContext *context, const Ipv6Addr *groupAddr)
Send Multicast Listener Report message.
Ipv4Addr groupAddr
Definition: igmp_common.h:214
Ipv6AddrState ipv6GetLinkLocalAddrState(NetInterface *interface)
Get the state of the link-local address.
Definition: ipv6.c:330
@ MLD_NODE_GROUP_STATE_NON_LISTENER
Definition: mld_node.h:57
@ IP_FILTER_MODE_INCLUDE
Definition: ip.h:69
void mldNodeDeleteGroup(MldNodeGroup *group)
Delete a multicast group.
@ MLD_NODE_GROUP_STATE_INIT_LISTENER
Definition: mld_node.h:58
NetTimer generalQueryTimer
Timer for scheduling responses to general queries.
Definition: mld_node.h:119
@ IPV6_ADDR_STATE_PREFERRED
An address assigned to an interface whose use is unrestricted.
Definition: ipv6.h:195
Helper functions for MLD node.
unsigned int uint_t
Definition: compiler_port.h:57
#define osMemset(p, value, length)
Definition: os_port.h:138
TCP/IP stack core.
@ MLD_VERSION_2
Definition: mld_common.h:95
bool_t netTimerExpired(NetTimer *timer)
Check whether the timer has expired.
Definition: net_misc.c:838
Multicast group.
Definition: mld_node.h:93
#define MLD_V2_UNSOLICITED_REPORT_INTERVAL
Definition: mld_common.h:67
@ NO_ERROR
Success.
Definition: error.h:44
Debugging facilities.
MldNodeGroupState state
Multicast group state.
Definition: mld_node.h:94