igmp_host.c
Go to the documentation of this file.
1 /**
2  * @file igmp_host.c
3  * @brief IGMP host
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  * IGMP is used by IP hosts to report their multicast group memberships to any
30  * immediately-neighboring multicast routers. Refer to the following RFCs for
31  * complete details:
32  * - RFC 1112: Host Extensions for IP Multicasting
33  * - RFC 2236: Internet Group Management Protocol, Version 2
34  * - RFC 3376: Internet Group Management Protocol, Version 3
35  * - RFC 9776: Internet Group Management Protocol, Version 3
36  *
37  * @author Oryx Embedded SARL (www.oryx-embedded.com)
38  * @version 2.5.2
39  **/
40 
41 //Switch to the appropriate trace level
42 #define TRACE_LEVEL IGMP_TRACE_LEVEL
43 
44 //Dependencies
45 #include "core/net.h"
46 #include "ipv4/ipv4.h"
47 #include "ipv4/ipv4_multicast.h"
48 #include "ipv4/ipv4_misc.h"
49 #include "igmp/igmp_host.h"
50 #include "igmp/igmp_host_misc.h"
51 #include "debug.h"
52 
53 //Check TCP/IP stack configuration
54 #if (IPV4_SUPPORT == ENABLED && IGMP_HOST_SUPPORT == ENABLED)
55 
56 
57 /**
58  * @brief IGMP host initialization
59  * @param[in] interface Underlying network interface
60  * @return Error code
61  **/
62 
64 {
65  IgmpHostContext *context;
66 
67  //Point to the IGMP host context
68  context = &interface->igmpHostContext;
69 
70  //Clear the IGMP host context
71  osMemset(context, 0, sizeof(IgmpHostContext));
72 
73  //Underlying network interface
74  context->interface = interface;
75  //The default host compatibility mode is IGMPv3
77 
78  //In order to switch gracefully between versions of IGMP, hosts keep both
79  //an IGMPv1 Querier Present timer and an IGMPv2 Querier Present timer per
80  //interface (refer to RFC 3376, section 7.2.1)
83 
84  //A timer per interface is used for scheduling responses to General Queries
86 
87  //A timer is used to retransmit State-Change reports
89 
90  //Successful initialization
91  return NO_ERROR;
92 }
93 
94 
95 /**
96  * @brief IGMP host timer handler
97  *
98  * This routine must be periodically called by the TCP/IP stack to
99  * handle IGMP related timers
100  *
101  * @param[in] context Pointer to the IGMP host context
102  **/
103 
105 {
106  uint_t i;
107  systime_t delay;
108  IgmpHostGroup *group;
109  NetInterface *interface;
110 
111  //Point to the underlying network interface
112  interface = context->interface;
113 
114  //In order to be compatible with older version routers, IGMPv3 hosts must
115  //operate in version 1 and version 2 compatibility modes (refer to RFC 3376,
116  //section 7.2.1)
118  {
119  //Stop IGMPv1 Querier Present timer
121 
122  //Check whether IGMPv2 Querier Present timer is running
124  {
125  //When the IGMPv1 Querier Present timer expires, a host switches to
126  //Host Compatibility mode of IGMPv2 if it has a running IGMPv2
127  //Querier Present timer
129  }
130  else
131  {
132  //If it does not have a running IGMPv2 Querier Present timer then it
133  //switches to Host Compatibility of IGMPv3
135  }
136  }
137  else if(netTimerExpired(&context->igmpv2QuerierPresentTimer))
138  {
139  //Stop IGMPv2 Querier Present timer
141 
142  //Check whether IGMPv1 Querier Present timer is running
144  {
145  //The Host Compatibility Mode is set IGMPv1 when the IGMPv1 Querier
146  //Present timer is running
147  }
148  else
149  {
150  //When the IGMPv2 Querier Present timer expires, a host switches to
151  //Host Compatibility mode of IGMPv3
153  }
154  }
155  else
156  {
157  //Just for sanity
158  }
159 
160  //Check host compatibility mode
161  if(context->compatibilityMode <= IGMP_VERSION_2)
162  {
163  //Loop through multicast groups
164  for(i = 0; i < IPV4_MULTICAST_FILTER_SIZE; i++)
165  {
166  //Point to the current group
167  group = &context->groups[i];
168 
169  //Check group state
171  {
172  //Valid IPv4 address assigned to the interface?
173  if(interface->linkState && ipv4IsHostAddrValid(interface))
174  {
175  //When a host joins a multicast group, it should immediately
176  //transmit an unsolicited Membership Report for that group
177  igmpHostSendMembershipReport(context, group->addr);
178 
179  //Start delay timer
181 
182  //Set flag
183  group->flag = TRUE;
184  //Enter the Delaying Member state
186  }
187  }
189  {
190  //Delay timer expired?
191  if(netTimerExpired(&group->timer))
192  {
193  //Send a Membership Report message for the group on the interface
194  igmpHostSendMembershipReport(context, group->addr);
195 
196  //Stop delay timer
197  netStopTimer(&group->timer);
198 
199  //Set flag
200  group->flag = TRUE;
201  //Switch to the Idle Member state
203  }
204  }
205  else
206  {
207  //Just for sanity
208  }
209  }
210  }
211  else
212  {
213  //If the expired timer is the interface timer, then one Current-State
214  //Record is sent for each multicast address for which the specified
215  //interface has reception state
216  if(netTimerExpired(&context->generalQueryTimer))
217  {
218  //Send Current-State report message
220 
221  //Stop interface timer
222  netStopTimer(&context->generalQueryTimer);
223  }
224 
225  //If the expired timer is a group timer, then a single Current-State
226  //Record is sent for the corresponding group address
227  for(i = 0; i < IPV4_MULTICAST_FILTER_SIZE; i++)
228  {
229  //Point to the current group
230  group = &context->groups[i];
231 
232  //Check group state
234  {
235  //Valid IPv4 address assigned to the interface?
236  if(interface->linkState && ipv4IsHostAddrValid(interface))
237  {
238 #if (IPV4_MAX_MULTICAST_SOURCES > 0)
239  //Once a valid address is available, a node should generate new
240  //IGMP Report messages for all multicast addresses joined on the
241  //interface
242  if(group->filterMode == IP_FILTER_MODE_INCLUDE &&
243  group->filter.numSources > 0)
244  {
245  uint_t j;
246 
247  //The State-Change report will include an ALLOW record
248  group->retransmitCount = 0;
249  group->allow.numSources = group->filter.numSources;
250  group->block.numSources = 0;
251 
252  //List of the sources that the system wishes to hear from
253  for(j = 0; j < group->filter.numSources; j++)
254  {
255  group->allow.sources[j].addr = group->filter.sources[j];
256  group->allow.sources[j].retransmitCount = IGMP_ROBUSTNESS_VARIABLE;
257  }
258 
259  //Send a State-Change report immediately
261  }
262  else if(group->filterMode == IP_FILTER_MODE_EXCLUDE)
263  {
264  //The State-Change report will include a TO_EX record
266  group->allow.numSources = 0;
267  group->block.numSources = 0;
268 
269  //Send a State-Change report immediately
271  }
272  else
273  {
274  //Just for sanity
275  }
276 #else
277  //Once a valid address is available, a node should generate new
278  //IGMP Report messages for all multicast addresses joined on the
279  //interface
280  if(group->filterMode == IP_FILTER_MODE_EXCLUDE)
281  {
282  //The State-Change report will include a TO_EX record
284  //Send a State-Change report immediately
286  }
287 #endif
288  //Enter the Idle Member state
290  }
291  }
292  else if(group->state == IGMP_HOST_GROUP_STATE_IDLE_MEMBER)
293  {
294  //Check whether the group timer has expired
295  if(netTimerExpired(&group->timer))
296  {
297  //Send Current-State report message
298  igmpHostSendCurrentStateReport(context, group->addr);
299 
300  //Stop group timer
301  netStopTimer(&group->timer);
302  }
303  }
304  else
305  {
306  //Just for sanity
307  }
308  }
309 
310  //If the expired timer is the retransmission timer, then the State-Change
311  //report is retransmitted
313  {
314  //Retransmit the State-Change report message
316 
317  //Retransmission state needs to be maintained until [Robustness
318  //Variable] State-Change reports have been sent by the host
319  if(igmpHostGetRetransmitStatus(context))
320  {
321  //Select a value in the range 0 - Unsolicited Report Interval
323  //Restart retransmission timer
324  netStartTimer(&context->stateChangeReportTimer, delay);
325  }
326  else
327  {
328  //[Robustness Variable] State-Change reports have been sent by the
329  //host
331  }
332 
333  //Delete groups in "non-existent" state
334  igmpHostFlushUnusedGroups(context);
335  }
336  }
337 }
338 
339 
340 /**
341  * @brief Process multicast reception state change
342  * @param[in] context Pointer to the IGMP host context
343  * @param[in] groupAddr Multicast group address
344  * @param[in] newFilterMode New filter mode for the affected group
345  * @param[in] newFilter New interface state for the affected group
346  **/
347 
349  IpFilterMode newFilterMode, const Ipv4SrcAddrList *newFilter)
350 {
351  systime_t delay;
352  IgmpHostGroup *group;
353  NetInterface *interface;
354 
355  //Point to the underlying network interface
356  interface = context->interface;
357 
358  //Search the list of groups for the specified multicast address
359  group = igmpHostFindGroup(context, groupAddr);
360 
361  //Check whether the interface has reception state for that group address
362  if(newFilterMode == IP_FILTER_MODE_EXCLUDE || newFilter->numSources > 0)
363  {
364  //No matching group found?
365  if(group == NULL)
366  {
367  //Create a new group
368  group = igmpHostCreateGroup(context, groupAddr);
369 
370  //Entry successfully created?
371  if(group != NULL)
372  {
373  //Valid IPv4 address assigned to the interface?
374  if(interface->linkState && ipv4IsHostAddrValid(interface))
375  {
376  //Check host compatibility mode
377  if(context->compatibilityMode <= IGMP_VERSION_2)
378  {
379  //When a host joins a multicast group, it should immediately
380  //transmit an unsolicited Membership Report for that group
381  igmpHostSendMembershipReport(context, group->addr);
382 
383  //Start delay timer
385 
386  //Set flag
387  group->flag = TRUE;
388  //Enter the Delaying Member state
390  }
391  else
392  {
393  //Enter the Idle Member state
394  group->state = IGMP_HOST_GROUP_STATE_IDLE_MEMBER;
395  }
396  }
397  else
398  {
399  //Clear flag
400  group->flag = FALSE;
401  //Enter the Init Member state
402  group->state = IGMP_HOST_GROUP_STATE_INIT_MEMBER;
403  }
404  }
405  }
406  }
407 
408  //Valid group?
409  if(group != NULL)
410  {
411  //Any state change detected?
412  if(group->filterMode != newFilterMode ||
413  !ipv4CompareSrcAddrLists(&group->filter, newFilter))
414  {
415  //Merge the difference report resulting from the state change and the
416  //pending report
417  igmpHostMergeReports(group, newFilterMode, newFilter);
418 
419  //Save the new state
420  group->filterMode = newFilterMode;
421  group->filter = *newFilter;
422 
423  //Check host compatibility mode
424  if(context->compatibilityMode <= IGMP_VERSION_2)
425  {
426  //The "non-existent" state is considered to have a filter mode of
427  //INCLUDE and an empty source list
428  if(group->filterMode == IP_FILTER_MODE_INCLUDE &&
429  group->filter.numSources == 0)
430  {
431  //Send a Leave Group message if the flag is set
432  if(group->flag)
433  {
434  igmpHostSendLeaveGroup(context, group->addr);
435  }
436 
437  //Delete the group
438  igmpHostDeleteGroup(group);
439  }
440  }
441  else
442  {
443  //Check group state
444  if(group->state == IGMP_HOST_GROUP_STATE_INIT_MEMBER)
445  {
446  //The "non-existent" state is considered to have a filter mode
447  //of INCLUDE and an empty source list
448  if(group->filterMode == IP_FILTER_MODE_INCLUDE &&
449  group->filter.numSources == 0)
450  {
451  //Delete the group
452  igmpHostDeleteGroup(group);
453  }
454  }
455  else
456  {
457  //Send a State-Change report message
459 
460  //To cover the possibility of the State-Change report being
461  //missed by one or more multicast routers, it is retransmitted
462  //[Robustness Variable] - 1 more times
463  if(igmpHostGetRetransmitStatus(context))
464  {
465  //Select a value in the range 0 - Unsolicited Report Interval
467  //Start retransmission timer
468  netStartTimer(&context->stateChangeReportTimer, delay);
469  }
470  else
471  {
472  //[Robustness Variable] State-Change reports have been sent
473  //by the host
475  }
476 
477  //Delete groups in "non-existent" state
478  igmpHostFlushUnusedGroups(context);
479  }
480  }
481  }
482  }
483 }
484 
485 
486 /**
487  * @brief Process link state change
488  * @param[in] context Pointer to the IGMP host context
489  **/
490 
492 {
493  uint_t i;
494  IgmpHostGroup *group;
495 
496  //The default host compatibility mode is IGMPv3
498 
499  //Stop timers
502  netStopTimer(&context->generalQueryTimer);
504 
505  //Loop through multicast groups
506  for(i = 0; i < IPV4_MULTICAST_FILTER_SIZE; i++)
507  {
508  //Point to the current group
509  group = &context->groups[i];
510 
511  //Valid group?
513  {
514  //Reset parameters
515  group->flag = FALSE;
516  group->retransmitCount = 0;
517 
518 #if (IPV4_MAX_MULTICAST_SOURCES > 0)
519  //Clear source lists
520  group->allow.numSources = 0;
521  group->block.numSources = 0;
522  group->queriedSources.numSources = 0;
523 #endif
524  //Stop delay timer
525  netStopTimer(&group->timer);
526 
527  //Enter the Init Member state
529  }
530  }
531 
532  //Delete groups in "non-existent" state
533  igmpHostFlushUnusedGroups(context);
534 }
535 
536 #endif
Multicast group.
Definition: igmp_host.h:93
systime_t igmpGetRandomDelay(systime_t maxDelay)
Generate a random delay.
Definition: igmp_common.c:374
IpFilterMode
Multicast filter mode.
Definition: ip.h:67
void netStartTimer(NetTimer *timer, systime_t interval)
Start timer.
Definition: net_misc.c:781
void igmpHostTick(IgmpHostContext *context)
IGMP host timer handler.
Definition: igmp_host.c:104
Source address list.
Definition: ipv4.h:400
bool_t netTimerRunning(NetTimer *timer)
Check whether the timer is running.
Definition: net_misc.c:808
IgmpHostGroupState state
Multicast group state.
Definition: igmp_host.h:94
NetInterface * interface
Underlying network interface.
Definition: igmp_host.h:115
#define TRUE
Definition: os_port.h:50
void igmpHostChangeCompatibilityMode(IgmpHostContext *context, IgmpVersion compatibilityMode)
Change host compatibility mode.
bool_t ipv4CompareSrcAddrLists(const Ipv4SrcAddrList *list1, const Ipv4SrcAddrList *list2)
Compare lists of sources.
void igmpHostMergeReports(IgmpHostGroup *group, IpFilterMode newFilterMode, const Ipv4SrcAddrList *newFilter)
Merge the difference report and the pending report.
@ IP_FILTER_MODE_EXCLUDE
Definition: ip.h:68
bool_t igmpHostGetRetransmitStatus(IgmpHostContext *context)
Get the retransmission status of the State-Change report.
IgmpHostGroup groups[IPV4_MULTICAST_FILTER_SIZE]
Multicast groups.
Definition: igmp_host.h:121
uint32_t Ipv4Addr
IPv4 network address.
Definition: ipv4.h:298
NetTimer stateChangeReportTimer
Retransmission timer for state-change reports.
Definition: igmp_host.h:120
void igmpHostSendStateChangeReport(IgmpHostContext *context)
Send State-Change Report message.
IpFilterMode filterMode
Filter mode.
Definition: igmp_host.h:99
IGMP host context.
Definition: igmp_host.h:114
IPv4 multicast filtering.
Helper functions for IPv4.
#define FALSE
Definition: os_port.h:46
void igmpHostLinkChangeEvent(IgmpHostContext *context)
Process link state change.
Definition: igmp_host.c:491
error_t
Error codes.
Definition: error.h:43
@ IGMP_HOST_GROUP_STATE_DELAYING_MEMBER
Definition: igmp_host.h:59
void netStopTimer(NetTimer *timer)
Stop timer.
Definition: net_misc.c:795
IgmpHostGroup * igmpHostCreateGroup(IgmpHostContext *context, Ipv4Addr groupAddr)
Create a new multicast group.
#define NetInterface
Definition: net.h:36
void igmpHostSendCurrentStateReport(IgmpHostContext *context, Ipv4Addr groupAddr)
Send Current-State Report message.
uint_t numSources
Number of source addresses.
Definition: ipv4.h:401
Ipv4Addr addr
Multicast group address.
Definition: igmp_host.h:95
void igmpHostFlushUnusedGroups(IgmpHostContext *context)
Delete groups in "non-existent" state.
bool_t ipv4IsHostAddrValid(NetInterface *interface)
Check whether a valid IPv4 address has been assigned to the interface.
Definition: ipv4_misc.c:404
@ IGMP_HOST_GROUP_STATE_NON_MEMBER
Definition: igmp_host.h:57
IGMP host.
#define IGMP_UNSOLICITED_REPORT_INTERVAL
Definition: igmp_common.h:107
#define IGMP_ROBUSTNESS_VARIABLE
Definition: igmp_common.h:46
uint32_t systime_t
System time.
NetTimer generalQueryTimer
Timer for scheduling responses to general queries.
Definition: igmp_host.h:119
@ IGMP_VERSION_2
Definition: igmp_common.h:163
#define IGMP_V3_UNSOLICITED_REPORT_INTERVAL
Definition: igmp_common.h:128
NetTimer igmpv2QuerierPresentTimer
IGMPv2 querier present timer.
Definition: igmp_host.h:118
void igmpHostSendLeaveGroup(IgmpHostContext *context, Ipv4Addr groupAddr)
Send Leave Group message.
bool_t flag
We are the last host to send a report for this group.
Definition: igmp_host.h:96
Ipv4Addr groupAddr
Definition: igmp_common.h:214
@ IGMP_VERSION_3
Definition: igmp_common.h:164
void igmpHostStateChangeEvent(IgmpHostContext *context, Ipv4Addr groupAddr, IpFilterMode newFilterMode, const Ipv4SrcAddrList *newFilter)
Process multicast reception state change.
Definition: igmp_host.c:348
void igmpHostDeleteGroup(IgmpHostGroup *group)
Delete a multicast group.
@ IP_FILTER_MODE_INCLUDE
Definition: ip.h:69
Helper functions for IGMP host.
IgmpHostGroup * igmpHostFindGroup(IgmpHostContext *context, Ipv4Addr groupAddr)
Search the list of multicast groups for a given group address.
void igmpHostSendMembershipReport(IgmpHostContext *context, Ipv4Addr groupAddr)
Send Membership Report message.
NetTimer igmpv1QuerierPresentTimer
IGMPv1 querier present timer.
Definition: igmp_host.h:117
NetTimer timer
Report delay timer.
Definition: igmp_host.h:98
error_t igmpHostInit(NetInterface *interface)
IGMP host initialization.
Definition: igmp_host.c:63
@ IGMP_HOST_GROUP_STATE_INIT_MEMBER
Definition: igmp_host.h:58
IPv4 (Internet Protocol Version 4)
IgmpVersion compatibilityMode
Host compatibility mode.
Definition: igmp_host.h:116
uint_t retransmitCount
Filter mode retransmission counter.
Definition: igmp_host.h:97
unsigned int uint_t
Definition: compiler_port.h:57
#define osMemset(p, value, length)
Definition: os_port.h:138
TCP/IP stack core.
bool_t netTimerExpired(NetTimer *timer)
Check whether the timer has expired.
Definition: net_misc.c:821
@ NO_ERROR
Success.
Definition: error.h:44
@ IGMP_HOST_GROUP_STATE_IDLE_MEMBER
Definition: igmp_host.h:60
Debugging facilities.
Ipv4SrcAddrList filter
Current-state record.
Definition: igmp_host.h:100
#define IPV4_UNSPECIFIED_ADDR
Definition: ipv4.h:117
#define IPV4_MULTICAST_FILTER_SIZE
Definition: ipv4.h:83