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-2025 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.5.2
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  //Underlying network interface
75  context->interface = interface;
76  //The default host compatibility mode is MLDv2
78 
79  //In order to ensure interoperability, hosts maintain an Older Version
80  //Querier Present timer per interface
82 
83  //A timer per interface is used for scheduling responses to General Queries
85 
86  //A timer is used to retransmit State-Change reports
88 
89  //Successful initialization
90  return NO_ERROR;
91 }
92 
93 
94 /**
95  * @brief MLD node timer handler
96  *
97  * This routine must be periodically called by the TCP/IP stack to
98  * handle MLD related timers
99  *
100  * @param[in] context Pointer to the MLD node context
101  **/
102 
104 {
105  uint_t i;
106  systime_t delay;
107  MldNodeGroup *group;
108  NetInterface *interface;
109 
110  //Point to the underlying network interface
111  interface = context->interface;
112 
113  //In order to be compatible with MLDv1 routers, MLDv2 hosts must operate in
114  //version 1 compatibility mode (refer to RFC 3810, section 8.2.1)
116  {
117  //Stop Older Version Querier Present timer
119 
120  //If the Older Version Querier Present timer expires, the host switches
121  //back to Host Compatibility Mode of MLDv2
123  }
124 
125  //Check host compatibility mode
126  if(context->compatibilityMode == MLD_VERSION_1)
127  {
128  //Loop through multicast groups
129  for(i = 0; i < IPV6_MULTICAST_FILTER_SIZE; i++)
130  {
131  //Point to the current group
132  group = &context->groups[i];
133 
134  //Check group state
136  {
137  //Valid link-local address assigned to the interface?
138  if(interface->linkState &&
140  {
141  //When a node starts listening to a multicast address on an
142  //interface, it should immediately transmit an unsolicited Report
143  //for that address on that interface
144  mldNodeSendListenerReport(context, &group->addr);
145 
146  //Start delay timer
148 
149  //Set flag
150  group->flag = TRUE;
151  //Enter the Delaying Listener state
153  }
154  }
156  {
157  //Delay timer expired?
158  if(netTimerExpired(&group->timer))
159  {
160  //Send a Multicast Listener Report message for the group on the
161  //interface
162  mldNodeSendListenerReport(context, &group->addr);
163 
164  //Stop delay timer
165  netStopTimer(&group->timer);
166 
167  //Set flag
168  group->flag = TRUE;
169  //Switch to the Idle Listener state
171  }
172  }
173  else
174  {
175  //Just for sanity
176  }
177  }
178  }
179  else
180  {
181  //If the expired timer is the interface timer, then one Current-State
182  //Record is sent for each multicast address for which the specified
183  //interface has reception state
184  if(netTimerExpired(&context->generalQueryTimer))
185  {
186  //Send Current-State report message
188 
189  //Stop interface timer
190  netStopTimer(&context->generalQueryTimer);
191  }
192 
193  //If the expired timer is a group timer, then a single Current-State
194  //Record is sent for the corresponding group address
195  for(i = 0; i < IPV6_MULTICAST_FILTER_SIZE; i++)
196  {
197  //Point to the current group
198  group = &context->groups[i];
199 
200  //Check group state
202  {
203  //Valid link-local address assigned to the interface?
204  if(interface->linkState &&
206  {
207 #if (IPV6_MAX_MULTICAST_SOURCES > 0)
208  //Once a valid link-local address is available, a node should
209  //generate new MLD Report messages for all multicast addresses
210  //joined on the interface (refer to RFC 3590, section 4)
211  if(group->filterMode == IP_FILTER_MODE_INCLUDE &&
212  group->filter.numSources > 0)
213  {
214  uint_t j;
215 
216  //The State-Change report will include an ALLOW record
217  group->retransmitCount = 0;
218  group->allow.numSources = group->filter.numSources;
219  group->block.numSources = 0;
220 
221  //List of the sources that the system wishes to hear from
222  for(j = 0; j < group->filter.numSources; j++)
223  {
224  group->allow.sources[j].addr = group->filter.sources[j];
225  group->allow.sources[j].retransmitCount = MLD_ROBUSTNESS_VARIABLE;
226  }
227 
228  //Send a State-Change report immediately
230  }
231  else if(group->filterMode == IP_FILTER_MODE_EXCLUDE)
232  {
233  //The State-Change report will include a TO_EX record
235  group->allow.numSources = 0;
236  group->block.numSources = 0;
237 
238  //Send a State-Change report immediately
240  }
241  else
242  {
243  //Just for sanity
244  }
245 #else
246  //Once a valid link-local address is available, a node should
247  //generate new MLD Report messages for all multicast addresses
248  //joined on the interface (refer to RFC 3590, section 4)
249  if(group->filterMode == IP_FILTER_MODE_EXCLUDE)
250  {
251  //The State-Change report will include a TO_EX record
253  //Send a State-Change report immediately
255  }
256 #endif
257  //Enter the Idle Listener state
259  }
260  }
261  else if(group->state == MLD_NODE_GROUP_STATE_IDLE_LISTENER)
262  {
263  //Check whether the group timer has expired
264  if(netTimerExpired(&group->timer))
265  {
266  //Send Current-State report message
267  mldNodeSendCurrentStateReport(context, &group->addr);
268 
269  //Stop group timer
270  netStopTimer(&group->timer);
271  }
272  }
273  else
274  {
275  //Just for sanity
276  }
277  }
278 
279  //If the expired timer is the retransmission timer, then the State-Change
280  //report is retransmitted
282  {
283  //Retransmit the State-Change report message
285 
286  //Retransmission state needs to be maintained until [Robustness
287  //Variable] State-Change reports have been sent by the host
288  if(mldNodeGetRetransmitStatus(context))
289  {
290  //Select a value in the range 0 - Unsolicited Report Interval
292  //Restart retransmission timer
293  netStartTimer(&context->stateChangeReportTimer, delay);
294  }
295  else
296  {
297  //[Robustness Variable] State-Change reports have been sent by the
298  //host
300  }
301 
302  //Delete groups in "non-existent" state
303  mldNodeFlushUnusedGroups(context);
304  }
305  }
306 }
307 
308 
309 /**
310  * @brief Process multicast reception state change
311  * @param[in] context Pointer to the MLD node context
312  * @param[in] groupAddr Multicast group address
313  * @param[in] newFilterMode New filter mode for the affected group
314  * @param[in] newFilter New interface state for the affected group
315  **/
316 
318  IpFilterMode newFilterMode, const Ipv6SrcAddrList *newFilter)
319 {
320  systime_t delay;
321  MldNodeGroup *group;
322  NetInterface *interface;
323 
324  //Point to the underlying network interface
325  interface = context->interface;
326 
327  //Search the list of groups for the specified multicast address
328  group = mldNodeFindGroup(context, groupAddr);
329 
330  //Check whether the interface has reception state for that group address
331  if(newFilterMode == IP_FILTER_MODE_EXCLUDE || newFilter->numSources > 0)
332  {
333  //No matching group found?
334  if(group == NULL)
335  {
336  //Create a new group
337  group = mldNodeCreateGroup(context, groupAddr);
338 
339  //Entry successfully created?
340  if(group != NULL)
341  {
342  //Valid link-local address assigned to the interface?
343  if(interface->linkState &&
345  {
346  //Check host compatibility mode
347  if(context->compatibilityMode == MLD_VERSION_1)
348  {
349  //When a node starts listening to a multicast address on an
350  //interface, it should immediately transmit an unsolicited
351  //Report for that address on that interface
352  mldNodeSendListenerReport(context, &group->addr);
353 
354  //Start delay timer
356 
357  //Set flag
358  group->flag = TRUE;
359  //Enter the Delaying Listener state
361  }
362  else
363  {
364  //Enter the Idle Listener state
365  group->state = MLD_NODE_GROUP_STATE_IDLE_LISTENER;
366  }
367  }
368  else
369  {
370  //Clear flag
371  group->flag = FALSE;
372  //Enter the Init Listener state
373  group->state = MLD_NODE_GROUP_STATE_INIT_LISTENER;
374  }
375  }
376  }
377  }
378 
379  //Valid group?
380  if(group != NULL)
381  {
382  //Any state change detected?
383  if(group->filterMode != newFilterMode ||
384  !ipv6CompareSrcAddrLists(&group->filter, newFilter))
385  {
386  //Merge the difference report resulting from the state change and the
387  //pending report
388  mldNodeMergeReports(group, newFilterMode, newFilter);
389 
390  //Save the new state
391  group->filterMode = newFilterMode;
392  group->filter = *newFilter;
393 
394  //Check host compatibility mode
395  if(context->compatibilityMode == MLD_VERSION_1)
396  {
397  //The "non-existent" state is considered to have a filter mode of
398  //INCLUDE and an empty source list
399  if(group->filterMode == IP_FILTER_MODE_INCLUDE &&
400  group->filter.numSources == 0)
401  {
402  //Send a Multicast Listener Done message if the flag is set
403  if(group->flag)
404  {
405  mldNodeSendListenerDone(context, &group->addr);
406  }
407 
408  //Delete the group
409  mldNodeDeleteGroup(group);
410  }
411  }
412  else
413  {
414  //Check group state
415  if(group->state == MLD_NODE_GROUP_STATE_INIT_LISTENER)
416  {
417  //The "non-existent" state is considered to have a filter mode
418  //of INCLUDE and an empty source list
419  if(group->filterMode == IP_FILTER_MODE_INCLUDE &&
420  group->filter.numSources == 0)
421  {
422  //Delete the group
423  mldNodeDeleteGroup(group);
424  }
425  }
426  else
427  {
428  //Send a State-Change report message
430 
431  //To cover the possibility of the State-Change report being
432  //missed by one or more multicast routers, it is retransmitted
433  //[Robustness Variable] - 1 more times
434  if(mldNodeGetRetransmitStatus(context))
435  {
436  //Select a value in the range 0 - Unsolicited Report Interval
438  //Start retransmission timer
439  netStartTimer(&context->stateChangeReportTimer, delay);
440  }
441  else
442  {
443  //[Robustness Variable] State-Change reports have been sent
444  //by the host
446  }
447 
448  //Delete groups in "non-existent" state
449  mldNodeFlushUnusedGroups(context);
450  }
451  }
452  }
453  }
454 }
455 
456 
457 /**
458  * @brief Callback function for link change event
459  * @param[in] context Pointer to the MLD node context
460  **/
461 
463 {
464  uint_t i;
465  MldNodeGroup *group;
466 
467  //The default host compatibility mode is MLDv2
468  context->compatibilityMode = MLD_VERSION_2;
469 
470  //Stop timers
472  netStopTimer(&context->generalQueryTimer);
474 
475  //Loop through multicast groups
476  for(i = 0; i < IPV6_MULTICAST_FILTER_SIZE; i++)
477  {
478  //Point to the current group
479  group = &context->groups[i];
480 
481  //Valid group?
483  {
484  //Reset parameters
485  group->flag = FALSE;
486  group->retransmitCount = 0;
487 
488 #if (IPV6_MAX_MULTICAST_SOURCES > 0)
489  //Clear source lists
490  group->allow.numSources = 0;
491  group->block.numSources = 0;
492  group->queriedSources.numSources = 0;
493 #endif
494  //Stop delay timer
495  netStopTimer(&group->timer);
496 
497  //Enter the Init Listener state
499  }
500  }
501 
502  //Delete groups in "non-existent" state
503  mldNodeFlushUnusedGroups(context);
504 }
505 
506 #endif
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:781
void mldNodeChangeCompatibilityMode(MldNodeContext *context, MldVersion compatibilityMode)
Change host compatibility mode.
Definition: mld_node_misc.c:53
Source address list.
Definition: ipv6.h:469
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:119
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:260
@ 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:116
#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:103
MldNodeGroup groups[IPV6_MULTICAST_FILTER_SIZE]
Multicast groups.
Definition: mld_node.h:120
#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:317
systime_t mldGetRandomDelay(systime_t maxDelay)
Generate a random delay.
Definition: mld_common.c:270
IPv6 multicast filtering.
#define FALSE
Definition: os_port.h:46
void mldNodeSendStateChangeReport(MldNodeContext *context, const Ipv6Addr *groupAddr)
Send State-Change Report message.
error_t
Error codes.
Definition: error.h:43
void netStopTimer(NetTimer *timer)
Stop timer.
Definition: net_misc.c:795
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:470
#define NetInterface
Definition: net.h:36
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:100
const Ipv6Addr IPV6_UNSPECIFIED_ADDR
Definition: ipv6.c:66
NetTimer olderVersionQuerierPresentTimer
Older version querier present timer.
Definition: mld_node.h:117
NetInterface * interface
Underlying network interface.
Definition: mld_node.h:115
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:462
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:327
@ 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:118
@ IPV6_ADDR_STATE_PREFERRED
An address assigned to an interface whose use is unrestricted.
Definition: ipv6.h:175
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:821
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