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-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  * 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.6.0
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  //Pointer to the TCP/IP stack context
74  context->netContext = interface->netContext;
75  //Underlying network interface
76  context->interface = interface;
77  //The default host compatibility mode is IGMPv3
79 
80  //In order to switch gracefully between versions of IGMP, hosts keep both
81  //an IGMPv1 Querier Present timer and an IGMPv2 Querier Present timer per
82  //interface (refer to RFC 3376, section 7.2.1)
85 
86  //A timer per interface is used for scheduling responses to General Queries
88 
89  //A timer is used to retransmit State-Change reports
91 
92  //Successful initialization
93  return NO_ERROR;
94 }
95 
96 
97 /**
98  * @brief IGMP host timer handler
99  *
100  * This routine must be periodically called by the TCP/IP stack to
101  * handle IGMP related timers
102  *
103  * @param[in] context Pointer to the IGMP host context
104  **/
105 
107 {
108  uint_t i;
109  systime_t delay;
110  IgmpHostGroup *group;
111  NetInterface *interface;
112 
113  //Point to the underlying network interface
114  interface = context->interface;
115 
116  //In order to be compatible with older version routers, IGMPv3 hosts must
117  //operate in version 1 and version 2 compatibility modes (refer to RFC 3376,
118  //section 7.2.1)
120  {
121  //Stop IGMPv1 Querier Present timer
123 
124  //Check whether IGMPv2 Querier Present timer is running
126  {
127  //When the IGMPv1 Querier Present timer expires, a host switches to
128  //Host Compatibility mode of IGMPv2 if it has a running IGMPv2
129  //Querier Present timer
131  }
132  else
133  {
134  //If it does not have a running IGMPv2 Querier Present timer then it
135  //switches to Host Compatibility of IGMPv3
137  }
138  }
139  else if(netTimerExpired(&context->igmpv2QuerierPresentTimer))
140  {
141  //Stop IGMPv2 Querier Present timer
143 
144  //Check whether IGMPv1 Querier Present timer is running
146  {
147  //The Host Compatibility Mode is set IGMPv1 when the IGMPv1 Querier
148  //Present timer is running
149  }
150  else
151  {
152  //When the IGMPv2 Querier Present timer expires, a host switches to
153  //Host Compatibility mode of IGMPv3
155  }
156  }
157  else
158  {
159  //Just for sanity
160  }
161 
162  //Check host compatibility mode
163  if(context->compatibilityMode <= IGMP_VERSION_2)
164  {
165  //Loop through multicast groups
166  for(i = 0; i < IPV4_MULTICAST_FILTER_SIZE; i++)
167  {
168  //Point to the current group
169  group = &context->groups[i];
170 
171  //Check group state
173  {
174  //Valid IPv4 address assigned to the interface?
175  if(interface->linkState && ipv4IsHostAddrValid(interface))
176  {
177  //When a host joins a multicast group, it should immediately
178  //transmit an unsolicited Membership Report for that group
179  igmpHostSendMembershipReport(context, group->addr);
180 
181  //Start delay timer
183 
184  //Set flag
185  group->flag = TRUE;
186  //Enter the Delaying Member state
188  }
189  }
191  {
192  //Delay timer expired?
193  if(netTimerExpired(&group->timer))
194  {
195  //Send a Membership Report message for the group on the interface
196  igmpHostSendMembershipReport(context, group->addr);
197 
198  //Stop delay timer
199  netStopTimer(&group->timer);
200 
201  //Set flag
202  group->flag = TRUE;
203  //Switch to the Idle Member state
205  }
206  }
207  else
208  {
209  //Just for sanity
210  }
211  }
212  }
213  else
214  {
215  //If the expired timer is the interface timer, then one Current-State
216  //Record is sent for each multicast address for which the specified
217  //interface has reception state
218  if(netTimerExpired(&context->generalQueryTimer))
219  {
220  //Send Current-State report message
222 
223  //Stop interface timer
224  netStopTimer(&context->generalQueryTimer);
225  }
226 
227  //If the expired timer is a group timer, then a single Current-State
228  //Record is sent for the corresponding group address
229  for(i = 0; i < IPV4_MULTICAST_FILTER_SIZE; i++)
230  {
231  //Point to the current group
232  group = &context->groups[i];
233 
234  //Check group state
236  {
237  //Valid IPv4 address assigned to the interface?
238  if(interface->linkState && ipv4IsHostAddrValid(interface))
239  {
240 #if (IPV4_MAX_MULTICAST_SOURCES > 0)
241  //Once a valid address is available, a node should generate new
242  //IGMP Report messages for all multicast addresses joined on the
243  //interface
244  if(group->filterMode == IP_FILTER_MODE_INCLUDE &&
245  group->filter.numSources > 0)
246  {
247  uint_t j;
248 
249  //The State-Change report will include an ALLOW record
250  group->retransmitCount = 0;
251  group->allow.numSources = group->filter.numSources;
252  group->block.numSources = 0;
253 
254  //List of the sources that the system wishes to hear from
255  for(j = 0; j < group->filter.numSources; j++)
256  {
257  group->allow.sources[j].addr = group->filter.sources[j];
258  group->allow.sources[j].retransmitCount = IGMP_ROBUSTNESS_VARIABLE;
259  }
260 
261  //Send a State-Change report immediately
263  }
264  else if(group->filterMode == IP_FILTER_MODE_EXCLUDE)
265  {
266  //The State-Change report will include a TO_EX record
268  group->allow.numSources = 0;
269  group->block.numSources = 0;
270 
271  //Send a State-Change report immediately
273  }
274  else
275  {
276  //Just for sanity
277  }
278 #else
279  //Once a valid address is available, a node should generate new
280  //IGMP Report messages for all multicast addresses joined on the
281  //interface
282  if(group->filterMode == IP_FILTER_MODE_EXCLUDE)
283  {
284  //The State-Change report will include a TO_EX record
286  //Send a State-Change report immediately
288  }
289 #endif
290  //Enter the Idle Member state
292  }
293  }
294  else if(group->state == IGMP_HOST_GROUP_STATE_IDLE_MEMBER)
295  {
296  //Check whether the group timer has expired
297  if(netTimerExpired(&group->timer))
298  {
299  //Send Current-State report message
300  igmpHostSendCurrentStateReport(context, group->addr);
301 
302  //Stop group timer
303  netStopTimer(&group->timer);
304  }
305  }
306  else
307  {
308  //Just for sanity
309  }
310  }
311 
312  //If the expired timer is the retransmission timer, then the State-Change
313  //report is retransmitted
315  {
316  //Retransmit the State-Change report message
318 
319  //Retransmission state needs to be maintained until [Robustness
320  //Variable] State-Change reports have been sent by the host
321  if(igmpHostGetRetransmitStatus(context))
322  {
323  //Select a value in the range 0 - Unsolicited Report Interval
324  delay = igmpGetRandomDelay(context->netContext,
326 
327  //Restart retransmission timer
328  netStartTimer(&context->stateChangeReportTimer, delay);
329  }
330  else
331  {
332  //[Robustness Variable] State-Change reports have been sent by the
333  //host
335  }
336 
337  //Delete groups in "non-existent" state
338  igmpHostFlushUnusedGroups(context);
339  }
340  }
341 }
342 
343 
344 /**
345  * @brief Process multicast reception state change
346  * @param[in] context Pointer to the IGMP host context
347  * @param[in] groupAddr Multicast group address
348  * @param[in] newFilterMode New filter mode for the affected group
349  * @param[in] newFilter New interface state for the affected group
350  **/
351 
353  IpFilterMode newFilterMode, const Ipv4SrcAddrList *newFilter)
354 {
355  systime_t delay;
356  IgmpHostGroup *group;
357  NetInterface *interface;
358 
359  //Point to the underlying network interface
360  interface = context->interface;
361 
362  //Search the list of groups for the specified multicast address
363  group = igmpHostFindGroup(context, groupAddr);
364 
365  //Check whether the interface has reception state for that group address
366  if(newFilterMode == IP_FILTER_MODE_EXCLUDE || newFilter->numSources > 0)
367  {
368  //No matching group found?
369  if(group == NULL)
370  {
371  //Create a new group
372  group = igmpHostCreateGroup(context, groupAddr);
373 
374  //Entry successfully created?
375  if(group != NULL)
376  {
377  //Valid IPv4 address assigned to the interface?
378  if(interface->linkState && ipv4IsHostAddrValid(interface))
379  {
380  //Check host compatibility mode
381  if(context->compatibilityMode <= IGMP_VERSION_2)
382  {
383  //When a host joins a multicast group, it should immediately
384  //transmit an unsolicited Membership Report for that group
385  igmpHostSendMembershipReport(context, group->addr);
386 
387  //Start delay timer
389 
390  //Set flag
391  group->flag = TRUE;
392  //Enter the Delaying Member state
394  }
395  else
396  {
397  //Enter the Idle Member state
398  group->state = IGMP_HOST_GROUP_STATE_IDLE_MEMBER;
399  }
400  }
401  else
402  {
403  //Clear flag
404  group->flag = FALSE;
405  //Enter the Init Member state
406  group->state = IGMP_HOST_GROUP_STATE_INIT_MEMBER;
407  }
408  }
409  }
410  }
411 
412  //Valid group?
413  if(group != NULL)
414  {
415  //Any state change detected?
416  if(group->filterMode != newFilterMode ||
417  !ipv4CompareSrcAddrLists(&group->filter, newFilter))
418  {
419  //Merge the difference report resulting from the state change and the
420  //pending report
421  igmpHostMergeReports(group, newFilterMode, newFilter);
422 
423  //Save the new state
424  group->filterMode = newFilterMode;
425  group->filter = *newFilter;
426 
427  //Check host compatibility mode
428  if(context->compatibilityMode <= IGMP_VERSION_2)
429  {
430  //The "non-existent" state is considered to have a filter mode of
431  //INCLUDE and an empty source list
432  if(group->filterMode == IP_FILTER_MODE_INCLUDE &&
433  group->filter.numSources == 0)
434  {
435  //Send a Leave Group message if the flag is set
436  if(group->flag)
437  {
438  igmpHostSendLeaveGroup(context, group->addr);
439  }
440 
441  //Delete the group
442  igmpHostDeleteGroup(group);
443  }
444  }
445  else
446  {
447  //Check group state
448  if(group->state == IGMP_HOST_GROUP_STATE_INIT_MEMBER)
449  {
450  //The "non-existent" state is considered to have a filter mode
451  //of INCLUDE and an empty source list
452  if(group->filterMode == IP_FILTER_MODE_INCLUDE &&
453  group->filter.numSources == 0)
454  {
455  //Delete the group
456  igmpHostDeleteGroup(group);
457  }
458  }
459  else
460  {
461  //Send a State-Change report message
463 
464  //To cover the possibility of the State-Change report being
465  //missed by one or more multicast routers, it is retransmitted
466  //[Robustness Variable] - 1 more times
467  if(igmpHostGetRetransmitStatus(context))
468  {
469  //Select a value in the range 0 - Unsolicited Report Interval
470  delay = igmpGetRandomDelay(context->netContext,
472 
473  //Start retransmission timer
474  netStartTimer(&context->stateChangeReportTimer, delay);
475  }
476  else
477  {
478  //[Robustness Variable] State-Change reports have been sent
479  //by the host
481  }
482 
483  //Delete groups in "non-existent" state
484  igmpHostFlushUnusedGroups(context);
485  }
486  }
487  }
488  }
489 }
490 
491 
492 /**
493  * @brief Process link state change
494  * @param[in] context Pointer to the IGMP host context
495  **/
496 
498 {
499  uint_t i;
500  IgmpHostGroup *group;
501 
502  //The default host compatibility mode is IGMPv3
504 
505  //Stop timers
508  netStopTimer(&context->generalQueryTimer);
510 
511  //Loop through multicast groups
512  for(i = 0; i < IPV4_MULTICAST_FILTER_SIZE; i++)
513  {
514  //Point to the current group
515  group = &context->groups[i];
516 
517  //Valid group?
519  {
520  //Reset parameters
521  group->flag = FALSE;
522  group->retransmitCount = 0;
523 
524 #if (IPV4_MAX_MULTICAST_SOURCES > 0)
525  //Clear source lists
526  group->allow.numSources = 0;
527  group->block.numSources = 0;
528  group->queriedSources.numSources = 0;
529 #endif
530  //Stop delay timer
531  netStopTimer(&group->timer);
532 
533  //Enter the Init Member state
535  }
536  }
537 
538  //Delete groups in "non-existent" state
539  igmpHostFlushUnusedGroups(context);
540 }
541 
542 #endif
Multicast group.
Definition: igmp_host.h:93
IpFilterMode
Multicast filter mode.
Definition: ip.h:67
void netStartTimer(NetTimer *timer, systime_t interval)
Start timer.
Definition: net_misc.c:798
void igmpHostTick(IgmpHostContext *context)
IGMP host timer handler.
Definition: igmp_host.c:106
Source address list.
Definition: ipv4.h:424
bool_t netTimerRunning(NetTimer *timer)
Check whether the timer is running.
Definition: net_misc.c:825
IgmpHostGroupState state
Multicast group state.
Definition: igmp_host.h:94
NetInterface * interface
Underlying network interface.
Definition: igmp_host.h:116
#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:122
uint32_t Ipv4Addr
IPv4 network address.
Definition: ipv4.h:322
NetTimer stateChangeReportTimer
Retransmission timer for state-change reports.
Definition: igmp_host.h:121
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:497
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:812
IgmpHostGroup * igmpHostCreateGroup(IgmpHostContext *context, Ipv4Addr groupAddr)
Create a new multicast group.
#define NetInterface
Definition: net.h:40
void igmpHostSendCurrentStateReport(IgmpHostContext *context, Ipv4Addr groupAddr)
Send Current-State Report message.
uint_t numSources
Number of source addresses.
Definition: ipv4.h:425
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:403
@ IGMP_HOST_GROUP_STATE_NON_MEMBER
Definition: igmp_host.h:57
IGMP host.
#define IGMP_UNSOLICITED_REPORT_INTERVAL
Definition: igmp_common.h:107
systime_t igmpGetRandomDelay(NetContext *context, systime_t maxDelay)
Generate a random delay.
Definition: igmp_common.c:373
#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:120
@ IGMP_VERSION_2
Definition: igmp_common.h:163
#define IGMP_V3_UNSOLICITED_REPORT_INTERVAL
Definition: igmp_common.h:128
NetContext * netContext
TCP/IP stack context.
Definition: igmp_host.h:115
NetTimer igmpv2QuerierPresentTimer
IGMPv2 querier present timer.
Definition: igmp_host.h:119
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:352
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:118
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:117
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:838
@ 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:128
#define IPV4_MULTICAST_FILTER_SIZE
Definition: ipv4.h:94