igmp_common.c
Go to the documentation of this file.
1 /**
2  * @file igmp_common.c
3  * @brief Definitions common to IGMP host, router and snooping switch
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2010-2025 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 to any
30  * immediately-neighboring multicast routers. Refer to the following RFCs for
31  * complete details:
32  * - RFC 1112: Host Extensions for IP Multicasting
33  * - RFC 2236: Internet Group Management Protocol, Version 2
34  * - RFC 3376: Internet Group Management Protocol, Version 3
35  * - RFC 4541: Considerations for IGMP and MLD Snooping Switches
36  * - RFC 9776: Internet Group Management Protocol, Version 3
37  *
38  * @author Oryx Embedded SARL (www.oryx-embedded.com)
39  * @version 2.5.2
40  **/
41 
42 //Switch to the appropriate trace level
43 #define TRACE_LEVEL IGMP_TRACE_LEVEL
44 
45 //Dependencies
46 #include "core/net.h"
47 #include "ipv4/ipv4_multicast.h"
48 #include "ipv4/ipv4_misc.h"
49 #include "igmp/igmp_host.h"
50 #include "igmp/igmp_host_misc.h"
51 #include "igmp/igmp_router.h"
52 #include "igmp/igmp_router_misc.h"
53 #include "igmp/igmp_snooping.h"
55 #include "igmp/igmp_common.h"
56 #include "igmp/igmp_debug.h"
57 #include "debug.h"
58 
59 //Check TCP/IP stack configuration
60 #if (IPV4_SUPPORT == ENABLED && (IGMP_HOST_SUPPORT == ENABLED || \
61  IGMP_ROUTER_SUPPORT == ENABLED || IGMP_SNOOPING_SUPPORT == ENABLED))
62 
63 //Tick counter to handle periodic operations
65 
66 
67 /**
68  * @brief IGMP initialization
69  * @param[in] interface Underlying network interface
70  * @return Error code
71  **/
72 
74 {
75  error_t error;
76 
77  //The all-systems multicast address, 224.0.0.1, is handled as a special
78  //case. On all systems (hosts and routers), reception of packets destined
79  //to the all-systems multicast address is permanently enabled on all
80  //interfaces on which multicast reception is supported
82  //Any error to report?
83  if(error)
84  return error;
85 
86 #if (IGMP_HOST_SUPPORT == ENABLED)
87  //IGMP host initialization
88  error = igmpHostInit(interface);
89  //Any error to report?
90  if(error)
91  return error;
92 #endif
93 
94  //Successful initialization
95  return NO_ERROR;
96 }
97 
98 
99 /**
100  * @brief IGMP timer handler
101  *
102  * This routine must be periodically called by the TCP/IP stack to
103  * handle IGMP related timers
104  *
105  * @param[in] interface Underlying network interface
106  **/
107 
108 void igmpTick(NetInterface *interface)
109 {
110 #if (IGMP_HOST_SUPPORT == ENABLED)
111  //Manage IGMP host timers
112  igmpHostTick(&interface->igmpHostContext);
113 #endif
114 
115 #if (IGMP_ROUTER_SUPPORT == ENABLED)
116  //Valid IGMP router context?
117  if(interface->igmpRouterContext != NULL)
118  {
119  //Manage IGMP router timers
120  igmpRouterTick(interface->igmpRouterContext);
121  }
122 #endif
123 
124 #if (IGMP_SNOOPING_SUPPORT == ENABLED)
125  //Valid IGMP snooping switch context?
126  if(interface->igmpSnoopingContext != NULL)
127  {
128  //Manage IGMP snooping switch timers
129  igmpSnoopingTick(interface->igmpSnoopingContext);
130  }
131 #endif
132 }
133 
134 
135 /**
136  * @brief Callback function for link change event
137  * @param[in] interface Underlying network interface
138  **/
139 
141 {
142 #if (IGMP_HOST_SUPPORT == ENABLED)
143  //Notify the IGMP host of link state changes
144  igmpHostLinkChangeEvent(&interface->igmpHostContext);
145 #endif
146 }
147 
148 
149 /**
150  * @brief Send IGMP message
151  * @param[in] interface Underlying network interface
152  * @param[in] destAddr Destination IP address
153  * @param[in] buffer Multi-part buffer containing the payload
154  * @param[in] offset Offset to the first byte of the payload
155  * @return Error code
156  **/
157 
159  NetBuffer *buffer, size_t offset)
160 {
161  error_t error;
162  size_t length;
165  Ipv4PseudoHeader pseudoHeader;
166 
167  //Retrieve the length of payload
168  length = netBufferGetLength(buffer) - offset;
169 
170  //Point to the beginning of the IGMP message
171  message = netBufferAt(buffer, offset, length);
172  //Sanity check
173  if(message == NULL)
174  return ERROR_FAILURE;
175 
176  //Select the source IPv4 address to use
177  error = ipv4SelectSourceAddr(&interface, destAddr, &srcIpAddr);
178 
179  //Check status code
180  if(!error)
181  {
182  pseudoHeader.srcAddr = srcIpAddr;
183  }
184  else
185  {
186  pseudoHeader.srcAddr = IPV4_UNSPECIFIED_ADDR;
187  }
188 
189  //Format IPv4 pseudo header
190  pseudoHeader.destAddr = destAddr;
191  pseudoHeader.reserved = 0;
192  pseudoHeader.protocol = IPV4_PROTOCOL_IGMP;
193  pseudoHeader.length = htons(length);
194 
195  //Debug message
196  TRACE_INFO("Sending IGMP message (%" PRIuSIZE " bytes)...\r\n", length);
197  //Dump message contents for debugging purpose
199 
200 #if (IGMP_SNOOPING_SUPPORT == ENABLED)
201  //Valid IGMP snooping switch context?
202  if(interface->igmpSnoopingContext != NULL)
203  {
204  NetRxAncillary ancillary;
205 
206  //Additional options can be passed to the stack along with the packet
207  ancillary = NET_DEFAULT_RX_ANCILLARY;
208  //Specify ingress port
209  ancillary.port = SWITCH_CPU_PORT;
210 
211  //Forward the message to the IGMP snooping switch
212  igmpSnoopingProcessMessage(interface->igmpSnoopingContext, &pseudoHeader,
213  message, length, &ancillary);
214 
215  //Sucessful processing
216  error = NO_ERROR;
217  }
218  else
219 #endif
220  {
221  NetTxAncillary ancillary;
222 
223  //Additional options can be passed to the stack along with the packet
224  ancillary = NET_DEFAULT_TX_ANCILLARY;
225 
226  //All IGMP messages are sent with an IP TTL of 1 and contain an IP Router
227  //Alert option in their IP header (refer to RFC 2236, section 2)
228  ancillary.ttl = IGMP_TTL;
229  ancillary.routerAlert = TRUE;
230 
231  //Every IGMPv3 message is sent with an IP Precedence of Internetwork
232  //Control (refer to RFC 3376, section 4)
233  if(message->type == IGMP_TYPE_MEMBERSHIP_QUERY &&
234  length >= sizeof(IgmpMembershipQueryV3))
235  {
237  }
238  else if(message->type == IGMP_TYPE_MEMBERSHIP_REPORT_V3)
239  {
241  }
242  else
243  {
244  ancillary.tos = 0;
245  }
246 
247  //Send the IGMP message
248  error = ipv4SendDatagram(interface, &pseudoHeader, buffer, offset,
249  &ancillary);
250  }
251 
252 #if (IGMP_HOST_SUPPORT == ENABLED && IGMP_ROUTER_SUPPORT == ENABLED)
253  //Check IGMP message type
255  {
256  //Forward Membership Query messages to the IGMP host
257  igmpHostProcessMessage(&interface->igmpHostContext, &pseudoHeader,
258  message, length);
259  }
260  else if(message->type == IGMP_TYPE_MEMBERSHIP_REPORT_V1 ||
263  {
264  //Valid IGMP router context?
265  if(interface->igmpRouterContext != NULL)
266  {
267  //Forward Membership Report and Leave Group messages to the IGMP router
268  igmpRouterProcessMessage(interface->igmpRouterContext, &pseudoHeader,
269  message, length);
270  }
271  }
272  else
273  {
274  //Just for sanity
275  }
276 #endif
277 
278  //Return status code
279  return error;
280 }
281 
282 
283 /**
284  * @brief Process incoming IGMP message
285  * @param[in] interface Underlying network interface
286  * @param[in] pseudoHeader IPv4 pseudo header
287  * @param[in] buffer Multi-part buffer containing the incoming IGMP message
288  * @param[in] offset Offset to the first byte of the IGMP message
289  * @param[in] ancillary Additional options passed to the stack along with
290  * the packet
291  **/
292 
294  const Ipv4PseudoHeader *pseudoHeader, const NetBuffer *buffer,
295  size_t offset, const NetRxAncillary *ancillary)
296 {
297  size_t length;
298  const IgmpMessage *message;
299 
300  //Retrieve the length of the IGMP message
301  length = netBufferGetLength(buffer) - offset;
302 
303  //To be valid, an IGMP message must be at least 8 octets long
304  if(length < sizeof(IgmpMessage))
305  return;
306 
307  //Point to the beginning of the IGMP message
308  message = netBufferAt(buffer, offset, length);
309  //Sanity check
310  if(message == NULL)
311  return;
312 
313  //Debug message
314  TRACE_INFO("IGMP message received (%" PRIuSIZE " bytes)...\r\n", length);
315 
316 #if (ETH_PORT_TAGGING_SUPPORT == ENABLED)
317  //Dump switch port identifier
318  if(ancillary->port != 0)
319  {
320  TRACE_INFO(" Switch Port = %" PRIu8 "\r\n", ancillary->port);
321  }
322 #endif
323 
324  //Dump message contents for debugging purpose
326 
327  //Verify checksum value
328  if(ipCalcChecksumEx(buffer, offset, length) != 0x0000)
329  {
330  //Debug message
331  TRACE_WARNING("Wrong IGMP header checksum!\r\n");
332  //Drop incoming message
333  return;
334  }
335 
336  //All IGMP messages are sent with an IP TTL of 1
337  if(ancillary->ttl != IGMP_TTL)
338  return;
339 
340 #if (IGMP_HOST_SUPPORT == ENABLED)
341  //Pass the message to the IGMP host
342  igmpHostProcessMessage(&interface->igmpHostContext, pseudoHeader, message,
343  length);
344 #endif
345 
346 #if (IGMP_ROUTER_SUPPORT == ENABLED)
347  //Valid IGMP router context?
348  if(interface->igmpRouterContext != NULL)
349  {
350  //Pass the message to the IGMP router
351  igmpRouterProcessMessage(interface->igmpRouterContext, pseudoHeader,
352  message, length);
353  }
354 #endif
355 
356 #if (IGMP_SNOOPING_SUPPORT == ENABLED)
357  //Valid IGMP snooping switch context?
358  if(interface->igmpSnoopingContext != NULL)
359  {
360  //Pass the message to the IGMP snooping switch
361  igmpSnoopingProcessMessage(interface->igmpSnoopingContext, pseudoHeader,
362  message, length, ancillary);
363  }
364 #endif
365 }
366 
367 
368 /**
369  * @brief Generate a random delay
370  * @param[in] maxDelay maximum delay
371  * @return Random amount of time
372  **/
373 
375 {
376  systime_t delay;
377 
378  //Generate a random delay in the specified range
379  if(maxDelay > IGMP_TICK_INTERVAL)
380  {
381  delay = netGenerateRandRange(0, maxDelay - IGMP_TICK_INTERVAL);
382  }
383  else
384  {
385  delay = 0;
386  }
387 
388  //Return the random value
389  return delay;
390 }
391 
392 
393 /**
394  * @brief Decode a floating-point value
395  * @param[in] code Floating-point representation
396  * @return Decoded value
397  **/
398 
400 {
401  uint8_t exp;
402  uint8_t mant;
403 
404  //Retrieve the value of the exponent
405  exp = (code >> 4) & 0x07;
406  //Retrieve the value of the mantissa
407  mant = code & 0x0F;
408 
409  //The code represents a floating-point value
410  return (mant | 0x10) << (exp + 3);
411 }
412 
413 #endif
systime_t igmpGetRandomDelay(systime_t maxDelay)
Generate a random delay.
Definition: igmp_common.c:374
#define htons(value)
Definition: cpu_endian.h:413
void igmpHostTick(IgmpHostContext *context)
IGMP host timer handler.
Definition: igmp_host.c:104
uint8_t code
Definition: coap_common.h:179
Ipv4Addr destAddr
Definition: ipv4.h:330
uint16_t ipCalcChecksumEx(const NetBuffer *buffer, size_t offset, size_t length)
Calculate IP checksum over a multi-part buffer.
Definition: ip.c:585
const NetTxAncillary NET_DEFAULT_TX_ANCILLARY
Definition: net_misc.c:72
IgmpMessage
Definition: igmp_common.h:215
Helper functions fore IGMP router.
#define SWITCH_CPU_PORT
Definition: nic.h:59
void igmpRouterProcessMessage(IgmpRouterContext *context, const Ipv4PseudoHeader *pseudoHeader, const IgmpMessage *message, size_t length)
Process incoming IGMP message.
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 TRUE
Definition: os_port.h:50
Data logging functions for debugging purpose (IGMP)
error_t ipv4SelectSourceAddr(NetInterface **interface, Ipv4Addr destAddr, Ipv4Addr *srcAddr)
IPv4 source address selection.
Definition: ipv4_misc.c:174
void igmpDumpMessage(const IgmpMessage *message, size_t length)
Dump IGMP message for debugging purpose.
Definition: igmp_debug.c:70
void igmpSnoopingTick(IgmpSnoopingContext *context)
IGMP snooping switch timer handler.
IGMP snooping switch.
@ IGMP_TYPE_MEMBERSHIP_REPORT_V2
Definition: igmp_common.h:176
Ipv4Addr srcIpAddr
Definition: ipcp.h:79
uint32_t Ipv4Addr
IPv4 network address.
Definition: ipv4.h:298
void igmpLinkChangeEvent(NetInterface *interface)
Callback function for link change event.
Definition: igmp_common.c:140
IGMP router.
systime_t igmpTickCounter
Definition: igmp_common.c:64
IPv4 multicast filtering.
Helper functions for IPv4.
@ IGMP_TYPE_MEMBERSHIP_REPORT_V1
Definition: igmp_common.h:175
@ IGMP_TYPE_MEMBERSHIP_QUERY
Definition: igmp_common.h:174
uint32_t netGenerateRandRange(uint32_t min, uint32_t max)
Generate a random value in the specified range.
Definition: net_misc.c:963
void igmpHostLinkChangeEvent(IgmpHostContext *context)
Process link state change.
Definition: igmp_host.c:491
error_t
Error codes.
Definition: error.h:43
#define IGMP_TICK_INTERVAL
Definition: igmp_common.h:39
const NetRxAncillary NET_DEFAULT_RX_ANCILLARY
Definition: net_misc.c:105
@ ERROR_FAILURE
Generic error code.
Definition: error.h:45
#define NetRxAncillary
Definition: net_misc.h:40
#define NetInterface
Definition: net.h:36
error_t igmpSendMessage(NetInterface *interface, Ipv4Addr destAddr, NetBuffer *buffer, size_t offset)
Send IGMP message.
Definition: igmp_common.c:158
#define NetTxAncillary
Definition: net_misc.h:36
@ IGMP_TYPE_MEMBERSHIP_REPORT_V3
Definition: igmp_common.h:178
#define Ipv4PseudoHeader
Definition: ipv4.h:39
#define TRACE_INFO(...)
Definition: debug.h:105
IGMP host.
uint8_t length
Definition: tcp.h:375
size_t netBufferGetLength(const NetBuffer *buffer)
Get the actual length of a multi-part buffer.
Definition: net_mem.c:297
void igmpSnoopingProcessMessage(IgmpSnoopingContext *context, const Ipv4PseudoHeader *pseudoHeader, const IgmpMessage *message, size_t length, const NetRxAncillary *ancillary)
Process incoming IGMP message.
error_t ipv4JoinMulticastGroup(NetInterface *interface, Ipv4Addr groupAddr)
Join the specified host group.
error_t igmpInit(NetInterface *interface)
IGMP initialization.
Definition: igmp_common.c:73
@ IPV4_TOS_PRECEDENCE_INTERNETWORK_CTRL
Definition: ipv4.h:220
#define IGMP_TTL
Definition: igmp_common.h:141
@ IPV4_PROTOCOL_IGMP
Definition: ipv4.h:252
uint32_t systime_t
System time.
uint32_t igmpDecodeFloatingPointValue(uint8_t code)
Decode a floating-point value.
Definition: igmp_common.c:399
#define TRACE_WARNING(...)
Definition: debug.h:93
error_t ipv4SendDatagram(NetInterface *interface, const Ipv4PseudoHeader *pseudoHeader, NetBuffer *buffer, size_t offset, NetTxAncillary *ancillary)
Send an IPv4 datagram.
Definition: ipv4.c:1031
IgmpMembershipQueryV3
Definition: igmp_common.h:240
void igmpHostProcessMessage(IgmpHostContext *context, const Ipv4PseudoHeader *pseudoHeader, const IgmpMessage *message, size_t length)
Process incoming IGMP message.
void igmpRouterTick(IgmpRouterContext *context)
IGMP router timer handler.
Definition: igmp_router.c:220
Helper functions for IGMP host.
error_t igmpHostInit(NetInterface *interface)
IGMP host initialization.
Definition: igmp_host.c:63
void * netBufferAt(const NetBuffer *buffer, size_t offset, size_t length)
Returns a pointer to a data segment.
Definition: net_mem.c:418
Helper functions for IGMP snooping switch.
#define PRIuSIZE
void igmpTick(NetInterface *interface)
IGMP timer handler.
Definition: igmp_common.c:108
TCP/IP stack core.
void igmpProcessMessage(NetInterface *interface, const Ipv4PseudoHeader *pseudoHeader, const NetBuffer *buffer, size_t offset, const NetRxAncillary *ancillary)
Process incoming IGMP message.
Definition: igmp_common.c:293
Definitions common to IGMP host, router and snooping switch.
@ NO_ERROR
Success.
Definition: error.h:44
@ IGMP_TYPE_LEAVE_GROUP
Definition: igmp_common.h:177
Debugging facilities.
#define IPV4_UNSPECIFIED_ADDR
Definition: ipv4.h:117