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