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  * @author Oryx Embedded SARL (www.oryx-embedded.com)
28  * @version 2.4.0
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 "igmp/igmp_host.h"
37 #include "igmp/igmp_host_misc.h"
38 #include "igmp/igmp_router.h"
39 #include "igmp/igmp_router_misc.h"
40 #include "igmp/igmp_snooping.h"
42 #include "igmp/igmp_common.h"
43 #include "debug.h"
44 
45 //Check TCP/IP stack configuration
46 #if (IPV4_SUPPORT == ENABLED && (IGMP_HOST_SUPPORT == ENABLED || \
47  IGMP_ROUTER_SUPPORT == ENABLED || IGMP_SNOOPING_SUPPORT == ENABLED))
48 
49 //Tick counter to handle periodic operations
51 
52 
53 /**
54  * @brief IGMP initialization
55  * @param[in] interface Underlying network interface
56  * @return Error code
57  **/
58 
60 {
61  error_t error;
62 
63  //Join the all-systems group
65  //Any error to report?
66  if(error)
67  return error;
68 
69 #if (IGMP_HOST_SUPPORT == ENABLED)
70  //IGMP host initialization
71  error = igmpHostInit(interface);
72  //Any error to report?
73  if(error)
74  return error;
75 #endif
76 
77  //Successful initialization
78  return NO_ERROR;
79 }
80 
81 
82 /**
83  * @brief IGMP timer handler
84  *
85  * This routine must be periodically called by the TCP/IP stack to
86  * handle IGMP related timers
87  *
88  * @param[in] interface Underlying network interface
89  **/
90 
91 void igmpTick(NetInterface *interface)
92 {
93 #if (IGMP_HOST_SUPPORT == ENABLED)
94  //Manage IGMP host timers
95  igmpHostTick(interface);
96 #endif
97 
98 #if (IGMP_ROUTER_SUPPORT == ENABLED)
99  //Valid IGMP router context?
100  if(interface->igmpRouterContext != NULL)
101  {
102  //Manage IGMP router timers
103  igmpRouterTick(interface->igmpRouterContext);
104  }
105 #endif
106 
107 #if (IGMP_SNOOPING_SUPPORT == ENABLED)
108  //Valid IGMP snooping switch context?
109  if(interface->igmpSnoopingContext != NULL)
110  {
111  //Manage IGMP snooping switch timers
112  igmpSnoopingTick(interface->igmpSnoopingContext);
113  }
114 #endif
115 }
116 
117 
118 /**
119  * @brief Callback function for link change event
120  * @param[in] interface Underlying network interface
121  **/
122 
124 {
125 #if (IGMP_HOST_SUPPORT == ENABLED)
126  //Notify the IGMP host of link state changes
127  igmpHostLinkChangeEvent(interface);
128 #endif
129 }
130 
131 
132 /**
133  * @brief Send IGMP message
134  * @param[in] interface Underlying network interface
135  * @param[in] destAddr Destination IP address
136  * @param[in] message Pointer to the IGMP message
137  * @param[in] length Length of the IGMP message, in bytes
138  * @return Error code
139  **/
140 
142  const IgmpMessage *message, size_t length)
143 {
144  error_t error;
145  Ipv4PseudoHeader pseudoHeader;
146 
147  //Initialize status code
148  error = NO_ERROR;
149 
150  //Format IPv4 pseudo header
151  pseudoHeader.srcAddr = interface->ipv4Context.addrList[0].addr;
152  pseudoHeader.destAddr = destAddr;
153  pseudoHeader.reserved = 0;
154  pseudoHeader.protocol = IPV4_PROTOCOL_IGMP;
155  pseudoHeader.length = htons(length);
156 
157  //Debug message
158  TRACE_INFO("Sending IGMP message (%" PRIuSIZE " bytes)...\r\n", length);
159  //Dump message contents for debugging purpose
161 
162 #if (IGMP_SNOOPING_SUPPORT == ENABLED)
163  //Valid IGMP snooping switch context?
164  if(interface->igmpSnoopingContext != NULL)
165  {
166  NetRxAncillary ancillary;
167 
168  //Additional options can be passed to the stack along with the packet
169  ancillary = NET_DEFAULT_RX_ANCILLARY;
170  //Specify ingress port
171  ancillary.port = SWITCH_CPU_PORT;
172 
173  //Forward the message to the IGMP snooping switch
174  igmpSnoopingProcessMessage(interface->igmpSnoopingContext, &pseudoHeader,
175  message, length, &ancillary);
176  }
177  else
178 #endif
179  {
180  size_t offset;
181  NetBuffer *buffer;
182  NetTxAncillary ancillary;
183 
184  //Allocate a memory buffer to hold an IGMP message
185  buffer = ipAllocBuffer(0, &offset);
186 
187  //Successful memory allocation?
188  if(buffer != NULL)
189  {
190  //Copy the IGMP message
191  error = netBufferAppend(buffer, message, length);
192 
193  //Check status code
194  if(!error)
195  {
196  //Additional options can be passed to the stack along with the packet
197  ancillary = NET_DEFAULT_TX_ANCILLARY;
198 
199  //All IGMP messages are sent with an IP TTL of 1 and contain an IP Router
200  //Alert option in their IP header (refer to RFC 2236, section 2)
201  ancillary.ttl = IGMP_TTL;
202  ancillary.routerAlert = TRUE;
203 
204  //Send the IGMP message
205  error = ipv4SendDatagram(interface, &pseudoHeader, buffer, offset,
206  &ancillary);
207  }
208 
209  //Free previously allocated memory
210  netBufferFree(buffer);
211  }
212  else
213  {
214  //Failed to allocate memory
215  error = ERROR_OUT_OF_MEMORY;
216  }
217  }
218 
219 #if (IGMP_HOST_SUPPORT == ENABLED && IGMP_ROUTER_SUPPORT == ENABLED)
220  //Check IGMP message type
222  {
223  //Forward Membership Query messages to the IGMP host
225  }
226  else if(message->type == IGMP_TYPE_MEMBERSHIP_REPORT_V1 ||
229  {
230  //Valid IGMP router context?
231  if(interface->igmpRouterContext != NULL)
232  {
233  //Forward Membership Report and Leave Group messages to the IGMP router
234  igmpRouterProcessMessage(interface->igmpRouterContext, &pseudoHeader,
235  message, length);
236  }
237  }
238  else
239  {
240  //Just for sanity
241  }
242 #endif
243 
244  //Return status code
245  return error;
246 }
247 
248 
249 /**
250  * @brief Process incoming IGMP message
251  * @param[in] interface Underlying network interface
252  * @param[in] pseudoHeader IPv4 pseudo header
253  * @param[in] buffer Multi-part buffer containing the incoming IGMP message
254  * @param[in] offset Offset to the first byte of the IGMP message
255  * @param[in] ancillary Additional options passed to the stack along with
256  * the packet
257  **/
258 
260  const Ipv4PseudoHeader *pseudoHeader, const NetBuffer *buffer,
261  size_t offset, const NetRxAncillary *ancillary)
262 {
263  size_t length;
264  const IgmpMessage *message;
265 
266  //Retrieve the length of the IGMP message
267  length = netBufferGetLength(buffer) - offset;
268 
269  //To be valid, an IGMP message must be at least 8 octets long
270  if(length < sizeof(IgmpMessage))
271  {
272  //Debug message
273  TRACE_WARNING("IGMP message length is invalid!\r\n");
274  //Silently discard incoming message
275  return;
276  }
277 
278  //Point to the beginning of the IGMP message
279  message = netBufferAt(buffer, offset);
280  //Sanity check
281  if(message == NULL)
282  return;
283 
284  //Debug message
285  TRACE_INFO("IGMP message received (%" PRIuSIZE " bytes)...\r\n", length);
286 
287 #if (ETH_PORT_TAGGING_SUPPORT == ENABLED)
288  //Dump switch port identifier
289  if(ancillary->port != 0)
290  {
291  TRACE_INFO(" Switch Port = %" PRIu8 "\r\n", ancillary->port);
292  }
293 #endif
294 
295  //Dump message contents for debugging purpose
297 
298  //Verify checksum value
299  if(ipCalcChecksumEx(buffer, offset, length) != 0x0000)
300  {
301  //Debug message
302  TRACE_WARNING("Wrong IGMP header checksum!\r\n");
303  //Drop incoming message
304  return;
305  }
306 
307 #if (IGMP_HOST_SUPPORT == ENABLED)
308  //Pass the message to the IGMP host
310 #endif
311 
312 #if (IGMP_ROUTER_SUPPORT == ENABLED)
313  //Valid IGMP router context?
314  if(interface->igmpRouterContext != NULL)
315  {
316  //Pass the message to the IGMP router
317  igmpRouterProcessMessage(interface->igmpRouterContext, pseudoHeader,
318  message, length);
319  }
320 #endif
321 
322 #if (IGMP_SNOOPING_SUPPORT == ENABLED)
323  //Valid IGMP snooping switch context?
324  if(interface->igmpSnoopingContext != NULL)
325  {
326  //Pass the message to the IGMP snooping switch
327  igmpSnoopingProcessMessage(interface->igmpSnoopingContext, pseudoHeader,
328  message, length, ancillary);
329  }
330 #endif
331 }
332 
333 
334 /**
335  * @brief Dump IGMP message for debugging purpose
336  * @param[in] message Pointer to the IGMP message
337  **/
338 
340 {
341 #if (TRACE_LEVEL >= TRACE_LEVEL_DEBUG)
342  const char_t *label;
343 
344  //Check IGMP message type
346  {
347  label = "Membership Query";
348  }
349  else if(message->type == IGMP_TYPE_MEMBERSHIP_REPORT_V1)
350  {
351  label = "Version 1 Membership Report";
352  }
353  else if(message->type == IGMP_TYPE_MEMBERSHIP_REPORT_V2)
354  {
355  label = "Version 2 Membership Report";
356  }
357  else if(message->type == IGMP_TYPE_MEMBERSHIP_REPORT_V3)
358  {
359  label = "Version 3 Membership Report";
360  }
361  else if(message->type == IGMP_TYPE_LEAVE_GROUP)
362  {
363  label = "Leave Group";
364  }
365  else
366  {
367  label = "Unknown";
368  }
369 
370  //Dump IGMP message
371  TRACE_DEBUG(" Type = 0x%02" PRIX8 " (%s)\r\n", message->type, label);
372  TRACE_DEBUG(" Max Resp Time = %" PRIu8 "\r\n", message->maxRespTime);
373  TRACE_DEBUG(" Checksum = 0x%04" PRIX16 "\r\n", ntohs(message->checksum));
374  TRACE_DEBUG(" Group Address = %s\r\n", ipv4AddrToString(message->groupAddr, NULL));
375 #endif
376 }
377 
378 #endif
uint8_t message[]
Definition: chap.h:154
#define PRIuSIZE
char char_t
Definition: compiler_port.h:48
#define htons(value)
Definition: cpu_endian.h:413
#define ntohs(value)
Definition: cpu_endian.h:421
Debugging facilities.
#define TRACE_DEBUG(...)
Definition: debug.h:107
#define TRACE_WARNING(...)
Definition: debug.h:85
#define TRACE_INFO(...)
Definition: debug.h:95
error_t
Error codes.
Definition: error.h:43
@ NO_ERROR
Success.
Definition: error.h:44
@ ERROR_OUT_OF_MEMORY
Definition: error.h:63
error_t igmpInit(NetInterface *interface)
IGMP initialization.
Definition: igmp_common.c:59
systime_t igmpTickCounter
Definition: igmp_common.c:50
void igmpLinkChangeEvent(NetInterface *interface)
Callback function for link change event.
Definition: igmp_common.c:123
void igmpDumpMessage(const IgmpMessage *message)
Dump IGMP message for debugging purpose.
Definition: igmp_common.c:339
void igmpProcessMessage(NetInterface *interface, const Ipv4PseudoHeader *pseudoHeader, const NetBuffer *buffer, size_t offset, const NetRxAncillary *ancillary)
Process incoming IGMP message.
Definition: igmp_common.c:259
error_t igmpSendMessage(NetInterface *interface, Ipv4Addr destAddr, const IgmpMessage *message, size_t length)
Send IGMP message.
Definition: igmp_common.c:141
void igmpTick(NetInterface *interface)
IGMP timer handler.
Definition: igmp_common.c:91
Definitions common to IGMP host, router and snooping switch.
IgmpMessage
Definition: igmp_common.h:172
#define IGMP_TTL
Definition: igmp_common.h:127
@ IGMP_TYPE_MEMBERSHIP_REPORT_V3
Definition: igmp_common.h:150
@ IGMP_TYPE_LEAVE_GROUP
Definition: igmp_common.h:149
@ IGMP_TYPE_MEMBERSHIP_REPORT_V2
Definition: igmp_common.h:148
@ IGMP_TYPE_MEMBERSHIP_REPORT_V1
Definition: igmp_common.h:147
@ IGMP_TYPE_MEMBERSHIP_QUERY
Definition: igmp_common.h:146
#define IGMP_ALL_SYSTEMS_ADDR
Definition: igmp_common.h:130
error_t igmpHostInit(NetInterface *interface)
IGMP host initialization.
Definition: igmp_host.c:59
void igmpHostLinkChangeEvent(NetInterface *interface)
Callback function for link change event.
Definition: igmp_host.c:223
void igmpHostTick(NetInterface *interface)
IGMP timer handler.
Definition: igmp_host.c:166
IGMP host.
void igmpHostProcessMessage(NetInterface *interface, const IgmpMessage *message, size_t length)
Process incoming IGMP message.
Helper functions for IGMP host.
void igmpRouterTick(IgmpRouterContext *context)
IGMP router timer handler.
Definition: igmp_router.c:220
IGMP router.
void igmpRouterProcessMessage(IgmpRouterContext *context, const Ipv4PseudoHeader *pseudoHeader, const IgmpMessage *message, size_t length)
Process incoming IGMP message.
Helper functions fore IGMP router.
void igmpSnoopingTick(IgmpSnoopingContext *context)
IGMP snooping switch timer handler.
IGMP snooping switch.
void igmpSnoopingProcessMessage(IgmpSnoopingContext *context, const Ipv4PseudoHeader *pseudoHeader, const IgmpMessage *message, size_t length, const NetRxAncillary *ancillary)
Process incoming IGMP message.
Helper functions for IGMP snooping switch.
NetBuffer * ipAllocBuffer(size_t length, size_t *offset)
Allocate a buffer to hold an IP packet.
Definition: ip.c:744
uint16_t ipCalcChecksumEx(const NetBuffer *buffer, size_t offset, size_t length)
Calculate IP checksum over a multi-part buffer.
Definition: ip.c:619
error_t ipv4SendDatagram(NetInterface *interface, const Ipv4PseudoHeader *pseudoHeader, NetBuffer *buffer, size_t offset, NetTxAncillary *ancillary)
Send an IPv4 datagram.
Definition: ipv4.c:1004
char_t * ipv4AddrToString(Ipv4Addr ipAddr, char_t *str)
Convert a binary IPv4 address to dot-decimal notation.
Definition: ipv4.c:1636
error_t ipv4JoinMulticastGroup(NetInterface *interface, Ipv4Addr groupAddr)
Join the specified host group.
Definition: ipv4.c:1362
#define Ipv4PseudoHeader
Definition: ipv4.h:39
@ IPV4_PROTOCOL_IGMP
Definition: ipv4.h:221
uint32_t Ipv4Addr
IPv4 network address.
Definition: ipv4.h:267
Ipv4Addr destAddr
Definition: ipv4.h:299
TCP/IP stack core.
#define NetInterface
Definition: net.h:36
void * netBufferAt(const NetBuffer *buffer, size_t offset)
Returns a pointer to the data at the specified position.
Definition: net_mem.c:415
void netBufferFree(NetBuffer *buffer)
Dispose a multi-part buffer.
Definition: net_mem.c:282
size_t netBufferGetLength(const NetBuffer *buffer)
Get the actual length of a multi-part buffer.
Definition: net_mem.c:297
error_t netBufferAppend(NetBuffer *dest, const void *src, size_t length)
Append data a multi-part buffer.
Definition: net_mem.c:588
const NetTxAncillary NET_DEFAULT_TX_ANCILLARY
Definition: net_misc.c:71
const NetRxAncillary NET_DEFAULT_RX_ANCILLARY
Definition: net_misc.c:101
#define NetRxAncillary
Definition: net_misc.h:40
#define NetTxAncillary
Definition: net_misc.h:36
#define SWITCH_CPU_PORT
Definition: nic.h:59
#define TRUE
Definition: os_port.h:50
uint32_t systime_t
System time.
Structure describing a buffer that spans multiple chunks.
Definition: net_mem.h:89
uint8_t length
Definition: tcp.h:368