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