ndp_cache.c
Go to the documentation of this file.
1 /**
2  * @file ndp_cache.c
3  * @brief Neighbor and destination cache management
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2010-2023 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.2.4
29  **/
30 
31 //Switch to the appropriate trace level
32 #define TRACE_LEVEL NDP_TRACE_LEVEL
33 
34 //Dependencies
35 #include "core/net.h"
36 #include "ipv6/ipv6_misc.h"
37 #include "ipv6/icmpv6.h"
38 #include "ipv6/ndp.h"
39 #include "ipv6/ndp_cache.h"
40 #include "ipv6/ndp_misc.h"
41 #include "debug.h"
42 
43 //Check TCP/IP stack configuration
44 #if (IPV6_SUPPORT == ENABLED && NDP_SUPPORT == ENABLED)
45 
46 
47 /**
48  * @brief Create a new entry in the Neighbor cache
49  * @param[in] interface Underlying network interface
50  * @return Pointer to the newly created entry
51  **/
52 
54 {
55  uint_t i;
57  NdpNeighborCacheEntry *entry;
58  NdpNeighborCacheEntry *oldestEntry;
59 
60  //Get current time
62 
63  //Keep track of the oldest entry
64  oldestEntry = NULL;
65 
66  //Loop through Neighbor cache entries
67  for(i = 0; i < NDP_NEIGHBOR_CACHE_SIZE; i++)
68  {
69  //Point to the current entry
70  entry = &interface->ndpContext.neighborCache[i];
71 
72  //Check the state of the Neighbor cache entry
73  if(entry->state == NDP_STATE_NONE)
74  {
75  //Initialize Neighbor cache entry
76  osMemset(entry, 0, sizeof(NdpNeighborCacheEntry));
77  //Return a pointer to the Neighbor cache entry
78  return entry;
79  }
80  else if(entry->state == NDP_STATE_PERMANENT)
81  {
82  //Static Neighbor cache entries are never updated
83  }
84  else
85  {
86  //Keep track of the oldest entry in the table
87  if(oldestEntry == NULL)
88  {
89  oldestEntry = entry;
90  }
91  else if((time - entry->timestamp) > (time - oldestEntry->timestamp))
92  {
93  oldestEntry = entry;
94  }
95  }
96  }
97 
98  //Any entry available in the Neighbor cache?
99  if(oldestEntry != NULL)
100  {
101  //Drop any pending packets
102  ndpFlushQueuedPackets(interface, oldestEntry);
103  //The oldest entry is removed whenever the table runs out of space
104  osMemset(oldestEntry, 0, sizeof(NdpNeighborCacheEntry));
105  }
106 
107  //Return a pointer to the Neighbor cache entry
108  return oldestEntry;
109 }
110 
111 
112 /**
113  * @brief Search the Neighbor cache for a given IPv6 address
114  * @param[in] interface Underlying network interface
115  * @param[in] ipAddr IPv6 address
116  * @return A pointer to the matching entry is returned. NULL is returned if
117  * the specified IPv6 address could not be found in the Neighbor cache
118  **/
119 
121  const Ipv6Addr *ipAddr)
122 {
123  uint_t i;
124  NdpNeighborCacheEntry *entry;
125 
126  //Loop through Neighbor cache entries
127  for(i = 0; i < NDP_NEIGHBOR_CACHE_SIZE; i++)
128  {
129  //Point to the current entry
130  entry = &interface->ndpContext.neighborCache[i];
131 
132  //Check whether the entry is currently in use
133  if(entry->state != NDP_STATE_NONE)
134  {
135  //Current entry matches the specified address?
136  if(ipv6CompAddr(&entry->ipAddr, ipAddr))
137  {
138  return entry;
139  }
140  }
141  }
142 
143  //No matching entry in Neighbor cache
144  return NULL;
145 }
146 
147 
148 /**
149  * @brief Periodically update Neighbor cache
150  * @param[in] interface Underlying network interface
151  **/
152 
154 {
155  uint_t i;
156  systime_t time;
157  NdpNeighborCacheEntry *entry;
158 
159  //Get current time
160  time = osGetSystemTime();
161 
162  //Go through Neighbor cache
163  for(i = 0; i < NDP_NEIGHBOR_CACHE_SIZE; i++)
164  {
165  //Point to the current entry
166  entry = &interface->ndpContext.neighborCache[i];
167 
168  //Check the state of the Neighbor cache entry
169  if(entry->state == NDP_STATE_PERMANENT)
170  {
171  //Static Neighbor cache entries are never updated
172  }
173  else if(entry->state == NDP_STATE_INCOMPLETE)
174  {
175  //The Neighbor Solicitation timed out?
176  if(timeCompare(time, entry->timestamp + entry->timeout) >= 0)
177  {
178  //Increment retransmission counter
179  entry->retransmitCount++;
180 
181  //Check whether the maximum number of retransmissions has been exceeded
183  {
184  //Retransmit the multicast Neighbor Solicitation message
185  ndpSendNeighborSol(interface, &entry->ipAddr, TRUE);
186 
187  //Save the time at which the message was sent
188  entry->timestamp = time;
189  //Set timeout value
190  entry->timeout = interface->ndpContext.retransTimer;
191  }
192  else
193  {
194  //Drop packets that are waiting for address resolution
195  ndpFlushQueuedPackets(interface, entry);
196  //The entry should be deleted since address resolution has failed
197  entry->state = NDP_STATE_NONE;
198  }
199  }
200  }
201  else if(entry->state == NDP_STATE_REACHABLE)
202  {
203  //Periodically time out Neighbor cache entries
204  if(timeCompare(time, entry->timestamp + entry->timeout) >= 0)
205  {
206  //Save current time
207  entry->timestamp = osGetSystemTime();
208  //Enter STALE state
209  entry->state = NDP_STATE_STALE;
210  }
211  }
212  else if(entry->state == NDP_STATE_STALE)
213  {
214  //The neighbor is no longer known to be reachable but until traffic
215  //is sent to the neighbor, no attempt should be made to verify its
216  //reachability
217  }
218  else if(entry->state == NDP_STATE_DELAY)
219  {
220  //Wait for the specified delay before sending the first probe
221  if(timeCompare(time, entry->timestamp + entry->timeout) >= 0)
222  {
224 
225  //Save the time at which the message was sent
226  entry->timestamp = time;
227  //Set timeout value
228  entry->timeout = interface->ndpContext.retransTimer;
229  //Switch to the PROBE state
230  entry->state = NDP_STATE_PROBE;
231 
232  //Target address
233  ipAddr = entry->ipAddr;
234 
235  //Send a unicast Neighbor Solicitation message
236  ndpSendNeighborSol(interface, &ipAddr, FALSE);
237  }
238  }
239  else if(entry->state == NDP_STATE_PROBE)
240  {
241  //The request timed out?
242  if(timeCompare(time, entry->timestamp + entry->timeout) >= 0)
243  {
244  //Increment retransmission counter
245  entry->retransmitCount++;
246 
247  //Check whether the maximum number of retransmissions has been exceeded
249  {
251 
252  //Save the time at which the packet was sent
253  entry->timestamp = time;
254  //Set timeout value
255  entry->timeout = interface->ndpContext.retransTimer;
256 
257  //Target address
258  ipAddr = entry->ipAddr;
259 
260  //Send a unicast Neighbor Solicitation message
261  ndpSendNeighborSol(interface, &ipAddr, FALSE);
262  }
263  else
264  {
265  //The entry should be deleted since the host is not reachable anymore
266  entry->state = NDP_STATE_NONE;
267 
268  //If at some point communication ceases to proceed, as determined
269  //by the Neighbor Unreachability Detection algorithm, next-hop
270  //determination may need to be performed again...
271  ndpUpdateNextHop(interface, &entry->ipAddr);
272  }
273  }
274  }
275  else
276  {
277  //Just for sanity
278  entry->state = NDP_STATE_NONE;
279  }
280  }
281 }
282 
283 
284 /**
285  * @brief Flush Neighbor cache
286  * @param[in] interface Underlying network interface
287  **/
288 
290 {
291  uint_t i;
292  NdpNeighborCacheEntry *entry;
293 
294  //Loop through Neighbor cache entries
295  for(i = 0; i < NDP_NEIGHBOR_CACHE_SIZE; i++)
296  {
297  //Point to the current entry
298  entry = &interface->ndpContext.neighborCache[i];
299 
300  //Check the state of the Neighbor cache entry
301  if(entry->state == NDP_STATE_PERMANENT)
302  {
303  //Static Neighbor cache entries are never updated
304  }
305  else
306  {
307  //Drop packets that are waiting for address resolution
308  ndpFlushQueuedPackets(interface, entry);
309  //Delete Neighbor cache entry
310  entry->state = NDP_STATE_NONE;
311  }
312  }
313 }
314 
315 
316 /**
317  * @brief Send packets that are waiting for address resolution
318  * @param[in] interface Underlying network interface
319  * @param[in] entry Pointer to a Neighbor cache entry
320  * @return The number of packets that have been sent
321  **/
322 
324 {
325  uint_t i;
326  NdpQueueItem *item;
327 #if (ETH_SUPPORT == ENABLED)
328  NetInterface *physicalInterface;
329 
330  //Point to the physical interface
331  physicalInterface = nicGetPhysicalInterface(interface);
332 #endif
333 
334  //Reset packet counter
335  i = 0;
336 
337  //Check the state of the Neighbor cache entry
338  if(entry->state == NDP_STATE_INCOMPLETE)
339  {
340  //Loop through the queued packets
341  for(i = 0; i < entry->queueSize; i++)
342  {
343  //Point to the current queue item
344  item = &entry->queue[i];
345 
346 #if (ETH_SUPPORT == ENABLED)
347  //Ethernet interface?
348  if(physicalInterface->nicDriver != NULL &&
349  physicalInterface->nicDriver->type == NIC_TYPE_ETHERNET)
350  {
351  size_t length;
352 
353  //Retrieve the length of the IPv6 packet
354  length = netBufferGetLength(item->buffer) - item->offset;
355  //Update IP statistics
356  ipv6UpdateOutStats(interface, &entry->ipAddr, length);
357 
358  //Send the IPv6 packet
359  ethSendFrame(interface, &entry->macAddr, ETH_TYPE_IPV6,
360  item->buffer, item->offset, &item->ancillary);
361  }
362 #endif
363  //Release memory buffer
364  netBufferFree(item->buffer);
365  }
366  }
367 
368  //The queue is now empty
369  entry->queueSize = 0;
370 
371  //Return the number of packets that have been sent
372  return i;
373 }
374 
375 
376 /**
377  * @brief Flush packet queue
378  * @param[in] interface Underlying network interface
379  * @param[in] entry Pointer to a Neighbor cache entry
380  **/
381 
383 {
384  uint_t i;
385  NdpQueueItem *item;
386 
387  //Check the state of the Neighbor cache entry
388  if(entry->state == NDP_STATE_INCOMPLETE)
389  {
390  //Loop through the queued packets
391  for(i = 0; i < entry->queueSize; i++)
392  {
393  //Point to the current queue item
394  item = &entry->queue[i];
395 
396  //Check whether the address resolution has failed
398  {
399  //Check whether the packet has been forwarded
400  if(item->srcInterface != NULL)
401  {
402  //A Destination Unreachable message should be generated by a
403  //router in response to a packet that cannot be delivered
405  ICMPV6_CODE_ADDR_UNREACHABLE, 0, item->buffer, item->offset);
406  }
407  }
408 
409  //Release memory buffer
410  netBufferFree(item->buffer);
411  }
412  }
413 
414  //The queue is now empty
415  entry->queueSize = 0;
416 }
417 
418 
419 /**
420  * @brief Create a new entry in the Destination Cache
421  * @param[in] interface Underlying network interface
422  * @return Pointer to the newly created entry
423  **/
424 
426 {
427  uint_t i;
428  systime_t time;
429  NdpDestCacheEntry *entry;
430  NdpDestCacheEntry *oldestEntry;
431 
432  //Get current time
433  time = osGetSystemTime();
434 
435  //Keep track of the oldest entry
436  oldestEntry = &interface->ndpContext.destCache[0];
437 
438  //Loop through Destination cache entries
439  for(i = 0; i < NDP_DEST_CACHE_SIZE; i++)
440  {
441  //Point to the current entry
442  entry = &interface->ndpContext.destCache[i];
443 
444  //Check whether the entry is currently in use or not
446  {
447  //Erase contents
448  osMemset(entry, 0, sizeof(NdpDestCacheEntry));
449  //Return a pointer to the Destination cache entry
450  return entry;
451  }
452 
453  //Keep track of the oldest entry in the table
454  if((time - entry->timestamp) > (time - oldestEntry->timestamp))
455  {
456  oldestEntry = entry;
457  }
458  }
459 
460  //The oldest entry is removed whenever the table runs out of space
461  osMemset(oldestEntry, 0, sizeof(NdpDestCacheEntry));
462 
463  //Return a pointer to the Destination cache entry
464  return oldestEntry;
465 }
466 
467 
468 /**
469  * @brief Search the Destination Cache for a given destination address
470  * @param[in] interface Underlying network interface
471  * @param[in] destAddr Destination IPv6 address
472  * @return A pointer to the matching entry is returned. NULL is returned if
473  * the specified address could not be found in the Destination cache
474  **/
475 
477  const Ipv6Addr *destAddr)
478 {
479  uint_t i;
480  NdpDestCacheEntry *entry;
481 
482  //Loop through Destination Cache entries
483  for(i = 0; i < NDP_DEST_CACHE_SIZE; i++)
484  {
485  //Point to the current entry
486  entry = &interface->ndpContext.destCache[i];
487 
488  //Current entry matches the specified destination address?
489  if(ipv6CompAddr(&entry->destAddr, destAddr))
490  {
491  return entry;
492  }
493  }
494 
495  //No matching entry in Destination Cache
496  return NULL;
497 }
498 
499 
500 /**
501  * @brief Flush Destination Cache
502  * @param[in] interface Underlying network interface
503  **/
504 
506 {
507  //Clear the Destination Cache
508  osMemset(interface->ndpContext.destCache, 0,
509  sizeof(interface->ndpContext.destCache));
510 }
511 
512 #endif
uint8_t length
Definition: coap_common.h:193
NdpNeighborCacheEntry * ndpFindNeighborCacheEntry(NetInterface *interface, const Ipv6Addr *ipAddr)
Search the Neighbor cache for a given IPv6 address.
Definition: ndp_cache.c:120
@ ICMPV6_TYPE_DEST_UNREACHABLE
Definition: icmpv6.h:53
#define NDP_MAX_UNICAST_SOLICIT
Definition: ndp.h:151
@ NDP_STATE_STALE
Definition: ndp.h:252
Helper functions for NDP (Neighbor Discovery Protocol)
void ipv6UpdateOutStats(NetInterface *interface, const Ipv6Addr *destIpAddr, size_t length)
Update IPv6 output statistics.
Definition: ipv6_misc.c:1502
NetTxAncillary ancillary
Additional options.
Definition: ndp.h:536
#define TRUE
Definition: os_port.h:52
NdpDestCacheEntry * ndpFindDestCacheEntry(NetInterface *interface, const Ipv6Addr *destAddr)
Search the Destination Cache for a given destination address.
Definition: ndp_cache.c:476
#define NDP_NEIGHBOR_CACHE_SIZE
Definition: ndp.h:53
void ndpFlushQueuedPackets(NetInterface *interface, NdpNeighborCacheEntry *entry)
Flush packet queue.
Definition: ndp_cache.c:382
error_t ethSendFrame(NetInterface *interface, const MacAddr *destAddr, uint16_t type, NetBuffer *buffer, size_t offset, NetTxAncillary *ancillary)
Send an Ethernet frame.
Definition: ethernet.c:401
uint_t queueSize
Number of queued packets.
Definition: ndp.h:554
Neighbor cache entry.
Definition: ndp.h:545
#define ipv6CompAddr(ipAddr1, ipAddr2)
Definition: ipv6.h:121
error_t ndpSendNeighborSol(NetInterface *interface, const Ipv6Addr *targetIpAddr, bool_t multicast)
Send a Neighbor Solicitation message.
Definition: ndp.c:1622
@ NDP_STATE_NONE
Definition: ndp.h:249
Ipv6Addr destAddr
Destination IPv6 address.
Definition: ndp.h:564
#define timeCompare(t1, t2)
Definition: os_port.h:42
size_t offset
Offset to the first byte of the packet.
Definition: ndp.h:535
MacAddr destAddr
Definition: ethernet.h:217
systime_t timestamp
Timestamp to manage entry lifetime.
Definition: ndp.h:550
error_t icmpv6SendErrorMessage(NetInterface *interface, uint8_t type, uint8_t code, uint32_t parameter, const NetBuffer *ipPacket, size_t ipPacketOffset)
Send an ICMPv6 Error message.
Definition: icmpv6.c:505
#define FALSE
Definition: os_port.h:48
ICMPv6 (Internet Control Message Protocol Version 6)
NetInterface * nicGetPhysicalInterface(NetInterface *interface)
Retrieve physical interface.
Definition: nic.c:84
MacAddr macAddr
Link layer address associated with the IPv6 address.
Definition: ndp.h:548
@ ICMPV6_CODE_ADDR_UNREACHABLE
Definition: icmpv6.h:80
systime_t timestamp
Timestamp to manage entry lifetime.
Definition: ndp.h:567
NdpNeighborCacheEntry * ndpCreateNeighborCacheEntry(NetInterface *interface)
Create a new entry in the Neighbor cache.
Definition: ndp_cache.c:53
NdpDestCacheEntry * ndpCreateDestCacheEntry(NetInterface *interface)
Create a new entry in the Destination Cache.
Definition: ndp_cache.c:425
#define NetInterface
Definition: net.h:36
void netBufferFree(NetBuffer *buffer)
Dispose a multi-part buffer.
Definition: net_mem.c:282
uint_t ndpSendQueuedPackets(NetInterface *interface, NdpNeighborCacheEntry *entry)
Send packets that are waiting for address resolution.
Definition: ndp_cache.c:323
@ NDP_STATE_DELAY
Definition: ndp.h:253
Helper functions for IPv6.
void ndpUpdateNextHop(NetInterface *interface, const Ipv6Addr *unreachableNextHop)
Update next-hop field of Destination Cache entries.
Definition: ndp_misc.c:554
const Ipv6Addr IPV6_UNSPECIFIED_ADDR
Definition: ipv6.c:66
size_t netBufferGetLength(const NetBuffer *buffer)
Get the actual length of a multi-part buffer.
Definition: net_mem.c:297
@ NDP_STATE_INCOMPLETE
Definition: ndp.h:250
#define NDP_MAX_MULTICAST_SOLICIT
Definition: ndp.h:144
Neighbor and destination cache management.
#define NDP_DEST_CACHE_SIZE
Definition: ndp.h:60
@ NDP_STATE_REACHABLE
Definition: ndp.h:251
NDP (Neighbor Discovery Protocol)
NdpQueueItem queue[NDP_MAX_PENDING_PACKETS]
Packets waiting for address resolution to complete.
Definition: ndp.h:553
uint32_t systime_t
System time.
void ndpFlushNeighborCache(NetInterface *interface)
Flush Neighbor cache.
Definition: ndp_cache.c:289
void ndpUpdateNeighborCache(NetInterface *interface)
Periodically update Neighbor cache.
Definition: ndp_cache.c:153
NDP queue item.
Definition: ndp.h:532
void ndpFlushDestCache(NetInterface *interface)
Flush Destination Cache.
Definition: ndp_cache.c:505
uint32_t time
Ipv6Addr ipAddr
Unicast IPv6 address.
Definition: ndp.h:547
NetInterface * srcInterface
Interface from which the packet has been received.
Definition: ndp.h:533
@ ETH_TYPE_IPV6
Definition: ethernet.h:168
NdpState state
Reachability state.
Definition: ndp.h:546
Destination cache entry.
Definition: ndp.h:563
uint_t retransmitCount
Retransmission counter.
Definition: ndp.h:552
NetBuffer * buffer
Packet waiting for address resolution.
Definition: ndp.h:534
unsigned int uint_t
Definition: compiler_port.h:50
#define osMemset(p, value, length)
Definition: os_port.h:134
TCP/IP stack core.
@ NDP_STATE_PERMANENT
Definition: ndp.h:255
__start_packed struct @0 Ipv6Addr
IPv6 network address.
@ NDP_STATE_PROBE
Definition: ndp.h:254
uint8_t ipAddr[4]
Definition: mib_common.h:187
systime_t timeout
Timeout value.
Definition: ndp.h:551
Debugging facilities.
@ NIC_TYPE_ETHERNET
Ethernet interface.
Definition: nic.h:83
systime_t osGetSystemTime(void)
Retrieve system time.