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