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-2024 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  * @author Oryx Embedded SARL (www.oryx-embedded.com)
28  * @version 2.4.4
29  **/
30 
31 //Switch to the appropriate trace level
32 #define TRACE_LEVEL MLD_TRACE_LEVEL
33 
34 //Dependencies
35 #include "core/net.h"
36 #include "ipv6/ipv6.h"
37 #include "ipv6/ipv6_multicast.h"
38 #include "ipv6/ipv6_misc.h"
39 #include "mld/mld_node.h"
40 #include "mld/mld_node_misc.h"
41 #include "debug.h"
42 
43 //Check TCP/IP stack configuration
44 #if (IPV6_SUPPORT == ENABLED && MLD_NODE_SUPPORT == ENABLED)
45 
46 
47 /**
48  * @brief MLD node initialization
49  * @param[in] interface Underlying network interface
50  * @return Error code
51  **/
52 
54 {
55  MldNodeContext *context;
56 
57  //Point to the MLD node context
58  context = &interface->mldNodeContext;
59 
60  //Clear the MLD node context
61  osMemset(context, 0, sizeof(MldNodeContext));
62 
63  //Underlying network interface
64  context->interface = interface;
65  //The default host compatibility mode is MLDv2
67 
68  //In order to ensure interoperability, hosts maintain an Older Version
69  //Querier Present timer per interface
71 
72  //A timer per interface is used for scheduling responses to General Queries
74 
75  //A timer is used to retransmit State-Change reports
77 
78  //Successful initialization
79  return NO_ERROR;
80 }
81 
82 
83 /**
84  * @brief MLD node timer handler
85  *
86  * This routine must be periodically called by the TCP/IP stack to
87  * handle MLD related timers
88  *
89  * @param[in] context Pointer to the MLD node context
90  **/
91 
93 {
94  uint_t i;
95  systime_t delay;
96  MldNodeGroup *group;
97  NetInterface *interface;
98 
99  //Point to the underlying network interface
100  interface = context->interface;
101 
102  //In order to be compatible with MLDv1 routers, MLDv2 hosts must operate in
103  //version 1 compatibility mode (refer to RFC 3810, section 8.2.1)
105  {
106  //Stop Older Version Querier Present timer
108 
109  //If the Older Version Querier Present timer expires, the host switches
110  //back to Host Compatibility Mode of MLDv2
112  }
113 
114  //Check host compatibility mode
115  if(context->compatibilityMode == MLD_VERSION_1)
116  {
117  //Loop through multicast groups
118  for(i = 0; i < IPV6_MULTICAST_FILTER_SIZE; i++)
119  {
120  //Point to the current group
121  group = &context->groups[i];
122 
123  //Check group state
125  {
126  //Valid link-local address assigned to the interface?
127  if(interface->linkState &&
129  {
130  //When a node starts listening to a multicast address on an
131  //interface, it should immediately transmit an unsolicited Report
132  //for that address on that interface
133  mldNodeSendListenerReport(context, &group->addr);
134 
135  //Start delay timer
137 
138  //Set flag
139  group->flag = TRUE;
140  //Enter the Delaying Listener state
142  }
143  }
145  {
146  //Delay timer expired?
147  if(netTimerExpired(&group->timer))
148  {
149  //Send a Multicast Listener Report message for the group on the
150  //interface
151  mldNodeSendListenerReport(context, &group->addr);
152 
153  //Stop delay timer
154  netStopTimer(&group->timer);
155 
156  //Set flag
157  group->flag = TRUE;
158  //Switch to the Idle Listener state
160  }
161  }
162  else
163  {
164  //Just for sanity
165  }
166  }
167  }
168  else
169  {
170  //If the expired timer is the interface timer, then one Current-State
171  //Record is sent for each multicast address for which the specified
172  //interface has reception state
173  if(netTimerExpired(&context->generalQueryTimer))
174  {
175  //Send Current-State report message
177 
178  //Stop interface timer
179  netStopTimer(&context->generalQueryTimer);
180  }
181 
182  //If the expired timer is a group timer, then a single Current-State
183  //Record is sent for the corresponding group address
184  for(i = 0; i < IPV6_MULTICAST_FILTER_SIZE; i++)
185  {
186  //Point to the current group
187  group = &context->groups[i];
188 
189  //Check group state
191  {
192  //Valid link-local address assigned to the interface?
193  if(interface->linkState &&
195  {
196 #if (IPV6_MAX_MULTICAST_SOURCES > 0)
197  //Once a valid link-local address is available, a node should
198  //generate new MLD Report messages for all multicast addresses
199  //joined on the interface (refer to RFC 3590, section 4)
200  if(group->filterMode == IP_FILTER_MODE_INCLUDE &&
201  group->filter.numSources > 0)
202  {
203  uint_t j;
204 
205  //The State-Change report will include an ALLOW record
206  group->retransmitCount = 0;
207  group->allow.numSources = group->filter.numSources;
208  group->block.numSources = 0;
209 
210  //List of the sources that the system wishes to hear from
211  for(j = 0; j < group->filter.numSources; j++)
212  {
213  group->allow.sources[j].addr = group->filter.sources[j];
214  group->allow.sources[j].retransmitCount = MLD_ROBUSTNESS_VARIABLE;
215  }
216 
217  //Send a State-Change report immediately
219  }
220  else if(group->filterMode == IP_FILTER_MODE_EXCLUDE)
221  {
222  //The State-Change report will include a TO_EX record
224  group->allow.numSources = 0;
225  group->block.numSources = 0;
226 
227  //Send a State-Change report immediately
229  }
230  else
231  {
232  //Just for sanity
233  }
234 #else
235  //Once a valid link-local address is available, a node should
236  //generate new MLD Report messages for all multicast addresses
237  //joined on the interface (refer to RFC 3590, section 4)
238  if(group->filterMode == IP_FILTER_MODE_EXCLUDE)
239  {
240  //The State-Change report will include a TO_EX record
242  //Send a State-Change report immediately
244  }
245 #endif
246  //Enter the Idle Listener state
248  }
249  }
250  else if(group->state == MLD_NODE_GROUP_STATE_IDLE_LISTENER)
251  {
252  //Check whether the group timer has expired
253  if(netTimerExpired(&group->timer))
254  {
255  //Send Current-State report message
256  mldNodeSendCurrentStateReport(context, &group->addr);
257 
258  //Stop group timer
259  netStopTimer(&group->timer);
260  }
261  }
262  else
263  {
264  //Just for sanity
265  }
266  }
267 
268  //If the expired timer is the retransmission timer, then the State-Change
269  //report is retransmitted
271  {
272  //Retransmit the State-Change report message
274 
275  //Retransmission state needs to be maintained until [Robustness
276  //Variable] State-Change reports have been sent by the host
277  if(mldNodeGetRetransmitStatus(context))
278  {
279  //Select a value in the range 0 - Unsolicited Report Interval
281  //Restart retransmission timer
282  netStartTimer(&context->stateChangeReportTimer, delay);
283  }
284  else
285  {
286  //[Robustness Variable] State-Change reports have been sent by the
287  //host
289  }
290 
291  //Delete groups in "non-existent" state
292  mldNodeFlushUnusedGroups(context);
293  }
294  }
295 }
296 
297 
298 /**
299  * @brief Process multicast reception state change
300  * @param[in] context Pointer to the MLD node context
301  * @param[in] groupAddr Multicast group address
302  * @param[in] newFilterMode New filter mode for the affected group
303  * @param[in] newFilter New interface state for the affected group
304  **/
305 
307  IpFilterMode newFilterMode, const Ipv6SrcAddrList *newFilter)
308 {
309  systime_t delay;
310  MldNodeGroup *group;
311  NetInterface *interface;
312 
313  //Point to the underlying network interface
314  interface = context->interface;
315 
316  //Search the list of groups for the specified multicast address
317  group = mldNodeFindGroup(context, groupAddr);
318 
319  //Check whether the interface has reception state for that group address
320  if(newFilterMode == IP_FILTER_MODE_EXCLUDE || newFilter->numSources > 0)
321  {
322  //No matching group found?
323  if(group == NULL)
324  {
325  //Create a new group
326  group = mldNodeCreateGroup(context, groupAddr);
327 
328  //Entry successfully created?
329  if(group != NULL)
330  {
331  //Valid link-local address assigned to the interface?
332  if(interface->linkState &&
334  {
335  //Check host compatibility mode
336  if(context->compatibilityMode == MLD_VERSION_1)
337  {
338  //When a node starts listening to a multicast address on an
339  //interface, it should immediately transmit an unsolicited
340  //Report for that address on that interface
341  mldNodeSendListenerReport(context, &group->addr);
342 
343  //Start delay timer
345 
346  //Set flag
347  group->flag = TRUE;
348  //Enter the Delaying Listener state
350  }
351  else
352  {
353  //Enter the Idle Listener state
354  group->state = MLD_NODE_GROUP_STATE_IDLE_LISTENER;
355  }
356  }
357  else
358  {
359  //Clear flag
360  group->flag = FALSE;
361  //Enter the Init Listener state
362  group->state = MLD_NODE_GROUP_STATE_INIT_LISTENER;
363  }
364  }
365  }
366  }
367 
368  //Valid group?
369  if(group != NULL)
370  {
371  //Any state change detected?
372  if(group->filterMode != newFilterMode ||
373  !ipv6CompareSrcAddrLists(&group->filter, newFilter))
374  {
375  //Merge the difference report resulting from the state change and the
376  //pending report
377  mldNodeMergeReports(group, newFilterMode, newFilter);
378 
379  //Save the new state
380  group->filterMode = newFilterMode;
381  group->filter = *newFilter;
382 
383  //Check host compatibility mode
384  if(context->compatibilityMode == MLD_VERSION_1)
385  {
386  //The "non-existent" state is considered to have a filter mode of
387  //INCLUDE and an empty source list
388  if(group->filterMode == IP_FILTER_MODE_INCLUDE &&
389  group->filter.numSources == 0)
390  {
391  //Send a Multicast Listener Done message if the flag is set
392  if(group->flag)
393  {
394  mldNodeSendListenerDone(context, &group->addr);
395  }
396 
397  //Delete the group
398  mldNodeDeleteGroup(group);
399  }
400  }
401  else
402  {
403  //Check group state
404  if(group->state == MLD_NODE_GROUP_STATE_INIT_LISTENER)
405  {
406  //The "non-existent" state is considered to have a filter mode
407  //of INCLUDE and an empty source list
408  if(group->filterMode == IP_FILTER_MODE_INCLUDE &&
409  group->filter.numSources == 0)
410  {
411  //Delete the group
412  mldNodeDeleteGroup(group);
413  }
414  }
415  else
416  {
417  //Send a State-Change report message
419 
420  //To cover the possibility of the State-Change report being
421  //missed by one or more multicast routers, it is retransmitted
422  //[Robustness Variable] - 1 more times
423  if(mldNodeGetRetransmitStatus(context))
424  {
425  //Select a value in the range 0 - Unsolicited Report Interval
427  //Start retransmission timer
428  netStartTimer(&context->stateChangeReportTimer, delay);
429  }
430  else
431  {
432  //[Robustness Variable] State-Change reports have been sent
433  //by the host
435  }
436 
437  //Delete groups in "non-existent" state
438  mldNodeFlushUnusedGroups(context);
439  }
440  }
441  }
442  }
443 }
444 
445 
446 /**
447  * @brief Callback function for link change event
448  * @param[in] context Pointer to the MLD node context
449  **/
450 
452 {
453  uint_t i;
454  MldNodeGroup *group;
455 
456  //The default host compatibility mode is MLDv2
457  context->compatibilityMode = MLD_VERSION_2;
458 
459  //Stop timers
461  netStopTimer(&context->generalQueryTimer);
463 
464  //Loop through multicast groups
465  for(i = 0; i < IPV6_MULTICAST_FILTER_SIZE; i++)
466  {
467  //Point to the current group
468  group = &context->groups[i];
469 
470  //Valid group?
472  {
473  //Reset parameters
474  group->flag = FALSE;
475  group->retransmitCount = 0;
476 
477 #if (IPV6_MAX_MULTICAST_SOURCES > 0)
478  //Clear source lists
479  group->allow.numSources = 0;
480  group->block.numSources = 0;
481  group->queriedSources.numSources = 0;
482 #endif
483  //Stop delay timer
484  netStopTimer(&group->timer);
485 
486  //Enter the Init Listener state
488  }
489  }
490 
491  //Delete groups in "non-existent" state
492  mldNodeFlushUnusedGroups(context);
493 }
494 
495 #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:766
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 difference the 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:53
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:92
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:306
systime_t mldGetRandomDelay(systime_t maxDelay)
Generate a random delay.
Definition: mld_common.c:269
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:780
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:451
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:50
#define osMemset(p, value, length)
Definition: os_port.h:135
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:806
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