igmp.c
Go to the documentation of this file.
1 /**
2  * @file igmp.c
3  * @brief IGMP (Internet Group Management Protocol)
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2010-2019 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
30  * to routers. Refer to the following RFCs for complete details:
31  * - RFC 1112: Host Extensions for IP Multicasting
32  * - RFC 2236: Internet Group Management Protocol, Version 2
33  * - RFC 3376: Internet Group Management Protocol, Version 3
34  *
35  * @author Oryx Embedded SARL (www.oryx-embedded.com)
36  * @version 1.9.6
37  **/
38 
39 //Switch to the appropriate trace level
40 #define TRACE_LEVEL IGMP_TRACE_LEVEL
41 
42 //Dependencies
43 #include "core/net.h"
44 #include "core/ip.h"
45 #include "ipv4/ipv4.h"
46 #include "ipv4/igmp.h"
47 #include "debug.h"
48 
49 //Check TCP/IP stack configuration
50 #if (IPV4_SUPPORT == ENABLED && IGMP_SUPPORT == ENABLED)
51 
52 //Tick counter to handle periodic operations
54 
55 
56 /**
57  * @brief IGMP initialization
58  * @param[in] interface Underlying network interface
59  * @return Error code
60  **/
61 
63 {
64  //The default host compatibility mode is IGMPv2
65  interface->igmpv1RouterPresent = FALSE;
66 
67  //Start IGMPv1 router present timer
68  interface->igmpv1RouterPresentTimer =
70 
71  //Successful initialization
72  return NO_ERROR;
73 }
74 
75 
76 /**
77  * @brief Join the specified host group
78  * @param[in] interface Underlying network interface
79  * @param[in] entry IPv4 filter entry identifying the host group to join
80  * @return Error code
81  **/
82 
84 {
85  //The all-systems group (address 224.0.0.1) is handled as a special
86  //case. The host starts in Idle Member state for that group on every
87  //interface and never transitions to another state
88  if(entry->addr == IGMP_ALL_SYSTEMS_ADDR)
89  {
90  //Clear flag
91  entry->flag = FALSE;
92  //Enter the Idle Member state
94  }
95  else
96  {
97  //Link is up?
98  if(interface->linkState)
99  {
100  //When a host joins a multicast group, it should immediately transmit
101  //an unsolicited Membership Report for that group
102  igmpSendReportMessage(interface, entry->addr);
103 
104  //Set flag
105  entry->flag = TRUE;
106  //Start timer
108  //Enter the Delaying Member state
110  }
111  //Link is down?
112  else
113  {
114  //Clear flag
115  entry->flag = FALSE;
116  //Enter the Idle Member state
117  entry->state = IGMP_STATE_IDLE_MEMBER;
118  }
119  }
120 
121  //Successful processing
122  return NO_ERROR;
123 }
124 
125 
126 /**
127  * @brief Leave the specified host group
128  * @param[in] interface Underlying network interface
129  * @param[in] entry IPv4 filter entry identifying the host group to leave
130  * @return Error code
131  **/
132 
134 {
135  //Check link state
136  if(interface->linkState)
137  {
138  //Send a Leave Group message if the flag is set
139  if(entry->flag)
140  igmpSendLeaveGroupMessage(interface, entry->addr);
141  }
142 
143  //Switch to the Non-Member state
144  entry->state = IGMP_STATE_NON_MEMBER;
145 
146  //Successful processing
147  return NO_ERROR;
148 }
149 
150 
151 /**
152  * @brief IGMP timer handler
153  *
154  * This routine must be periodically called by the TCP/IP stack to
155  * handle IGMP related timers
156  *
157  * @param[in] interface Underlying network interface
158  **/
159 
160 void igmpTick(NetInterface *interface)
161 {
162  uint_t i;
163  systime_t time;
164  Ipv4FilterEntry *entry;
165 
166  //Get current time
167  time = osGetSystemTime();
168 
169  //Check IGMPv1 router present timer
170  if(timeCompare(time, interface->igmpv1RouterPresentTimer) >= 0)
171  interface->igmpv1RouterPresent = FALSE;
172 
173  //Go through the multicast filter table
174  for(i = 0; i < IPV4_MULTICAST_FILTER_SIZE; i++)
175  {
176  //Point to the current entry
177  entry = &interface->ipv4Context.multicastFilter[i];
178 
179  //Valid entry?
180  if(entry->refCount > 0)
181  {
182  //Delaying Member state?
183  if(entry->state == IGMP_STATE_DELAYING_MEMBER)
184  {
185  //Timer expired?
186  if(timeCompare(time, entry->timer) >= 0)
187  {
188  //Send a Membership Report message for the group on the interface
189  igmpSendReportMessage(interface, entry->addr);
190 
191  //Set flag
192  entry->flag = TRUE;
193  //Switch to the Idle Member state
194  entry->state = IGMP_STATE_IDLE_MEMBER;
195  }
196  }
197  }
198  }
199 }
200 
201 
202 /**
203  * @brief Callback function for link change event
204  * @param[in] interface Underlying network interface
205  **/
206 
208 {
209  uint_t i;
210  systime_t time;
211  Ipv4FilterEntry *entry;
212 
213  //Get current time
214  time = osGetSystemTime();
215 
216  //Link up event?
217  if(interface->linkState)
218  {
219  //The default host compatibility mode is IGMPv2
220  interface->igmpv1RouterPresent = FALSE;
221  //Start IGMPv1 router present timer
222  interface->igmpv1RouterPresentTimer = time + IGMP_V1_ROUTER_PRESENT_TIMEOUT;
223 
224  //Go through the multicast filter table
225  for(i = 0; i < IPV4_MULTICAST_FILTER_SIZE; i++)
226  {
227  //Point to the current entry
228  entry = &interface->ipv4Context.multicastFilter[i];
229 
230  //Valid entry?
231  if(entry->refCount > 0)
232  {
233  //The all-systems group (address 224.0.0.1) is handled as a special
234  //case. The host starts in Idle Member state for that group on every
235  //interface and never transitions to another state
236  if(entry->addr != IGMP_ALL_SYSTEMS_ADDR)
237  {
238  //Send an unsolicited Membership Report for that group
239  igmpSendReportMessage(interface, entry->addr);
240 
241  //Set flag
242  entry->flag = TRUE;
243  //Start timer
245  //Enter the Delaying Member state
247  }
248  }
249  }
250  }
251  //Link down event?
252  else
253  {
254  //Go through the multicast filter table
255  for(i = 0; i < IPV4_MULTICAST_FILTER_SIZE; i++)
256  {
257  //Point to the current entry
258  entry = &interface->ipv4Context.multicastFilter[i];
259 
260  //Valid entry?
261  if(entry->refCount > 0)
262  {
263  //Clear flag
264  entry->flag = FALSE;
265  //Enter the Idle Member state
266  entry->state = IGMP_STATE_IDLE_MEMBER;
267  }
268  }
269  }
270 }
271 
272 
273 /**
274  * @brief Process incoming IGMP message
275  * @param[in] interface Underlying network interface
276  * @param[in] buffer Multi-part buffer containing the incoming IGMP message
277  * @param[in] offset Offset to the first byte of the IGMP message
278  **/
279 
281  const NetBuffer *buffer, size_t offset)
282 {
283  size_t length;
285 
286  //Retrieve the length of the IGMP message
287  length = netBufferGetLength(buffer) - offset;
288 
289  //Ensure the message length is correct
290  if(length < sizeof(IgmpMessage))
291  {
292  //Debug message
293  TRACE_WARNING("IGMP message length is invalid!\r\n");
294  //Silently discard incoming message
295  return;
296  }
297 
298  //Point to the beginning of the IGMP message
299  message = netBufferAt(buffer, offset);
300  //Sanity check
301  if(message == NULL)
302  return;
303 
304  //Debug message
305  TRACE_INFO("IGMP message received (%" PRIuSIZE " bytes)...\r\n", length);
306  //Dump message contents for debugging purpose
308 
309  //Verify checksum value
310  if(ipCalcChecksumEx(buffer, offset, length) != 0x0000)
311  {
312  //Debug message
313  TRACE_WARNING("Wrong IGMP header checksum!\r\n");
314  //Drop incoming message
315  return;
316  }
317 
318  //Check the type field
319  switch(message->type)
320  {
321  //Membership Query message?
323  //Process Membership Query message
325  break;
326  //Membership Report message?
329  //Process Membership Query message
331  break;
332  //Unknown type?
333  default:
334  //Debug message
335  TRACE_WARNING("Unknown IGMP message type!\r\n");
336  //Discard incoming IGMP message
337  break;
338  }
339 }
340 
341 
342 /**
343  * @brief Process incoming Membership Query message
344  * @param[in] interface Underlying network interface
345  * @param[in] message Incoming Membership Query message
346  * @param[in] length Message length
347  **/
348 
350  const IgmpMessage *message, size_t length)
351 {
352  uint_t i;
353  systime_t time;
355  Ipv4FilterEntry *entry;
356 
357  //Get current time
358  time = osGetSystemTime();
359 
360  //IGMPv1 Membership Query message?
361  if(message->maxRespTime == 0)
362  {
363  //The host receives a query with the Max Response Time field set to 0
364  interface->igmpv1RouterPresent = TRUE;
365  //Restart IGMPv1 router present timer
366  interface->igmpv1RouterPresentTimer = time + IGMP_V1_ROUTER_PRESENT_TIMEOUT;
367  //The maximum response time is 10 seconds by default
369  }
370  //IGMPv2 Membership Query message?
371  else
372  {
373  //The Max Resp Time field specifies the maximum time allowed
374  //before sending a responding report
375  maxRespTime = message->maxRespTime * 10;
376  }
377 
378  //Go through the multicast filter table
379  for(i = 0; i < IPV4_MULTICAST_FILTER_SIZE; i++)
380  {
381  //Point to the current entry
382  entry = &interface->ipv4Context.multicastFilter[i];
383 
384  //Valid entry?
385  if(entry->refCount > 0)
386  {
387  //The all-systems group (224.0.0.1) is handled as a special case. The
388  //host starts in Idle Member state for that group on every interface
389  //and never transitions to another state
390  if(entry->addr != IGMP_ALL_SYSTEMS_ADDR)
391  {
392  //A General Query applies to all memberships on the interface from which
393  //the Query is received. A Group-Specific Query applies to membership
394  //in a single group on the interface from which the Query is received
395  if(message->groupAddr == IPV4_UNSPECIFIED_ADDR ||
396  message->groupAddr == entry->addr)
397  {
398  //Delaying Member state?
399  if(entry->state == IGMP_STATE_DELAYING_MEMBER)
400  {
401  //The timer has not yet expired?
402  if(timeCompare(time, entry->timer) < 0)
403  {
404  //If a timer for the group is already running, it is reset to
405  //the random value only if the requested Max Response Time is
406  //less than the remaining value of the running timer
407  if(maxRespTime < (entry->timer - time))
408  {
409  //Restart delay timer
410  entry->timer = time + igmpRand(maxRespTime);
411  }
412  }
413  }
414  //Idle Member state?
415  else if(entry->state == IGMP_STATE_IDLE_MEMBER)
416  {
417  //Switch to the Delaying Member state
419  //Delay the response by a random amount of time
420  entry->timer = time + igmpRand(maxRespTime);
421  }
422  }
423  }
424  }
425  }
426 }
427 
428 
429 /**
430  * @brief Process incoming Membership Report message
431  * @param[in] interface Underlying network interface
432  * @param[in] message Incoming Membership Report message
433  * @param[in] length Message length
434  **/
435 
437  const IgmpMessage *message, size_t length)
438 {
439  uint_t i;
440  Ipv4FilterEntry *entry;
441 
442  //Go through the multicast filter table
443  for(i = 0; i < IPV4_MULTICAST_FILTER_SIZE; i++)
444  {
445  //Point to the current entry
446  entry = &interface->ipv4Context.multicastFilter[i];
447 
448  //Valid entry?
449  if(entry->refCount > 0)
450  {
451  //Report messages are ignored for memberships in
452  //the Non-Member or Idle Member state
453  if(entry->state == IGMP_STATE_DELAYING_MEMBER)
454  {
455  //The Membership Report message matches the current entry?
456  if(message->groupAddr == entry->addr)
457  {
458  //Clear flag
459  entry->flag = FALSE;
460  //Switch to the Idle Member state
461  entry->state = IGMP_STATE_IDLE_MEMBER;
462  }
463  }
464  }
465  }
466 }
467 
468 
469 /**
470  * @brief Send Membership Report message
471  * @param[in] interface Underlying network interface
472  * @param[in] ipAddr IPv4 address specifying the group address
473  * @return Error code
474  **/
475 
477 {
478  error_t error;
479  size_t offset;
481  NetBuffer *buffer;
482  Ipv4PseudoHeader pseudoHeader;
483 
484  //Make sure the specified address is a valid multicast address
486  return ERROR_INVALID_ADDRESS;
487 
488  //The all-systems group (224.0.0.1) is handled as a special case.
489  //The host never sends a report for that group
491  return ERROR_INVALID_ADDRESS;
492 
493  //Allocate a memory buffer to hold an IGMP message
494  buffer = ipAllocBuffer(sizeof(IgmpMessage), &offset);
495  //Failed to allocate memory?
496  if(buffer == NULL)
497  return ERROR_OUT_OF_MEMORY;
498 
499  //Point to the beginning of the IGMP message
500  message = netBufferAt(buffer, offset);
501 
502  //The type of report is determined by the state of the interface
503  if(interface->igmpv1RouterPresent)
505  else
507 
508  //Format the Membership Report message
509  message->maxRespTime = 0;
510  message->checksum = 0;
511  message->groupAddr = ipAddr;
512 
513  //Message checksum calculation
514  message->checksum = ipCalcChecksumEx(buffer, offset, sizeof(IgmpMessage));
515 
516  //Format IPv4 pseudo header
517  pseudoHeader.srcAddr = interface->ipv4Context.addrList[0].addr;
518  pseudoHeader.destAddr = ipAddr;
519  pseudoHeader.reserved = 0;
520  pseudoHeader.protocol = IPV4_PROTOCOL_IGMP;
521  pseudoHeader.length = HTONS(sizeof(IgmpMessage));
522 
523  //Debug message
524  TRACE_INFO("Sending IGMP message (%" PRIuSIZE " bytes)...\r\n", sizeof(IgmpMessage));
525  //Dump message contents for debugging purpose
527 
528  //The Membership Report message is sent to the group being reported
529  error = ipv4SendDatagram(interface, &pseudoHeader, buffer, offset, IGMP_TTL);
530 
531  //Free previously allocated memory
532  netBufferFree(buffer);
533  //Return status code
534  return error;
535 }
536 
537 
538 /**
539  * @brief Send Leave Group message
540  * @param[in] interface Underlying network interface
541  * @param[in] ipAddr IPv4 address specifying the group address being left
542  * @return Error code
543  **/
544 
546 {
547  error_t error;
548  size_t offset;
549  NetBuffer *buffer;
551  Ipv4PseudoHeader pseudoHeader;
552 
553  //Make sure the specified address is a valid multicast address
555  return ERROR_INVALID_ADDRESS;
556 
557  //The all-systems group (224.0.0.1) is handled as a special case.
558  //The host never sends a Leave Group message for that group
560  return ERROR_INVALID_ADDRESS;
561 
562  //If the interface state says the querier is running
563  //IGMPv1, this action should be skipped
564  if(interface->igmpv1RouterPresent)
565  return NO_ERROR;
566 
567  //Allocate a memory buffer to hold an IGMP message
568  buffer = ipAllocBuffer(sizeof(IgmpMessage), &offset);
569  //Failed to allocate memory?
570  if(buffer == NULL)
571  return ERROR_OUT_OF_MEMORY;
572 
573  //Point to the beginning of the IGMP message
574  message = netBufferAt(buffer, offset);
575 
576  //Format the Leave Group message
578  message->maxRespTime = 0;
579  message->checksum = 0;
580  message->groupAddr = ipAddr;
581 
582  //Message checksum calculation
583  message->checksum = ipCalcChecksumEx(buffer, offset, sizeof(IgmpMessage));
584 
585  //Format IPv4 pseudo header
586  pseudoHeader.srcAddr = interface->ipv4Context.addrList[0].addr;
587  pseudoHeader.destAddr = IGMP_ALL_ROUTERS_ADDR;
588  pseudoHeader.reserved = 0;
589  pseudoHeader.protocol = IPV4_PROTOCOL_IGMP;
590  pseudoHeader.length = HTONS(sizeof(IgmpMessage));
591 
592  //Debug message
593  TRACE_INFO("Sending IGMP message (%" PRIuSIZE " bytes)...\r\n", sizeof(IgmpMessage));
594  //Dump message contents for debugging purpose
596 
597  //The Leave Group message is sent to the all-routers multicast group
598  error = ipv4SendDatagram(interface, &pseudoHeader, buffer, offset, IGMP_TTL);
599 
600  //Free previously allocated memory
601  netBufferFree(buffer);
602  //Return status code
603  return error;
604 }
605 
606 
607 /**
608  * @brief Get a random value in the specified range
609  * @param[in] max Upper bound
610  * @return Random value in the specified range
611  **/
612 
613 uint32_t igmpRand(uint32_t max)
614 {
615  //Return a random value in the given range
616  return netGetRand() % (max + 1);
617 }
618 
619 
620 /**
621  * @brief Dump IGMP message for debugging purpose
622  * @param[in] message Pointer to the IGMP message
623  **/
624 
626 {
627  //Dump IGMP message
628  TRACE_DEBUG(" Type = 0x%02" PRIX8 "\r\n", message->type);
629  TRACE_DEBUG(" Max Resp Time = 0x%02" PRIX8 "\r\n", message->maxRespTime);
630  TRACE_DEBUG(" Checksum = 0x%04" PRIX16 "\r\n", ntohs(message->checksum));
631  TRACE_DEBUG(" Group Address = %s\r\n", ipv4AddrToString(message->groupAddr, NULL));
632 }
633 
634 #endif
#define ipv4IsMulticastAddr(ipAddr)
Definition: ipv4.h:162
uint8_t length
Definition: dtls_misc.h:149
NetBuffer * ipAllocBuffer(size_t length, size_t *offset)
Allocate a buffer to hold an IP packet.
Definition: ip.c:637
void igmpLinkChangeEvent(NetInterface *interface)
Callback function for link change event.
Definition: igmp.c:207
uint16_t ipCalcChecksumEx(const NetBuffer *buffer, size_t offset, size_t length)
Calculate IP checksum over a multi-part buffer.
Definition: ip.c:512
error_t igmpSendReportMessage(NetInterface *interface, Ipv4Addr ipAddr)
Send Membership Report message.
Definition: igmp.c:476
systime_t igmpTickCounter
Definition: igmp.c:53
void igmpProcessMessage(NetInterface *interface, const NetBuffer *buffer, size_t offset)
Process incoming IGMP message.
Definition: igmp.c:280
Structure describing a buffer that spans multiple chunks.
Definition: net_mem.h:88
#define IGMP_ALL_ROUTERS_ADDR
Definition: igmp.h:78
uint_t state
IGMP host state.
Definition: ipv4.h:328
#define TRUE
Definition: os_port.h:50
error_t ipv4SendDatagram(NetInterface *interface, Ipv4PseudoHeader *pseudoHeader, NetBuffer *buffer, size_t offset, uint_t flags)
Send an IPv4 datagram.
Definition: ipv4.c:878
IGMP (Internet Group Management Protocol)
@ IGMP_TYPE_LEAVE_GROUP
Definition: igmp.h:107
@ IGMP_STATE_IDLE_MEMBER
Definition: igmp.h:94
@ ERROR_OUT_OF_MEMORY
Definition: error.h:63
#define IGMP_V1_MAX_RESPONSE_TIME
Definition: igmp.h:60
@ IGMP_STATE_NON_MEMBER
Definition: igmp.h:92
#define timeCompare(t1, t2)
Definition: os_port.h:42
#define IGMP_V1_ROUTER_PRESENT_TIMEOUT
Definition: igmp.h:67
uint32_t Ipv4Addr
IPv4 network address.
Definition: ipv4.h:239
error_t igmpJoinGroup(NetInterface *interface, Ipv4FilterEntry *entry)
Join the specified host group.
Definition: igmp.c:83
#define FALSE
Definition: os_port.h:46
void igmpProcessQueryMessage(NetInterface *interface, const IgmpMessage *message, size_t length)
Process incoming Membership Query message.
Definition: igmp.c:349
void igmpDumpMessage(const IgmpMessage *message)
Dump IGMP message for debugging purpose.
Definition: igmp.c:625
error_t
Error codes.
Definition: error.h:42
uint32_t igmpRand(uint32_t max)
Get a random value in the specified range.
Definition: igmp.c:613
void * netBufferAt(const NetBuffer *buffer, size_t offset)
Returns a pointer to the data at the specified position.
Definition: net_mem.c:413
void igmpProcessReportMessage(NetInterface *interface, const IgmpMessage *message, size_t length)
Process incoming Membership Report message.
Definition: igmp.c:436
@ ERROR_INVALID_ADDRESS
Definition: error.h:102
error_t igmpInit(NetInterface *interface)
IGMP initialization.
Definition: igmp.c:62
#define NetInterface
Definition: net.h:36
void netBufferFree(NetBuffer *buffer)
Dispose a multi-part buffer.
Definition: net_mem.c:282
IPv4 multicast filter entry.
Definition: ipv4.h:324
#define Ipv4PseudoHeader
Definition: ipv4.h:39
#define TRACE_INFO(...)
Definition: debug.h:94
size_t netBufferGetLength(const NetBuffer *buffer)
Get the actual length of a multi-part buffer.
Definition: net_mem.c:297
__start_packed struct @169 IgmpMessage
General IGMP message format.
@ IGMP_TYPE_MEMBERSHIP_REPORT_V2
Definition: igmp.h:106
void igmpTick(NetInterface *interface)
IGMP timer handler.
Definition: igmp.c:160
uint32_t netGetRand(void)
Get a random value.
Definition: net.c:1810
systime_t timer
Delay timer.
Definition: ipv4.h:330
#define IGMP_ALL_SYSTEMS_ADDR
Definition: igmp.h:76
bool_t flag
IGMP flag.
Definition: ipv4.h:329
@ IPV4_PROTOCOL_IGMP
Definition: ipv4.h:215
#define ntohs(value)
Definition: cpu_endian.h:398
#define TRACE_WARNING(...)
Definition: debug.h:84
#define TRACE_DEBUG(...)
Definition: debug.h:106
Ipv4Addr addr
Multicast address.
Definition: ipv4.h:326
uint32_t time
@ IGMP_TYPE_MEMBERSHIP_REPORT_V1
Definition: igmp.h:105
#define IGMP_TTL
Definition: igmp.h:73
IPv4 and IPv6 common routines.
#define HTONS(value)
Definition: cpu_endian.h:390
#define IGMP_UNSOLICITED_REPORT_INTERVAL
Definition: igmp.h:53
uint8_t message[]
Definition: chap.h:152
uint8_t maxRespTime
Definition: igmp.h:125
uint_t refCount
Reference count for the current entry.
Definition: ipv4.h:327
IPv4 (Internet Protocol Version 4)
#define PRIuSIZE
Definition: compiler_port.h:78
unsigned int uint_t
Definition: compiler_port.h:45
TCP/IP stack core.
char_t * ipv4AddrToString(Ipv4Addr ipAddr, char_t *str)
Convert a binary IPv4 address to dot-decimal notation.
Definition: ipv4.c:1466
@ IGMP_STATE_DELAYING_MEMBER
Definition: igmp.h:93
@ IGMP_TYPE_MEMBERSHIP_QUERY
Definition: igmp.h:104
uint32_t systime_t
Definition: compiler_port.h:46
uint8_t ipAddr[4]
Definition: mib_common.h:187
@ NO_ERROR
Success.
Definition: error.h:44
Debugging facilities.
error_t igmpSendLeaveGroupMessage(NetInterface *interface, Ipv4Addr ipAddr)
Send Leave Group message.
Definition: igmp.c:545
#define IPV4_UNSPECIFIED_ADDR
Definition: ipv4.h:104
error_t igmpLeaveGroup(NetInterface *interface, Ipv4FilterEntry *entry)
Leave the specified host group.
Definition: igmp.c:133
#define IPV4_MULTICAST_FILTER_SIZE
Definition: ipv4.h:77
systime_t osGetSystemTime(void)
Retrieve system time.