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