igmp_router_misc.c
Go to the documentation of this file.
1 /**
2  * @file igmp_router_misc.c
3  * @brief Helper functions fore IGMP router
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  * @author Oryx Embedded SARL (www.oryx-embedded.com)
28  * @version 2.6.0
29  **/
30 
31 //Switch to the appropriate trace level
32 #define TRACE_LEVEL IGMP_TRACE_LEVEL
33 
34 //Dependencies
35 #include "core/net.h"
36 #include "ipv4/ipv4.h"
37 #include "igmp/igmp_router.h"
38 #include "igmp/igmp_router_misc.h"
39 #include "debug.h"
40 
41 //Check TCP/IP stack configuration
42 #if (IPV4_SUPPORT == ENABLED && IGMP_ROUTER_SUPPORT == ENABLED)
43 
44 
45 /**
46  * @brief Send General Query message
47  * @param[in] context Pointer to the IGMP router context
48  * @return Error code
49  **/
50 
52 {
53  //A General Query is addressed to the all-systems multicast group, has a
54  //Group Address field of zero, and has a Max Response Time of Query Response
55  //Interval
58 }
59 
60 
61 /**
62  * @brief Send Group-Specific Query message
63  * @param[in] context Pointer to the IGMP router context
64  * @param[in] groupAddr Multicast address of the group being queried
65  * @return Error code
66  **/
67 
70 {
71  //The Group-Specific Query is sent to the group being queried, and has a
72  //Max Response Time of Last Member Query Interval
75 }
76 
77 
78 /**
79  * @brief Send Membership Query message
80  * @param[in] context Pointer to the IGMP router context
81  * @param[in] destAddr Destination IP address
82  * @param[in] groupAddr Multicast group address
83  * @param[in] maxRespTime Maximum response time
84  * @return Error code
85  **/
86 
89 {
90  error_t error;
91  size_t offset;
92  NetBuffer *buffer;
94 
95  //Allocate a memory buffer to hold the IGMP message
96  buffer = ipAllocBuffer(sizeof(IgmpMessage), &offset);
97  //Failed to allocate memory?
98  if(buffer == NULL)
99  return ERROR_OUT_OF_MEMORY;
100 
101  //Point to the beginning of the IGMP message
102  message = netBufferAt(buffer, offset, 0);
103 
104  //Format Membership Query message
106  message->checksum = 0;
107 
108  //IGMPv1 compatibility mode?
109  if(context->version == IGMP_VERSION_1)
110  {
111  //When in IGMPv1 mode, routers must send Periodic Queries with a Max
112  //Response Time of 0 (refer to RFC 2236, section 4)
113  message->maxRespTime = 0;
114  }
115  else
116  {
117  //The Max Response Time field is meaningful only in Membership Query
118  //messages, and specifies the maximum allowed time before sending a
119  //responding report in units of 1/10 second
120  message->maxRespTime = (uint8_t) (maxRespTime / 100);
121  }
122 
123  //In a Membership Query message, the group address field is set to zero
124  //when sending a General Query, and set to the group address being queried
125  //when sending a Group-Specific Query
126  message->groupAddr = groupAddr;
127 
128  //Message checksum calculation
129  message->checksum = ipCalcChecksum(&message, sizeof(IgmpMessage));
130 
131  //The Membership Report message is sent to the group being reported
132  error = igmpSendMessage(context->interface, destAddr, buffer, offset);
133 
134  //Free previously allocated memory
135  netBufferFree(buffer);
136 
137  //Return status code
138  return error;
139 }
140 
141 
142 /**
143  * @brief Process incoming IGMP message
144  * @param[in] context Pointer to the IGMP router context
145  * @param[in] pseudoHeader IPv4 pseudo header
146  * @param[in] message Pointer to the incoming IGMP message
147  * @param[in] length Length of the IGMP message, in bytes
148  **/
149 
151  const Ipv4PseudoHeader *pseudoHeader, const IgmpMessage *message,
152  size_t length)
153 {
154  //Check IGMP message type
156  {
157  //Process Membership Query message
158  igmpRouterProcessMembershipQuery(context, pseudoHeader, message, length);
159  }
160  else if(message->type == IGMP_TYPE_MEMBERSHIP_REPORT_V1 ||
162  {
163  //Process Membership Report message
164  igmpRouterProcessMembershipReport(context, pseudoHeader, message, length);
165  }
166  else if(message->type == IGMP_TYPE_LEAVE_GROUP)
167  {
168  //Process Leave Group message
169  igmpRouterProcessLeaveGroup(context, pseudoHeader, message, length);
170  }
171  else
172  {
173  //Discard unrecognized IGMP messages
174  }
175 }
176 
177 
178 /**
179  * @brief Process incoming Membership Query message
180  * @param[in] context Pointer to the IGMP router context
181  * @param[in] pseudoHeader IPv4 pseudo header
182  * @param[in] message Pointer to the incoming IGMP message
183  * @param[in] length Length of the IGMP message, in bytes
184  **/
185 
187  const Ipv4PseudoHeader *pseudoHeader, const IgmpMessage *message,
188  size_t length)
189 {
191  IgmpRouterGroup *group;
192  NetInterface *interface;
193 
194  //The group address in the IGMP header must either be zero or a valid
195  //multicast group address (refer to RFC 2236, section 6)
196  if(message->groupAddr != IPV4_UNSPECIFIED_ADDR &&
197  !ipv4IsMulticastAddr(message->groupAddr))
198  {
199  return;
200  }
201 
202  //Point to the underlying network interface
203  interface = context->interface;
204 
205  //IGMPv1 or IGMPv2 Membership Query message?
206  if(message->maxRespTime == 0)
207  {
208  //The maximum response time is 10 seconds by default
210  }
211  else
212  {
213  //The Max Resp Time field specifies the maximum time allowed before
214  //sending a responding report
215  maxRespTime = message->maxRespTime * 100;
216  }
217 
218  //Valid source address?
219  if(pseudoHeader->srcAddr != IPV4_UNSPECIFIED_ADDR)
220  {
221  //Check whether the IGMP Membership Query is received from a router on
222  //the same network with a lower IP address
223  if(htonl(pseudoHeader->srcAddr) < htonl(interface->ipv4Context.addrList[0].addr))
224  {
225  //Start Other Querier Present timer
226  netStartTimer(&context->otherQuerierPresentTimer,
228 
229  //Switch to the "Non Querier" state
230  context->state = IGMP_ROUTER_STATE_NON_QUERIER;
231  }
232  }
233 
234  //There are two sub-types of Membership Query messages. These two messages
235  //are differentiated by the Group Address (refer to RFC 2236, section 2.1)
236  if(message->groupAddr == IPV4_UNSPECIFIED_ADDR)
237  {
238  //General Queries are used to learn which groups have members on an
239  //attached network
240  }
241  else if(ipv4IsMulticastAddr(message->groupAddr))
242  {
243  //Group-Specific Queries are used to learn if a particular group has any
244  //members on an attached network
245  group = igmpRouterFindGroup(context, message->groupAddr);
246 
247  //Valid multicast group?
248  if(group != NULL)
249  {
250  //Non-Querier router?
251  if(context->state == IGMP_ROUTER_STATE_NON_QUERIER)
252  {
253  //"Members Present" state?
255  {
256  //Non-Queriers do not send any messages and are only driven by
257  //message reception (refer to RFC 2236, section 7)
258  group->lastMemberQueryCount = 0;
259 
260  //Set the timer to [Max Response Time] * [Last Member Query Count]
261  //if this router is a non-Querier
262  netStartTimer(&group->timer, maxRespTime * 100 *
264 
265  //Switch to the "Checking Membership" state
267  }
268  else
269  {
270  //When a non-Querier receives a Group-Specific Query message,
271  //if its existing group membership timer is greater than [Last
272  //Member Query Count] times the Max Response Time specified in
273  //the message, it sets its group membership timer to that value
274  }
275  }
276  }
277  }
278  else
279  {
280  //The group address in the IGMP header must either be zero or a valid
281  //multicast group address (refer to RFC 2236, section 6)
282  }
283 }
284 
285 
286 /**
287  * @brief Process incoming Membership Report message
288  * @param[in] context Pointer to the IGMP router context
289  * @param[in] pseudoHeader IPv4 pseudo header
290  * @param[in] message Pointer to the incoming IGMP message
291  * @param[in] length Length of the IGMP message, in bytes
292  **/
293 
295  const Ipv4PseudoHeader *pseudoHeader, const IgmpMessage *message,
296  size_t length)
297 {
298  IgmpRouterGroup *group;
299 
300  //The group address in the IGMP header must be a valid multicast group
301  //address
302  if(!ipv4IsMulticastAddr(message->groupAddr))
303  return;
304 
305  //In a Membership Report, the group address field holds the IP multicast
306  //group address of the group being reported (refer to RFC 2236, section 2.4)
307  group = igmpRouterFindGroup(context, message->groupAddr);
308 
309  //First report received for this multicast group?
310  if(group == NULL)
311  {
312  //Create a new multicast group
313  group = igmpRouterCreateGroup(context, message->groupAddr);
314  }
315 
316  //Valid multicast group?
317  if(group != NULL)
318  {
319  //Version 1 Membership Report received by a non-Querier router?
320  if(context->state == IGMP_ROUTER_STATE_QUERIER &&
322  {
323  //Start the timer for the group membership
325  //Start IGMPv1 Host timer
327 
328  //Switch to the "V1 Members Present" state
330  }
331  else
332  {
333  //Start the timer for the group membership
335 
336  //"No Members Present", "Members Present" or "Checking Membership" state?
338  {
339  //Switch to the "Members Present" state
341  }
342  }
343  }
344 }
345 
346 
347 /**
348  * @brief Process incoming Leave Group message
349  * @param[in] context Pointer to the IGMP router context
350  * @param[in] pseudoHeader IPv4 pseudo header
351  * @param[in] message Pointer to the incoming IGMP message
352  * @param[in] length Length of the IGMP message, in bytes
353  **/
354 
356  const Ipv4PseudoHeader *pseudoHeader, const IgmpMessage *message,
357  size_t length)
358 {
359  IgmpRouterGroup *group;
360 
361  //Routers should accept a Leave Group message addressed to the group being
362  //left, in order to accommodate implementations of an earlier version of
363  //this standard (refer to RFC 2236, section 3)
364  if(pseudoHeader->destAddr != IGMP_ALL_ROUTERS_ADDR &&
365  pseudoHeader->destAddr != message->groupAddr)
366  {
367  return;
368  }
369 
370  //When in IGMPv1 mode, routers must ignore Leave Group messages (refer to
371  //RFC 2236, section 4)
372  if(context->version == IGMP_VERSION_1)
373  return;
374 
375  //Non-Queriers must ignore Leave Group messages (refer to RFC 2236,
376  //section 3)
377  if(context->state != IGMP_ROUTER_STATE_QUERIER)
378  return;
379 
380  //The group address in the IGMP header must be a valid multicast group
381  //address
382  if(!ipv4IsMulticastAddr(message->groupAddr))
383  return;
384 
385  //In a Leave Group message, the group address field holds the IP multicast
386  //group address of the group being left (refer to RFC 2236, section 2.4)
387  group = igmpRouterFindGroup(context, message->groupAddr);
388 
389  //Queriers should ignore Leave Group messages for which there are no
390  //group members on the reception interface (refer to RFC 2236, section 3)
391  if(group != NULL)
392  {
393  //"Members Present" state?
395  {
396  //When a Querier receives a Leave Group message for a group that has
397  //group members on the reception interface, it sends Group-Specific
398  //Queries to the group being left
399  igmpRouterSendGroupSpecificQuery(context, group->addr);
400 
401  //Start retransmit timer for the group membership
403 
404  //Number of Group-Specific Queries left to sent before the router
405  //assumes there are no local members
407 
408  //Set the timer to [Last Member Query Interval] * [Last Member
409  //Query Count] if this router is a Querier
412 
413  //Switch to the "Checking Membership" state
415  }
416  }
417 }
418 
419 
420 /**
421  * @brief Create a new multicast group
422  * @param[in] context Pointer to the IGMP router context
423  * @param[in] groupAddr Multicast group address
424  * @return Pointer to the newly created multicast group
425  **/
426 
429 {
430  uint_t i;
431  IgmpRouterGroup *group;
432 
433  //Initialize pointer
434  group = NULL;
435 
436  //Loop through multicast groups
437  for(i = 0; i < context->numGroups; i++)
438  {
439  //Check whether the entry is available
440  if(context->groups[i].state == IGMP_ROUTER_GROUP_STATE_NO_MEMBERS_PRESENT)
441  {
442  //Point to the current group
443  group = &context->groups[i];
444  //Save the multicast group address
445  group->addr = groupAddr;
446 
447  //Any registered callback?
448  if(context->addMcastRouteCallback != NULL)
449  {
450  //Notify the routing protocol that there are members of this group
451  //on this connected network
452  context->addMcastRouteCallback(context, group->addr,
453  context->interface);
454  }
455 
456  //We are done
457  break;
458  }
459  }
460 
461  //Return a pointer to the newly created multicast group
462  return group;
463 }
464 
465 
466 /**
467  * @brief Search the list of multicast groups for a given group address
468  * @param[in] context Pointer to the IGMP router context
469  * @param[in] groupAddr Multicast group address
470  * @return Pointer to the matching multicast group, if any
471  **/
472 
475 {
476  uint_t i;
477  IgmpRouterGroup *group;
478 
479  //Initialize pointer
480  group = NULL;
481 
482  //Loop through multicast groups
483  for(i = 0; i < context->numGroups; i++)
484  {
485  //Check whether there are hosts on the network which have sent reports
486  //for this multicast group
487  if(context->groups[i].state != IGMP_ROUTER_GROUP_STATE_NO_MEMBERS_PRESENT &&
488  context->groups[i].addr == groupAddr)
489  {
490  //Point to the current group
491  group = &context->groups[i];
492  break;
493  }
494  }
495 
496  //Return a pointer to the matching multicast group
497  return group;
498 }
499 
500 
501 /**
502  * @brief Delete a multicast group
503  * @param[in] context Pointer to the IGMP router context
504  * @param[in] group Multicast group
505  **/
506 
508 {
509  //Any registered callback?
510  if(context->deleteMcastRouteCallback != NULL)
511  {
512  //Notify the routing protocol that there are no longer any members of
513  //this group on this connected network
514  context->deleteMcastRouteCallback(context, group->addr,
515  context->interface);
516  }
517 
518  //Groups in "No Members Present" state require no storage in the router
520 }
521 
522 #endif
#define ipv4IsMulticastAddr(ipAddr)
Definition: ipv4.h:186
@ IGMP_ROUTER_GROUP_STATE_NO_MEMBERS_PRESENT
Definition: igmp_router.h:73
void netStartTimer(NetTimer *timer, systime_t interval)
Start timer.
Definition: net_misc.c:798
NetBuffer * ipAllocBuffer(size_t length, size_t *offset)
Allocate a buffer to hold an IP packet.
Definition: ip.c:716
IgmpRouterGroup * igmpRouterCreateGroup(IgmpRouterContext *context, Ipv4Addr groupAddr)
Create a new multicast group.
Ipv4Addr destAddr
Definition: ipv4.h:354
error_t igmpRouterSendMembershipQuery(IgmpRouterContext *context, Ipv4Addr destAddr, Ipv4Addr groupAddr, systime_t maxRespTime)
Send Membership Query message.
uint8_t maxRespTime
Definition: igmp_common.h:212
IgmpMessage
Definition: igmp_common.h:215
Helper functions fore IGMP router.
IgmpRouterGroup * igmpRouterFindGroup(IgmpRouterContext *context, Ipv4Addr groupAddr)
Search the list of multicast groups for a given group address.
void igmpRouterProcessMessage(IgmpRouterContext *context, const Ipv4PseudoHeader *pseudoHeader, const IgmpMessage *message, size_t length)
Process incoming IGMP message.
#define IGMP_GROUP_MEMBERSHIP_INTERVAL
Definition: igmp_common.h:66
Ipv4Addr addr
Multicast group address.
Definition: igmp_router.h:103
Structure describing a buffer that spans multiple chunks.
Definition: net_mem.h:89
uint8_t message[]
Definition: chap.h:154
#define IGMP_ALL_SYSTEMS_ADDR
Definition: igmp_common.h:144
#define IGMP_LAST_MEMBER_QUERY_COUNT
Definition: igmp_common.h:96
uint_t lastMemberQueryCount
Number of Group-Specific Queries to be sent.
Definition: igmp_router.h:104
@ IGMP_ROUTER_GROUP_STATE_MEMBERS_PRESENT
Definition: igmp_router.h:74
@ ERROR_OUT_OF_MEMORY
Definition: error.h:63
void igmpRouterProcessLeaveGroup(IgmpRouterContext *context, const Ipv4PseudoHeader *pseudoHeader, const IgmpMessage *message, size_t length)
Process incoming Leave Group message.
#define IGMP_OTHER_QUERIER_PRESENT_INTERVAL
Definition: igmp_common.h:70
@ IGMP_ROUTER_GROUP_STATE_CHECKING_MEMBERSHIP
Definition: igmp_router.h:76
@ IGMP_TYPE_MEMBERSHIP_REPORT_V2
Definition: igmp_common.h:176
uint32_t Ipv4Addr
IPv4 network address.
Definition: ipv4.h:322
@ IGMP_ROUTER_GROUP_STATE_V1_MEMBERS_PRESENT
Definition: igmp_router.h:75
IGMP router.
void igmpRouterDeleteGroup(IgmpRouterContext *context, IgmpRouterGroup *group)
Delete a multicast group.
@ IGMP_TYPE_MEMBERSHIP_REPORT_V1
Definition: igmp_common.h:175
@ IGMP_ROUTER_STATE_QUERIER
Definition: igmp_router.h:62
#define htonl(value)
Definition: cpu_endian.h:414
@ IGMP_TYPE_MEMBERSHIP_QUERY
Definition: igmp_common.h:174
error_t
Error codes.
Definition: error.h:43
NetTimer retransmitTimer
Retransmit timer for the group membership.
Definition: igmp_router.h:107
#define IGMP_ALL_ROUTERS_ADDR
Definition: igmp_common.h:146
error_t igmpRouterSendGeneralQuery(IgmpRouterContext *context)
Send General Query message.
void igmpRouterProcessMembershipReport(IgmpRouterContext *context, const Ipv4PseudoHeader *pseudoHeader, const IgmpMessage *message, size_t length)
Process incoming Membership Report message.
#define NetInterface
Definition: net.h:40
void netBufferFree(NetBuffer *buffer)
Dispose a multi-part buffer.
Definition: net_mem.c:282
IgmpRouterGroupState state
Multicast group state.
Definition: igmp_router.h:102
error_t igmpSendMessage(NetInterface *interface, Ipv4Addr destAddr, NetBuffer *buffer, size_t offset)
Send IGMP message.
Definition: igmp_common.c:155
#define Ipv4PseudoHeader
Definition: ipv4.h:39
uint8_t length
Definition: tcp.h:375
NetTimer v1HostTimer
IGMPv1 Host timer.
Definition: igmp_router.h:106
NetTimer timer
Timer for the group membership.
Definition: igmp_router.h:105
@ IGMP_ROUTER_STATE_NON_QUERIER
Definition: igmp_router.h:63
error_t igmpRouterSendGroupSpecificQuery(IgmpRouterContext *context, Ipv4Addr groupAddr)
Send Group-Specific Query message.
uint32_t systime_t
System time.
#define IGMP_LAST_MEMBER_QUERY_INTERVAL
Definition: igmp_common.h:89
void igmpRouterProcessMembershipQuery(IgmpRouterContext *context, const Ipv4PseudoHeader *pseudoHeader, const IgmpMessage *message, size_t length)
Process incoming Membership Query message.
Ipv4Addr groupAddr
Definition: igmp_common.h:214
@ IGMP_VERSION_1
Definition: igmp_common.h:162
#define IgmpRouterContext
Definition: igmp_router.h:47
void * netBufferAt(const NetBuffer *buffer, size_t offset, size_t length)
Returns a pointer to a data segment.
Definition: net_mem.c:418
#define IGMP_QUERY_RESPONSE_INTERVAL
Definition: igmp_common.h:60
IPv4 (Internet Protocol Version 4)
#define IGMP_V1_MAX_RESPONSE_TIME
Definition: igmp_common.h:121
unsigned int uint_t
Definition: compiler_port.h:57
TCP/IP stack core.
Multicast group.
Definition: igmp_router.h:101
@ IGMP_TYPE_LEAVE_GROUP
Definition: igmp_common.h:177
Debugging facilities.
#define IPV4_UNSPECIFIED_ADDR
Definition: ipv4.h:128
uint16_t ipCalcChecksum(const void *data, size_t length)
IP checksum calculation.
Definition: ip.c:471