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-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  * @section Description
28  *
29  * IGMP is used by IP hosts to report their multicast group memberships to any
30  * immediately-neighboring multicast routersRefer 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  *
37  * @author Oryx Embedded SARL (www.oryx-embedded.com)
38  * @version 2.4.4
39  **/
40 
41 //Switch to the appropriate trace level
42 #define TRACE_LEVEL IGMP_TRACE_LEVEL
43 
44 //Dependencies
45 #include "core/net.h"
46 #include "ipv4/ipv4_multicast.h"
47 #include "ipv4/ipv4_misc.h"
48 #include "igmp/igmp_host.h"
49 #include "igmp/igmp_host_misc.h"
50 #include "igmp/igmp_router.h"
51 #include "igmp/igmp_router_misc.h"
52 #include "igmp/igmp_snooping.h"
54 #include "igmp/igmp_common.h"
55 #include "igmp/igmp_debug.h"
56 #include "debug.h"
57 
58 //Check TCP/IP stack configuration
59 #if (IPV4_SUPPORT == ENABLED && (IGMP_HOST_SUPPORT == ENABLED || \
60  IGMP_ROUTER_SUPPORT == ENABLED || IGMP_SNOOPING_SUPPORT == ENABLED))
61 
62 //Tick counter to handle periodic operations
64 
65 
66 /**
67  * @brief IGMP initialization
68  * @param[in] interface Underlying network interface
69  * @return Error code
70  **/
71 
73 {
74  error_t error;
75 
76  //The all-systems multicast address, 224.0.0.1, is handled as a special
77  //case. On all systems (hosts and routers), reception of packets destined
78  //to the all-systems multicast address is permanently enabled on all
79  //interfaces on which multicast reception is supported
81  //Any error to report?
82  if(error)
83  return error;
84 
85 #if (IGMP_HOST_SUPPORT == ENABLED)
86  //IGMP host initialization
87  error = igmpHostInit(interface);
88  //Any error to report?
89  if(error)
90  return error;
91 #endif
92 
93  //Successful initialization
94  return NO_ERROR;
95 }
96 
97 
98 /**
99  * @brief IGMP timer handler
100  *
101  * This routine must be periodically called by the TCP/IP stack to
102  * handle IGMP related timers
103  *
104  * @param[in] interface Underlying network interface
105  **/
106 
107 void igmpTick(NetInterface *interface)
108 {
109 #if (IGMP_HOST_SUPPORT == ENABLED)
110  //Manage IGMP host timers
111  igmpHostTick(&interface->igmpHostContext);
112 #endif
113 
114 #if (IGMP_ROUTER_SUPPORT == ENABLED)
115  //Valid IGMP router context?
116  if(interface->igmpRouterContext != NULL)
117  {
118  //Manage IGMP router timers
119  igmpRouterTick(interface->igmpRouterContext);
120  }
121 #endif
122 
123 #if (IGMP_SNOOPING_SUPPORT == ENABLED)
124  //Valid IGMP snooping switch context?
125  if(interface->igmpSnoopingContext != NULL)
126  {
127  //Manage IGMP snooping switch timers
128  igmpSnoopingTick(interface->igmpSnoopingContext);
129  }
130 #endif
131 }
132 
133 
134 /**
135  * @brief Callback function for link change event
136  * @param[in] interface Underlying network interface
137  **/
138 
140 {
141 #if (IGMP_HOST_SUPPORT == ENABLED)
142  //Notify the IGMP host of link state changes
143  igmpHostLinkChangeEvent(&interface->igmpHostContext);
144 #endif
145 }
146 
147 
148 /**
149  * @brief Send IGMP message
150  * @param[in] interface Underlying network interface
151  * @param[in] destAddr Destination IP address
152  * @param[in] buffer Multi-part buffer containing the payload
153  * @param[in] offset Offset to the first byte of the payload
154  * @return Error code
155  **/
156 
158  NetBuffer *buffer, size_t offset)
159 {
160  error_t error;
161  size_t length;
164  Ipv4PseudoHeader pseudoHeader;
165 
166  //Retrieve the length of payload
167  length = netBufferGetLength(buffer) - offset;
168 
169  //Point to the beginning of the IGMP message
170  message = netBufferAt(buffer, offset, length);
171  //Sanity check
172  if(message == NULL)
173  return ERROR_FAILURE;
174 
175  //Select the source IPv4 address to use
176  error = ipv4SelectSourceAddr(&interface, destAddr, &srcIpAddr);
177 
178  //Check status code
179  if(!error)
180  {
181  pseudoHeader.srcAddr = srcIpAddr;
182  }
183  else
184  {
185  pseudoHeader.srcAddr = IPV4_UNSPECIFIED_ADDR;
186  }
187 
188  //Format IPv4 pseudo header
189  pseudoHeader.destAddr = destAddr;
190  pseudoHeader.reserved = 0;
191  pseudoHeader.protocol = IPV4_PROTOCOL_IGMP;
192  pseudoHeader.length = htons(length);
193 
194  //Debug message
195  TRACE_INFO("Sending IGMP message (%" PRIuSIZE " bytes)...\r\n", length);
196  //Dump message contents for debugging purpose
198 
199 #if (IGMP_SNOOPING_SUPPORT == ENABLED)
200  //Valid IGMP snooping switch context?
201  if(interface->igmpSnoopingContext != NULL)
202  {
203  NetRxAncillary ancillary;
204 
205  //Additional options can be passed to the stack along with the packet
206  ancillary = NET_DEFAULT_RX_ANCILLARY;
207  //Specify ingress port
208  ancillary.port = SWITCH_CPU_PORT;
209 
210  //Forward the message to the IGMP snooping switch
211  igmpSnoopingProcessMessage(interface->igmpSnoopingContext, &pseudoHeader,
212  message, length, &ancillary);
213 
214  //Sucessful processing
215  error = NO_ERROR;
216  }
217  else
218 #endif
219  {
220  NetTxAncillary ancillary;
221 
222  //Additional options can be passed to the stack along with the packet
223  ancillary = NET_DEFAULT_TX_ANCILLARY;
224 
225  //All IGMP messages are sent with an IP TTL of 1 and contain an IP Router
226  //Alert option in their IP header (refer to RFC 2236, section 2)
227  ancillary.ttl = IGMP_TTL;
228  ancillary.routerAlert = TRUE;
229 
230  //Every IGMPv3 message is sent with an IP Precedence of Internetwork
231  //Control (refer to RFC 3376, section 4)
232  if(message->type == IGMP_TYPE_MEMBERSHIP_QUERY &&
233  length >= sizeof(IgmpMembershipQueryV3))
234  {
236  }
237  else if(message->type == IGMP_TYPE_MEMBERSHIP_REPORT_V3)
238  {
240  }
241  else
242  {
243  ancillary.tos = 0;
244  }
245 
246  //Send the IGMP message
247  error = ipv4SendDatagram(interface, &pseudoHeader, buffer, offset,
248  &ancillary);
249  }
250 
251 #if (IGMP_HOST_SUPPORT == ENABLED && IGMP_ROUTER_SUPPORT == ENABLED)
252  //Check IGMP message type
254  {
255  //Forward Membership Query messages to the IGMP host
256  igmpHostProcessMessage(&interface->igmpHostContext, &pseudoHeader,
257  message, length);
258  }
259  else if(message->type == IGMP_TYPE_MEMBERSHIP_REPORT_V1 ||
262  {
263  //Valid IGMP router context?
264  if(interface->igmpRouterContext != NULL)
265  {
266  //Forward Membership Report and Leave Group messages to the IGMP router
267  igmpRouterProcessMessage(interface->igmpRouterContext, &pseudoHeader,
268  message, length);
269  }
270  }
271  else
272  {
273  //Just for sanity
274  }
275 #endif
276 
277  //Return status code
278  return error;
279 }
280 
281 
282 /**
283  * @brief Process incoming IGMP message
284  * @param[in] interface Underlying network interface
285  * @param[in] pseudoHeader IPv4 pseudo header
286  * @param[in] buffer Multi-part buffer containing the incoming IGMP message
287  * @param[in] offset Offset to the first byte of the IGMP message
288  * @param[in] ancillary Additional options passed to the stack along with
289  * the packet
290  **/
291 
293  const Ipv4PseudoHeader *pseudoHeader, const NetBuffer *buffer,
294  size_t offset, const NetRxAncillary *ancillary)
295 {
296  size_t length;
297  const IgmpMessage *message;
298 
299  //Retrieve the length of the IGMP message
300  length = netBufferGetLength(buffer) - offset;
301 
302  //To be valid, an IGMP message must be at least 8 octets long
303  if(length < sizeof(IgmpMessage))
304  return;
305 
306  //Point to the beginning of the IGMP message
307  message = netBufferAt(buffer, offset, length);
308  //Sanity check
309  if(message == NULL)
310  return;
311 
312  //Debug message
313  TRACE_INFO("IGMP message received (%" PRIuSIZE " bytes)...\r\n", length);
314 
315 #if (ETH_PORT_TAGGING_SUPPORT == ENABLED)
316  //Dump switch port identifier
317  if(ancillary->port != 0)
318  {
319  TRACE_INFO(" Switch Port = %" PRIu8 "\r\n", ancillary->port);
320  }
321 #endif
322 
323  //Dump message contents for debugging purpose
325 
326  //Verify checksum value
327  if(ipCalcChecksumEx(buffer, offset, length) != 0x0000)
328  {
329  //Debug message
330  TRACE_WARNING("Wrong IGMP header checksum!\r\n");
331  //Drop incoming message
332  return;
333  }
334 
335  //All IGMP messages are sent with an IP TTL of 1
336  if(ancillary->ttl != IGMP_TTL)
337  return;
338 
339 #if (IGMP_HOST_SUPPORT == ENABLED)
340  //Pass the message to the IGMP host
341  igmpHostProcessMessage(&interface->igmpHostContext, pseudoHeader, message,
342  length);
343 #endif
344 
345 #if (IGMP_ROUTER_SUPPORT == ENABLED)
346  //Valid IGMP router context?
347  if(interface->igmpRouterContext != NULL)
348  {
349  //Pass the message to the IGMP router
350  igmpRouterProcessMessage(interface->igmpRouterContext, pseudoHeader,
351  message, length);
352  }
353 #endif
354 
355 #if (IGMP_SNOOPING_SUPPORT == ENABLED)
356  //Valid IGMP snooping switch context?
357  if(interface->igmpSnoopingContext != NULL)
358  {
359  //Pass the message to the IGMP snooping switch
360  igmpSnoopingProcessMessage(interface->igmpSnoopingContext, pseudoHeader,
361  message, length, ancillary);
362  }
363 #endif
364 }
365 
366 
367 /**
368  * @brief Generate a random delay
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(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
systime_t igmpGetRandomDelay(systime_t maxDelay)
Generate a random delay.
Definition: igmp_common.c:373
#define htons(value)
Definition: cpu_endian.h:413
void igmpHostTick(IgmpHostContext *context)
IGMP host timer handler.
Definition: igmp_host.c:102
uint8_t code
Definition: coap_common.h:179
Ipv4Addr destAddr
Definition: ipv4.h:329
uint16_t ipCalcChecksumEx(const NetBuffer *buffer, size_t offset, size_t length)
Calculate IP checksum over a multi-part buffer.
Definition: ip.c:586
const NetTxAncillary NET_DEFAULT_TX_ANCILLARY
Definition: net_misc.c:71
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:297
void igmpLinkChangeEvent(NetInterface *interface)
Callback function for link change event.
Definition: igmp_common.c:139
IGMP router.
systime_t igmpTickCounter
Definition: igmp_common.c:63
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:948
void igmpHostLinkChangeEvent(IgmpHostContext *context)
Process link state change.
Definition: igmp_host.c:489
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:104
@ 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:157
#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:95
IGMP host.
uint8_t length
Definition: tcp.h:368
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:72
@ IPV4_TOS_PRECEDENCE_INTERNETWORK_CTRL
Definition: ipv4.h:220
#define IGMP_TTL
Definition: igmp_common.h:141
@ IPV4_PROTOCOL_IGMP
Definition: ipv4.h:251
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:85
error_t ipv4SendDatagram(NetInterface *interface, const Ipv4PseudoHeader *pseudoHeader, NetBuffer *buffer, size_t offset, NetTxAncillary *ancillary)
Send an IPv4 datagram.
Definition: ipv4.c:1010
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:61
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:107
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:292
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