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-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  * @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.6.0
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 
64 /**
65  * @brief IGMP initialization
66  * @param[in] interface Underlying network interface
67  * @return Error code
68  **/
69 
71 {
72  error_t error;
73 
74  //The all-systems multicast address, 224.0.0.1, is handled as a special
75  //case. On all systems (hosts and routers), reception of packets destined
76  //to the all-systems multicast address is permanently enabled on all
77  //interfaces on which multicast reception is supported
79  //Any error to report?
80  if(error)
81  return error;
82 
83 #if (IGMP_HOST_SUPPORT == ENABLED)
84  //IGMP host initialization
85  error = igmpHostInit(interface);
86  //Any error to report?
87  if(error)
88  return error;
89 #endif
90 
91  //Successful initialization
92  return NO_ERROR;
93 }
94 
95 
96 /**
97  * @brief IGMP timer handler
98  *
99  * This routine must be periodically called by the TCP/IP stack to
100  * handle IGMP related timers
101  *
102  * @param[in] interface Underlying network interface
103  **/
104 
105 void igmpTick(NetInterface *interface)
106 {
107 #if (IGMP_HOST_SUPPORT == ENABLED)
108  //Manage IGMP host timers
109  igmpHostTick(&interface->igmpHostContext);
110 #endif
111 
112 #if (IGMP_ROUTER_SUPPORT == ENABLED)
113  //Valid IGMP router context?
114  if(interface->igmpRouterContext != NULL)
115  {
116  //Manage IGMP router timers
117  igmpRouterTick(interface->igmpRouterContext);
118  }
119 #endif
120 
121 #if (IGMP_SNOOPING_SUPPORT == ENABLED)
122  //Valid IGMP snooping switch context?
123  if(interface->igmpSnoopingContext != NULL)
124  {
125  //Manage IGMP snooping switch timers
126  igmpSnoopingTick(interface->igmpSnoopingContext);
127  }
128 #endif
129 }
130 
131 
132 /**
133  * @brief Callback function for link change event
134  * @param[in] interface Underlying network interface
135  **/
136 
138 {
139 #if (IGMP_HOST_SUPPORT == ENABLED)
140  //Notify the IGMP host of link state changes
141  igmpHostLinkChangeEvent(&interface->igmpHostContext);
142 #endif
143 }
144 
145 
146 /**
147  * @brief Send IGMP message
148  * @param[in] interface Underlying network interface
149  * @param[in] destAddr Destination IP address
150  * @param[in] buffer Multi-part buffer containing the payload
151  * @param[in] offset Offset to the first byte of the payload
152  * @return Error code
153  **/
154 
156  NetBuffer *buffer, size_t offset)
157 {
158  error_t error;
159  size_t length;
162  Ipv4PseudoHeader pseudoHeader;
163 
164  //Retrieve the length of payload
165  length = netBufferGetLength(buffer) - offset;
166 
167  //Point to the beginning of the IGMP message
168  message = netBufferAt(buffer, offset, length);
169  //Sanity check
170  if(message == NULL)
171  return ERROR_FAILURE;
172 
173  //Select the source IPv4 address to use
174  error = ipv4SelectSourceAddr(interface->netContext, &interface, destAddr,
175  &srcIpAddr);
176 
177  //Check status code
178  if(!error)
179  {
180  pseudoHeader.srcAddr = srcIpAddr;
181  }
182  else
183  {
184  pseudoHeader.srcAddr = IPV4_UNSPECIFIED_ADDR;
185  }
186 
187  //Format IPv4 pseudo header
188  pseudoHeader.destAddr = destAddr;
189  pseudoHeader.reserved = 0;
190  pseudoHeader.protocol = IPV4_PROTOCOL_IGMP;
191  pseudoHeader.length = htons(length);
192 
193  //Debug message
194  TRACE_INFO("Sending IGMP message (%" PRIuSIZE " bytes)...\r\n", length);
195  //Dump message contents for debugging purpose
197 
198 #if (IGMP_SNOOPING_SUPPORT == ENABLED)
199  //Valid IGMP snooping switch context?
200  if(interface->igmpSnoopingContext != NULL)
201  {
202  NetRxAncillary ancillary;
203 
204  //Additional options can be passed to the stack along with the packet
205  ancillary = NET_DEFAULT_RX_ANCILLARY;
206  //Specify ingress port
207  ancillary.port = SWITCH_CPU_PORT;
208 
209  //Forward the message to the IGMP snooping switch
210  igmpSnoopingProcessMessage(interface->igmpSnoopingContext, &pseudoHeader,
211  message, length, &ancillary);
212 
213  //Sucessful processing
214  error = NO_ERROR;
215  }
216  else
217 #endif
218  {
219  NetTxAncillary ancillary;
220 
221  //Additional options can be passed to the stack along with the packet
222  ancillary = NET_DEFAULT_TX_ANCILLARY;
223 
224  //All IGMP messages are sent with an IP TTL of 1 and contain an IP Router
225  //Alert option in their IP header (refer to RFC 2236, section 2)
226  ancillary.ttl = IGMP_TTL;
227  ancillary.routerAlert = TRUE;
228 
229  //Every IGMPv3 message is sent with an IP Precedence of Internetwork
230  //Control (refer to RFC 3376, section 4)
231  if(message->type == IGMP_TYPE_MEMBERSHIP_QUERY &&
232  length >= sizeof(IgmpMembershipQueryV3))
233  {
235  }
236  else if(message->type == IGMP_TYPE_MEMBERSHIP_REPORT_V3)
237  {
239  }
240  else
241  {
242  ancillary.tos = 0;
243  }
244 
245  //Send the IGMP message
246  error = ipv4SendDatagram(interface, &pseudoHeader, buffer, offset,
247  &ancillary);
248  }
249 
250 #if (IGMP_HOST_SUPPORT == ENABLED && IGMP_ROUTER_SUPPORT == ENABLED)
251  //Check IGMP message type
253  {
254  //Forward Membership Query messages to the IGMP host
255  igmpHostProcessMessage(&interface->igmpHostContext, &pseudoHeader,
256  message, length);
257  }
258  else if(message->type == IGMP_TYPE_MEMBERSHIP_REPORT_V1 ||
261  {
262  //Valid IGMP router context?
263  if(interface->igmpRouterContext != NULL)
264  {
265  //Forward Membership Report and Leave Group messages to the IGMP router
266  igmpRouterProcessMessage(interface->igmpRouterContext, &pseudoHeader,
267  message, length);
268  }
269  }
270  else
271  {
272  //Just for sanity
273  }
274 #endif
275 
276  //Return status code
277  return error;
278 }
279 
280 
281 /**
282  * @brief Process incoming IGMP message
283  * @param[in] interface Underlying network interface
284  * @param[in] pseudoHeader IPv4 pseudo header
285  * @param[in] buffer Multi-part buffer containing the incoming IGMP message
286  * @param[in] offset Offset to the first byte of the IGMP message
287  * @param[in] ancillary Additional options passed to the stack along with
288  * the packet
289  **/
290 
292  const Ipv4PseudoHeader *pseudoHeader, const NetBuffer *buffer,
293  size_t offset, const NetRxAncillary *ancillary)
294 {
295  size_t length;
296  const IgmpMessage *message;
297 
298  //Retrieve the length of the IGMP message
299  length = netBufferGetLength(buffer) - offset;
300 
301  //To be valid, an IGMP message must be at least 8 octets long
302  if(length < sizeof(IgmpMessage))
303  return;
304 
305  //Point to the beginning of the IGMP message
306  message = netBufferAt(buffer, offset, length);
307  //Sanity check
308  if(message == NULL)
309  return;
310 
311  //Debug message
312  TRACE_INFO("IGMP message received (%" PRIuSIZE " bytes)...\r\n", length);
313 
314 #if (ETH_PORT_TAGGING_SUPPORT == ENABLED)
315  //Dump switch port identifier
316  if(ancillary->port != 0)
317  {
318  TRACE_INFO(" Switch Port = %" PRIu8 "\r\n", ancillary->port);
319  }
320 #endif
321 
322  //Dump message contents for debugging purpose
324 
325  //Verify checksum value
326  if(ipCalcChecksumEx(buffer, offset, length) != 0x0000)
327  {
328  //Debug message
329  TRACE_WARNING("Wrong IGMP header checksum!\r\n");
330  //Drop incoming message
331  return;
332  }
333 
334  //All IGMP messages are sent with an IP TTL of 1
335  if(ancillary->ttl != IGMP_TTL)
336  return;
337 
338 #if (IGMP_HOST_SUPPORT == ENABLED)
339  //Pass the message to the IGMP host
340  igmpHostProcessMessage(&interface->igmpHostContext, pseudoHeader, message,
341  length);
342 #endif
343 
344 #if (IGMP_ROUTER_SUPPORT == ENABLED)
345  //Valid IGMP router context?
346  if(interface->igmpRouterContext != NULL)
347  {
348  //Pass the message to the IGMP router
349  igmpRouterProcessMessage(interface->igmpRouterContext, pseudoHeader,
350  message, length);
351  }
352 #endif
353 
354 #if (IGMP_SNOOPING_SUPPORT == ENABLED)
355  //Valid IGMP snooping switch context?
356  if(interface->igmpSnoopingContext != NULL)
357  {
358  //Pass the message to the IGMP snooping switch
359  igmpSnoopingProcessMessage(interface->igmpSnoopingContext, pseudoHeader,
360  message, length, ancillary);
361  }
362 #endif
363 }
364 
365 
366 /**
367  * @brief Generate a random delay
368  * @param[in] context Pointer to the TCP/IP stack context
369  * @param[in] maxDelay maximum delay
370  * @return Random amount of time
371  **/
372 
374 {
375  systime_t delay;
376 
377  //Generate a random delay in the specified range
378  if(maxDelay > IGMP_TICK_INTERVAL)
379  {
380  delay = netGenerateRandRange(context, 0, maxDelay - IGMP_TICK_INTERVAL);
381  }
382  else
383  {
384  delay = 0;
385  }
386 
387  //Return the random value
388  return delay;
389 }
390 
391 
392 /**
393  * @brief Decode a floating-point value
394  * @param[in] code Floating-point representation
395  * @return Decoded value
396  **/
397 
399 {
400  uint8_t exp;
401  uint8_t mant;
402 
403  //Retrieve the value of the exponent
404  exp = (code >> 4) & 0x07;
405  //Retrieve the value of the mantissa
406  mant = code & 0x0F;
407 
408  //The code represents a floating-point value
409  return (mant | 0x10) << (exp + 3);
410 }
411 
412 #endif
#define htons(value)
Definition: cpu_endian.h:413
#define NetContext
Definition: net.h:36
void igmpHostTick(IgmpHostContext *context)
IGMP host timer handler.
Definition: igmp_host.c:106
uint8_t code
Definition: coap_common.h:179
Ipv4Addr destAddr
Definition: ipv4.h:354
uint16_t ipCalcChecksumEx(const NetBuffer *buffer, size_t offset, size_t length)
Calculate IP checksum over a multi-part buffer.
Definition: ip.c:591
const NetTxAncillary NET_DEFAULT_TX_ANCILLARY
Definition: net_misc.c:70
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)
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:322
void igmpLinkChangeEvent(NetInterface *interface)
Callback function for link change event.
Definition: igmp_common.c:137
IGMP router.
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
void igmpHostLinkChangeEvent(IgmpHostContext *context)
Process link state change.
Definition: igmp_host.c:497
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:103
@ ERROR_FAILURE
Generic error code.
Definition: error.h:45
error_t ipv4SelectSourceAddr(NetContext *context, NetInterface **interface, Ipv4Addr destAddr, Ipv4Addr *srcAddr)
IPv4 source address selection.
Definition: ipv4_misc.c:173
#define NetRxAncillary
Definition: net_misc.h:40
#define NetInterface
Definition: net.h:40
uint32_t netGenerateRandRange(NetContext *context, uint32_t min, uint32_t max)
Generate a random value in the specified range.
Definition: net_misc.c:983
error_t igmpSendMessage(NetInterface *interface, Ipv4Addr destAddr, NetBuffer *buffer, size_t offset)
Send IGMP message.
Definition: igmp_common.c:155
#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.
systime_t igmpGetRandomDelay(NetContext *context, systime_t maxDelay)
Generate a random delay.
Definition: igmp_common.c:373
error_t igmpInit(NetInterface *interface)
IGMP initialization.
Definition: igmp_common.c:70
@ IPV4_TOS_PRECEDENCE_INTERNETWORK_CTRL
Definition: ipv4.h:244
#define IGMP_TTL
Definition: igmp_common.h:141
@ IPV4_PROTOCOL_IGMP
Definition: ipv4.h:276
uint32_t systime_t
System time.
uint32_t igmpDecodeFloatingPointValue(uint8_t code)
Decode a floating-point value.
Definition: igmp_common.c:398
#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:1021
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:227
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:105
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:291
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:128