mld.c
Go to the documentation of this file.
1 /**
2  * @file mld.c
3  * @brief MLD (Multicast Listener Discovery for IPv6)
4  *
5  * @section License
6  *
7  * Copyright (C) 2010-2018 Oryx Embedded SARL. All rights reserved.
8  *
9  * This file is part of CycloneTCP Open.
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software Foundation,
23  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
24  *
25  * @section Description
26  *
27  * MLD is used by an IPv6 router to discover the presence of multicast
28  * listeners on its directly attached links, and to discover specifically
29  * which multicast addresses are of interest to those neighboring nodes.
30  * Refer to the following RFCs for complete details:
31  * - RFC 2710: Multicast Listener Discovery (MLD) for IPv6
32  * - RFC 3810: Multicast Listener Discovery Version 2 (MLDv2) for IPv6
33  *
34  * @author Oryx Embedded SARL (www.oryx-embedded.com)
35  * @version 1.9.0
36  **/
37 
38 //Switch to the appropriate trace level
39 #define TRACE_LEVEL MLD_TRACE_LEVEL
40 
41 //Dependencies
42 #include "core/net.h"
43 #include "core/ip.h"
44 #include "ipv6/ipv6.h"
45 #include "ipv6/icmpv6.h"
46 #include "ipv6/mld.h"
47 #include "mibs/ip_mib_module.h"
48 #include "debug.h"
49 
50 //Check TCP/IP stack configuration
51 #if (IPV6_SUPPORT == ENABLED && MLD_SUPPORT == ENABLED)
52 
53 //Tick counter to handle periodic operations
55 
56 
57 /**
58  * @brief MLD initialization
59  * @param[in] interface Underlying network interface
60  * @return Error code
61  **/
62 
64 {
65  //Successful initialization
66  return NO_ERROR;
67 }
68 
69 
70 /**
71  * @brief Start listening to the address on the interface
72  * @param[in] interface Underlying network interface
73  * @param[in] entry IPv6 filter entry identifying the address to listen to
74  * @return Error code
75  **/
76 
78 {
79  //The link-scope all-nodes address (FF02::1) is handled as a special
80  //case. The host starts in Idle Listener state for that address on
81  //every interface and never transitions to another state
83  {
84  //Clear flag
85  entry->flag = FALSE;
86  //Enter the Idle Listener state
88  }
89  else
90  {
91  //Link is up?
92  if(interface->linkState)
93  {
94  //Send a Multicast Listener Report message for the group on the interface
95  mldSendListenerReport(interface, &entry->addr);
96 
97  //Set flag
98  entry->flag = TRUE;
99  //Start timer
101  //Enter the Delaying Listener state
103  }
104  //Link is down?
105  else
106  {
107  //Clear flag
108  entry->flag = FALSE;
109  //Enter the Idle Listener state
111  }
112  }
113 
114  //Successful processing
115  return NO_ERROR;
116 }
117 
118 
119 /**
120  * @brief Stop listening to the address on the interface
121  * @param[in] interface Underlying network interface
122  * @param[in] entry IPv6 filter entry identifying the multicast address to leave
123  * @return Error code
124  **/
125 
127 {
128  //Check link state
129  if(interface->linkState)
130  {
131  //Send a Multicast Listener Done message if the flag is set
132  if(entry->flag)
133  mldSendListenerDone(interface, &entry->addr);
134  }
135 
136  //Switch to the Non-Listener state
137  entry->state = MLD_STATE_NON_LISTENER;
138 
139  //Successful processing
140  return NO_ERROR;
141 }
142 
143 
144 /**
145  * @brief MLD timer handler
146  *
147  * This routine must be periodically called by the TCP/IP stack to
148  * handle MLD related timers
149  *
150  * @param[in] interface Underlying network interface
151  **/
152 
153 void mldTick(NetInterface *interface)
154 {
155  uint_t i;
156  systime_t time;
157  Ipv6FilterEntry *entry;
158 
159  //Get current time
160  time = osGetSystemTime();
161 
162  //Go through the multicast filter table
163  for(i = 0; i < IPV6_MULTICAST_FILTER_SIZE; i++)
164  {
165  //Point to the current entry
166  entry = &interface->ipv6Context.multicastFilter[i];
167 
168  //Valid entry?
169  if(entry->refCount > 0)
170  {
171  //Delaying Listener state?
172  if(entry->state == MLD_STATE_DELAYING_LISTENER)
173  {
174  //Timer expired?
175  if(timeCompare(time, entry->timer) >= 0)
176  {
177  //Send a Multicast Listener Report message
178  mldSendListenerReport(interface, &entry->addr);
179 
180  //Set flag
181  entry->flag = TRUE;
182  //Switch to the Idle Listener state
184  }
185  }
186  }
187  }
188 }
189 
190 
191 /**
192  * @brief Callback function for link change event
193  * @param[in] interface Underlying network interface
194  **/
195 
197 {
198  uint_t i;
199  systime_t time;
200  Ipv6FilterEntry *entry;
201 
202  //Get current time
203  time = osGetSystemTime();
204 
205  //Link up event?
206  if(interface->linkState)
207  {
208  //Go through the multicast filter table
209  for(i = 0; i < IPV6_MULTICAST_FILTER_SIZE; i++)
210  {
211  //Point to the current entry
212  entry = &interface->ipv6Context.multicastFilter[i];
213 
214  //Valid entry?
215  if(entry->refCount > 0)
216  {
217  //The link-scope all-nodes address (FF02::1) is handled as a special
218  //case. The host starts in Idle Listener state for that address on
219  //every interface and never transitions to another state
221  {
222  //Send an unsolicited Multicast Listener Report message for that group
223  mldSendListenerReport(interface, &entry->addr);
224 
225  //Set flag
226  entry->flag = TRUE;
227  //Start timer
229  //Enter the Delaying Listener state
231  }
232  }
233  }
234  }
235  //Link down event?
236  else
237  {
238  //Go through the multicast filter table
239  for(i = 0; i < IPV6_MULTICAST_FILTER_SIZE; i++)
240  {
241  //Point to the current entry
242  entry = &interface->ipv6Context.multicastFilter[i];
243 
244  //Valid entry?
245  if(entry->refCount > 0)
246  {
247  //Clear flag
248  entry->flag = FALSE;
249  //Enter the Idle Listener state
251  }
252  }
253  }
254 }
255 
256 
257 /**
258  * @brief Process incoming Multicast Listener Query message
259  * @param[in] interface Underlying network interface
260  * @param[in] pseudoHeader IPv6 pseudo header
261  * @param[in] buffer Multi-part buffer containing the incoming MLD message
262  * @param[in] offset Offset to the first byte of the MLD message
263  * @param[in] hopLimit Hop Limit field from IPv6 header
264  **/
265 
267  const NetBuffer *buffer, size_t offset, uint8_t hopLimit)
268 {
269  uint_t i;
270  size_t length;
271  systime_t time;
274  Ipv6FilterEntry *entry;
275 
276  //Retrieve the length of the MLD message
277  length = netBufferGetLength(buffer) - offset;
278 
279  //The message must be at least 24 octets long
280  if(length < sizeof(MldMessage))
281  return;
282 
283  //Point to the beginning of the MLD message
284  message = netBufferAt(buffer, offset);
285  //Sanity check
286  if(message == NULL)
287  return;
288 
289  //Debug message
290  TRACE_INFO("MLD message received (%" PRIuSIZE " bytes)...\r\n", length);
291  //Dump message contents for debugging purpose
293 
294  //Make sure the source address of the message is a valid link-local address
295  if(!ipv6IsLinkLocalUnicastAddr(&pseudoHeader->srcAddr))
296  return;
297 
298  //Check the Hop Limit field
299  if(hopLimit != MLD_HOP_LIMIT)
300  return;
301 
302  //Get current time
303  time = osGetSystemTime();
304 
305  //The Max Resp Delay field specifies the maximum time allowed
306  //before sending a responding report
307  maxRespDelay = message->maxRespDelay * 10;
308 
309  //Go through the multicast filter table
310  for(i = 0; i < IPV6_MULTICAST_FILTER_SIZE; i++)
311  {
312  //Point to the current entry
313  entry = &interface->ipv6Context.multicastFilter[i];
314 
315  //Valid entry?
316  if(entry->refCount > 0)
317  {
318  //The link-scope all-nodes address (FF02::1) is handled as a special
319  //case. The host starts in Idle Listener state for that address on
320  //every interface and never transitions to another state
322  {
323  //A General Query is used to learn which multicast addresses have listeners
324  //on an attached link. A Multicast-Address-Specific Query is used to learn
325  //if a particular multicast address has any listeners on an attached link
326  if(ipv6CompAddr(&message->multicastAddr, &IPV6_UNSPECIFIED_ADDR) ||
327  ipv6CompAddr(&message->multicastAddr, &entry->addr))
328  {
329  //Delaying Listener state?
330  if(entry->state == MLD_STATE_DELAYING_LISTENER)
331  {
332  //The timer has not yet expired?
333  if(timeCompare(time, entry->timer) < 0)
334  {
335  //If a timer for the address is already running, it is reset to
336  //the new random value only if the requested Max Response Delay
337  //is less than the remaining value of the running timer
338  if(maxRespDelay < (entry->timer - time))
339  {
340  //Restart delay timer
341  entry->timer = time + mldRand(maxRespDelay);
342  }
343  }
344  }
345  //Idle Listener state?
346  else if(entry->state == MLD_STATE_IDLE_LISTENER)
347  {
348  //Switch to the Delaying Listener state
350  //Delay the response by a random amount of time
351  entry->timer = time + mldRand(maxRespDelay);
352  }
353  }
354  }
355  }
356  }
357 }
358 
359 
360 /**
361  * @brief Process incoming Multicast Listener Report message
362  * @param[in] interface Underlying network interface
363  * @param[in] pseudoHeader IPv6 pseudo header
364  * @param[in] buffer Multi-part buffer containing the incoming MLD message
365  * @param[in] offset Offset to the first byte of the MLD message
366  * @param[in] hopLimit Hop Limit field from IPv6 header
367  **/
368 
370  const NetBuffer *buffer, size_t offset, uint8_t hopLimit)
371 {
372  uint_t i;
373  size_t length;
375  Ipv6FilterEntry *entry;
376 
377  //Retrieve the length of the MLD message
378  length = netBufferGetLength(buffer) - offset;
379 
380  //The message must be at least 24 octets long
381  if(length < sizeof(MldMessage))
382  return;
383 
384  //Point to the beginning of the MLD message
385  message = netBufferAt(buffer, offset);
386  //Sanity check
387  if(message == NULL)
388  return;
389 
390  //Debug message
391  TRACE_INFO("MLD message received (%" PRIuSIZE " bytes)...\r\n", length);
392  //Dump message contents for debugging purpose
394 
395  //Make sure the source address of the message is a valid link-local address
396  if(!ipv6IsLinkLocalUnicastAddr(&pseudoHeader->srcAddr))
397  return;
398  //Check the Hop Limit field
399  if(hopLimit != MLD_HOP_LIMIT)
400  return;
401 
402  //Go through the multicast filter table
403  for(i = 0; i < IPV6_MULTICAST_FILTER_SIZE; i++)
404  {
405  //Point to the current entry
406  entry = &interface->ipv6Context.multicastFilter[i];
407 
408  //Valid entry?
409  if(entry->refCount > 0)
410  {
411  //Report messages are ignored for multicast addresses
412  //in the Non-Listener or Idle Listener state
413  if(entry->state == MLD_STATE_DELAYING_LISTENER)
414  {
415  //The Multicast Listener Report message matches the current entry?
416  if(ipv6CompAddr(&message->multicastAddr, &entry->addr))
417  {
418  //Clear flag
419  entry->flag = FALSE;
420  //Switch to the Idle Listener state
422  }
423  }
424  }
425  }
426 }
427 
428 
429 /**
430  * @brief Send Multicast Listener Report message
431  * @param[in] interface Underlying network interface
432  * @param[in] ipAddr IPv6 address specifying the multicast address
433  * @return Error code
434  **/
435 
437 {
438  error_t error;
439  size_t offset;
441  NetBuffer *buffer;
442  Ipv6PseudoHeader pseudoHeader;
443 
444  //Make sure the specified address is a valid multicast address
446  return ERROR_INVALID_ADDRESS;
447 
448  //The link-scope all-nodes address (FF02::1) is handled as a special
449  //case. The host never sends a report for that address
451  return ERROR_INVALID_ADDRESS;
452 
453  //Allocate a memory buffer to hold a MLD message
454  buffer = ipAllocBuffer(sizeof(MldMessage), &offset);
455  //Failed to allocate memory?
456  if(buffer == NULL)
457  return ERROR_OUT_OF_MEMORY;
458 
459  //Point to the beginning of the MLD message
460  message = netBufferAt(buffer, offset);
461 
462  //Format the Multicast Listener Report message
464  message->code = 0;
465  message->checksum = 0;
466  message->maxRespDelay = 0;
467  message->reserved = 0;
468  message->multicastAddr = *ipAddr;
469 
470  //Format IPv6 pseudo header
471  pseudoHeader.srcAddr = interface->ipv6Context.addrList[0].addr;
472  pseudoHeader.destAddr = *ipAddr;
473  pseudoHeader.length = HTONS(sizeof(MldMessage));
474  pseudoHeader.reserved = 0;
475  pseudoHeader.nextHeader = IPV6_ICMPV6_HEADER;
476 
477  //Message checksum calculation
478  message->checksum = ipCalcUpperLayerChecksumEx(&pseudoHeader,
479  sizeof(Ipv6PseudoHeader), buffer, offset, sizeof(MldMessage));
480 
481  //Total number of ICMP messages which this entity attempted to send
482  IP_MIB_INC_COUNTER32(icmpv6Stats.icmpStatsOutMsgs, 1);
483 
484  //Increment per-message type ICMP counter
485  IP_MIB_INC_COUNTER32(icmpv6MsgStatsTable.icmpMsgStatsOutPkts[
487 
488  //Debug message
489  TRACE_INFO("Sending MLD message (%" PRIuSIZE " bytes)...\r\n", sizeof(MldMessage));
490  //Dump message contents for debugging purpose
492 
493  //The Multicast Listener Report message is sent to the multicast address being reported
494  error = ipv6SendDatagram(interface, &pseudoHeader, buffer, offset, MLD_HOP_LIMIT);
495 
496  //Free previously allocated memory
497  netBufferFree(buffer);
498  //Return status code
499  return error;
500 }
501 
502 
503 /**
504  * @brief Send Multicast Listener Done message
505  * @param[in] interface Underlying network interface
506  * @param[in] ipAddr IPv6 address specifying the multicast address being left
507  * @return Error code
508  **/
509 
511 {
512  error_t error;
513  size_t offset;
515  NetBuffer *buffer;
516  Ipv6PseudoHeader pseudoHeader;
517 
518  //Make sure the specified address is a valid multicast address
520  return ERROR_INVALID_ADDRESS;
521 
522  //The link-scope all-nodes address (FF02::1) is handled as a special
523  //case. The host never sends a report for that address
525  return ERROR_INVALID_ADDRESS;
526 
527  //Allocate a memory buffer to hold a MLD message
528  buffer = ipAllocBuffer(sizeof(MldMessage), &offset);
529  //Failed to allocate memory?
530  if(buffer == NULL)
531  return ERROR_OUT_OF_MEMORY;
532 
533  //Point to the beginning of the MLD message
534  message = netBufferAt(buffer, offset);
535 
536  //Format the Multicast Listener Done message
538  message->code = 0;
539  message->checksum = 0;
540  message->maxRespDelay = 0;
541  message->reserved = 0;
542  message->multicastAddr = *ipAddr;
543 
544  //Format IPv6 pseudo header
545  pseudoHeader.srcAddr = interface->ipv6Context.addrList[0].addr;
546  pseudoHeader.destAddr = IPV6_LINK_LOCAL_ALL_ROUTERS_ADDR;
547  pseudoHeader.length = HTONS(sizeof(MldMessage));
548  pseudoHeader.reserved = 0;
549  pseudoHeader.nextHeader = IPV6_ICMPV6_HEADER;
550 
551  //Message checksum calculation
552  message->checksum = ipCalcUpperLayerChecksumEx(&pseudoHeader,
553  sizeof(Ipv6PseudoHeader), buffer, offset, sizeof(MldMessage));
554 
555  //Total number of ICMP messages which this entity attempted to send
556  IP_MIB_INC_COUNTER32(icmpv6Stats.icmpStatsOutMsgs, 1);
557 
558  //Increment per-message type ICMP counter
559  IP_MIB_INC_COUNTER32(icmpv6MsgStatsTable.icmpMsgStatsOutPkts[
561 
562  //Debug message
563  TRACE_INFO("Sending MLD message (%" PRIuSIZE " bytes)...\r\n", sizeof(MldMessage));
564  //Dump message contents for debugging purpose
566 
567  //The Multicast Listener Done message is sent to the all-routers multicast address
568  error = ipv6SendDatagram(interface, &pseudoHeader, buffer, offset, MLD_HOP_LIMIT);
569 
570  //Free previously allocated memory
571  netBufferFree(buffer);
572  //Return status code
573  return error;
574 }
575 
576 
577 /**
578  * @brief Get a random value in the specified range
579  * @param[in] max Upper bound
580  * @return Random value in the specified range
581  **/
582 
583 uint32_t mldRand(uint32_t max)
584 {
585  //Return a random value in the given range
586  return netGetRand() % (max + 1);
587 }
588 
589 
590 /**
591  * @brief Dump MLD message for debugging purpose
592  * @param[in] message Pointer to the MLD message
593  **/
594 
596 {
597  //Dump MLD message
598  TRACE_DEBUG(" Type = %" PRIu8 "\r\n", message->type);
599  TRACE_DEBUG(" Code = %" PRIu8 "\r\n", message->code);
600  TRACE_DEBUG(" Checksum = 0x%04" PRIX16 "\r\n", ntohs(message->checksum));
601  TRACE_DEBUG(" Max Resp Delay = %" PRIu16 "\r\n", message->maxRespDelay);
602  TRACE_DEBUG(" Multicast Address = %s\r\n", ipv6AddrToString(&message->multicastAddr, NULL));
603 }
604 
605 #endif
uint32_t systime_t
Definition: compiler_port.h:44
error_t ipv6SendDatagram(NetInterface *interface, Ipv6PseudoHeader *pseudoHeader, NetBuffer *buffer, size_t offset, uint8_t hopLimit)
Send an IPv6 datagram.
Definition: ipv6.c:1574
#define timeCompare(t1, t2)
Definition: os_port.h:40
error_t mldSendListenerDone(NetInterface *interface, Ipv6Addr *ipAddr)
Send Multicast Listener Done message.
Definition: mld.c:510
#define ipv6IsMulticastAddr(ipAddr)
Definition: ipv6.h:131
#define MLD_HOP_LIMIT
Definition: mld.h:57
systime_t osGetSystemTime(void)
Retrieve system time.
uint32_t time
const Ipv6Addr IPV6_LINK_LOCAL_ALL_ROUTERS_ADDR
Definition: ipv6.c:77
TCP/IP stack core.
void netBufferFree(NetBuffer *buffer)
Dispose a multi-part buffer.
Definition: net_mem.c:280
Debugging facilities.
uint32_t mldRand(uint32_t max)
Get a random value in the specified range.
Definition: mld.c:583
size_t netBufferGetLength(const NetBuffer *buffer)
Get the actual length of a multi-part buffer.
Definition: net_mem.c:295
const Ipv6Addr IPV6_LINK_LOCAL_ALL_NODES_ADDR
Definition: ipv6.c:73
error_t mldInit(NetInterface *interface)
MLD initialization.
Definition: mld.c:63
uint8_t message[]
Definition: chap.h:150
MLD (Multicast Listener Discovery for IPv6)
IPv4 and IPv6 common routines.
__start_packed struct @183 Ipv6Addr
IPv6 network address.
uint16_t ipCalcUpperLayerChecksumEx(const void *pseudoHeader, size_t pseudoHeaderLen, const NetBuffer *buffer, size_t offset, size_t length)
Calculate IP upper-layer checksum over a multi-part buffer.
Definition: ip.c:571
uint_t state
MLD node state.
Definition: ipv6.h:450
#define IPV6_MULTICAST_FILTER_SIZE
Definition: ipv6.h:99
uint16_t maxRespDelay
Definition: mld.h:92
#define HTONS(value)
Definition: cpu_endian.h:388
#define TRUE
Definition: os_port.h:48
#define ipv6IsLinkLocalUnicastAddr(ipAddr)
Definition: ipv6.h:123
uint8_t ipAddr[4]
Definition: mib_common.h:185
error_t mldSendListenerReport(NetInterface *interface, Ipv6Addr *ipAddr)
Send Multicast Listener Report message.
Definition: mld.c:436
#define MLD_UNSOLICITED_REPORT_INTERVAL
Definition: mld.h:51
error_t mldStartListening(NetInterface *interface, Ipv6FilterEntry *entry)
Start listening to the address on the interface.
Definition: mld.c:77
#define ntohs(value)
Definition: cpu_endian.h:396
uint32_t netGetRand(void)
Get a random value.
Definition: net.c:1523
void * netBufferAt(const NetBuffer *buffer, size_t offset)
Returns a pointer to the data at the specified position.
Definition: net_mem.c:411
void mldTick(NetInterface *interface)
MLD timer handler.
Definition: mld.c:153
NetBuffer * ipAllocBuffer(size_t length, size_t *offset)
Allocate a buffer to hold an IP packet.
Definition: ip.c:596
error_t mldStopListening(NetInterface *interface, Ipv6FilterEntry *entry)
Stop listening to the address on the interface.
Definition: mld.c:126
void mldDumpMessage(const MldMessage *message)
Dump MLD message for debugging purpose.
Definition: mld.c:595
#define Ipv6PseudoHeader
Definition: ipv6.h:40
Structure describing a buffer that spans multiple chunks.
Definition: net_mem.h:86
Ipv6Addr addr
Multicast address.
Definition: ipv6.h:448
IP MIB module.
void mldProcessListenerReport(NetInterface *interface, Ipv6PseudoHeader *pseudoHeader, const NetBuffer *buffer, size_t offset, uint8_t hopLimit)
Process incoming Multicast Listener Report message.
Definition: mld.c:369
#define TRACE_INFO(...)
Definition: debug.h:86
IPv6 (Internet Protocol Version 6)
Success.
Definition: error.h:42
systime_t timer
Delay timer.
Definition: ipv6.h:452
bool_t flag
MLD flag.
Definition: ipv6.h:451
error_t
Error codes.
Definition: error.h:40
#define IP_MIB_INC_COUNTER32(name, value)
Definition: ip_mib_module.h:44
systime_t mldTickCounter
Definition: mld.c:54
unsigned int uint_t
Definition: compiler_port.h:43
ICMPv6 (Internet Control Message Protocol Version 6)
#define PRIuSIZE
Definition: compiler_port.h:72
#define NetInterface
Definition: net.h:34
void mldLinkChangeEvent(NetInterface *interface)
Callback function for link change event.
Definition: mld.c:196
const Ipv6Addr IPV6_UNSPECIFIED_ADDR
Definition: ipv6.c:65
IPv6 multicast filter entry.
Definition: ipv6.h:446
char_t * ipv6AddrToString(const Ipv6Addr *ipAddr, char_t *str)
Convert a binary IPv6 address to a string representation.
Definition: ipv6.c:2298
void mldProcessListenerQuery(NetInterface *interface, Ipv6PseudoHeader *pseudoHeader, const NetBuffer *buffer, size_t offset, uint8_t hopLimit)
Process incoming Multicast Listener Query message.
Definition: mld.c:266
__start_packed struct @193 MldMessage
MLD message.
uint8_t length
Definition: dtls_misc.h:140
#define FALSE
Definition: os_port.h:44
#define ipv6CompAddr(ipAddr1, ipAddr2)
Definition: ipv6.h:119
#define TRACE_DEBUG(...)
Definition: debug.h:98
uint_t refCount
Reference count for the current entry.
Definition: ipv6.h:449