blob: a78065c888b74c4be86473dcd3fbb912eed30639 [file] [log] [blame]
Junxiao Shi0e42c572014-10-05 20:33:48 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
Davide Pesavento50a6af32019-02-21 00:04:40 -05002/*
Davide Pesavento2c9d2ca2024-01-27 16:36:51 -05003 * Copyright (c) 2014-2024, Regents of the University of California,
Junxiao Shi9f5b01d2016-08-05 03:54:28 +00004 * Arizona Board of Regents,
5 * Colorado State University,
6 * University Pierre & Marie Curie, Sorbonne University,
7 * Washington University in St. Louis,
8 * Beijing Institute of Technology,
9 * The University of Memphis.
Junxiao Shi0e42c572014-10-05 20:33:48 -070010 *
11 * This file is part of NFD (Named Data Networking Forwarding Daemon).
12 * See AUTHORS.md for complete list of NFD authors and contributors.
13 *
14 * NFD is free software: you can redistribute it and/or modify it under the terms
15 * of the GNU General Public License as published by the Free Software Foundation,
16 * either version 3 of the License, or (at your option) any later version.
17 *
18 * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
19 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
20 * PURPOSE. See the GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License along with
23 * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
24 */
25
26#ifndef NFD_DAEMON_TABLE_DEAD_NONCE_LIST_HPP
27#define NFD_DAEMON_TABLE_DEAD_NONCE_LIST_HPP
28
Junxiao Shi9f5b01d2016-08-05 03:54:28 +000029#include "core/common.hpp"
Junxiao Shi0e42c572014-10-05 20:33:48 -070030
Davide Pesavento2c9d2ca2024-01-27 16:36:51 -050031#include <ndn-cxx/util/scheduler.hpp>
32
Davide Pesavento50a6af32019-02-21 00:04:40 -050033#include <boost/multi_index_container.hpp>
34#include <boost/multi_index/hashed_index.hpp>
35#include <boost/multi_index/sequenced_index.hpp>
36
Junxiao Shi0e42c572014-10-05 20:33:48 -070037namespace nfd {
38
Davide Pesaventod4123932021-05-28 18:48:37 -040039/**
40 * \brief Represents the Dead Nonce List.
Junxiao Shi0e42c572014-10-05 20:33:48 -070041 *
Davide Pesaventod4123932021-05-28 18:48:37 -040042 * The Dead Nonce List is a global table that supplements the PIT for loop detection.
43 * When a Nonce is erased (dead) from a PIT entry, the Nonce and the Interest Name are added to
44 * the Dead Nonce List and kept for a duration in which most loops are expected to have occured.
Junxiao Shi0e42c572014-10-05 20:33:48 -070045 *
Davide Pesaventod4123932021-05-28 18:48:37 -040046 * To reduce memory usage, the Interest Name and Nonce are stored as a 64-bit hash.
47 * The probability of false positives (a non-looping Interest considered as looping) is small
48 * and a collision is recoverable when the consumer retransmits with a different Nonce.
Junxiao Shi0e42c572014-10-05 20:33:48 -070049 *
Davide Pesaventod4123932021-05-28 18:48:37 -040050 * To reduce memory usage, entries do not have associated timestamps. Instead, the lifetime
51 * of the entries is controlled by dynamically adjusting the capacity of the container.
52 * At fixed intervals, a MARK (an entry with a special value) is inserted into the container.
53 * The number of MARKs stored in the container reflects the lifetime of the entries,
54 * because MARKs are inserted at fixed intervals.
Junxiao Shi0e42c572014-10-05 20:33:48 -070055 */
56class DeadNonceList : noncopyable
57{
58public:
Davide Pesaventod4123932021-05-28 18:48:37 -040059 /**
60 * \brief Constructs the Dead Nonce List
61 * \param lifetime expected lifetime of each nonce, must be no less than #MIN_LIFETIME.
62 * This should be set to a duration over which most loops would have occured.
63 * A loop cannot be detected if the total delay of the cycle is greater than lifetime.
64 * \throw std::invalid_argument if lifetime is less than #MIN_LIFETIME
Junxiao Shi0e42c572014-10-05 20:33:48 -070065 */
66 explicit
Davide Pesavento50a6af32019-02-21 00:04:40 -050067 DeadNonceList(time::nanoseconds lifetime = DEFAULT_LIFETIME);
Junxiao Shi0e42c572014-10-05 20:33:48 -070068
Davide Pesaventod4123932021-05-28 18:48:37 -040069 /**
70 * \brief Determines if name+nonce is in the list
71 * \return true if name+nonce exists, false otherwise
Junxiao Shi0e42c572014-10-05 20:33:48 -070072 */
73 bool
Davide Pesavento51cf75c2020-03-11 22:21:13 -040074 has(const Name& name, Interest::Nonce nonce) const;
Junxiao Shi0e42c572014-10-05 20:33:48 -070075
Davide Pesaventod4123932021-05-28 18:48:37 -040076 /**
77 * \brief Adds name+nonce to the list
Junxiao Shi0e42c572014-10-05 20:33:48 -070078 */
79 void
Davide Pesavento51cf75c2020-03-11 22:21:13 -040080 add(const Name& name, Interest::Nonce nonce);
Junxiao Shi0e42c572014-10-05 20:33:48 -070081
Davide Pesaventod4123932021-05-28 18:48:37 -040082 /**
83 * \brief Returns the number of stored nonces
84 * \note The return value does not contain non-Nonce entries in the index, if any.
Junxiao Shi0e42c572014-10-05 20:33:48 -070085 */
86 size_t
87 size() const;
88
Davide Pesaventod4123932021-05-28 18:48:37 -040089 /**
90 * \brief Returns the expected nonce lifetime
Junxiao Shia110f262014-10-12 12:35:20 -070091 */
Davide Pesavento50a6af32019-02-21 00:04:40 -050092 time::nanoseconds
93 getLifetime() const
94 {
95 return m_lifetime;
96 }
Junxiao Shia110f262014-10-12 12:35:20 -070097
Davide Pesaventod4123932021-05-28 18:48:37 -040098private:
99 using Entry = uint64_t;
Junxiao Shi0e42c572014-10-05 20:33:48 -0700100
101 static Entry
Davide Pesavento51cf75c2020-03-11 22:21:13 -0400102 makeEntry(const Name& name, Interest::Nonce nonce);
Junxiao Shi0e42c572014-10-05 20:33:48 -0700103
Davide Pesavento50a6af32019-02-21 00:04:40 -0500104 /** \brief Return the number of MARKs in the index
Junxiao Shi0e42c572014-10-05 20:33:48 -0700105 */
106 size_t
107 countMarks() const;
108
Davide Pesavento50a6af32019-02-21 00:04:40 -0500109 /** \brief Add a MARK, then record number of MARKs in m_actualMarkCounts
Junxiao Shi0e42c572014-10-05 20:33:48 -0700110 */
111 void
112 mark();
113
Davide Pesavento50a6af32019-02-21 00:04:40 -0500114 /** \brief Adjust capacity according to m_actualMarkCounts
Junxiao Shi0e42c572014-10-05 20:33:48 -0700115 *
116 * If all counts are above EXPECTED_MARK_COUNT, reduce capacity to m_capacity * CAPACITY_DOWN.
117 * If all counts are below EXPECTED_MARK_COUNT, increase capacity to m_capacity * CAPACITY_UP.
118 */
119 void
120 adjustCapacity();
121
Davide Pesavento50a6af32019-02-21 00:04:40 -0500122 /** \brief Evict some entries if index is over capacity
Junxiao Shi0e42c572014-10-05 20:33:48 -0700123 */
124 void
125 evictEntries();
126
127public:
Davide Pesavento50a6af32019-02-21 00:04:40 -0500128 /// Default entry lifetime
Davide Pesaventod4123932021-05-28 18:48:37 -0400129 static constexpr time::nanoseconds DEFAULT_LIFETIME = 6_s;
Davide Pesavento50a6af32019-02-21 00:04:40 -0500130 /// Minimum entry lifetime
Davide Pesavento7647cc72021-06-10 14:30:04 -0400131 static constexpr time::nanoseconds MIN_LIFETIME = 50_ms;
Junxiao Shi0e42c572014-10-05 20:33:48 -0700132
133private:
Davide Pesaventod4123932021-05-28 18:48:37 -0400134 const time::nanoseconds m_lifetime;
135
Davide Pesaventoe08cc4c2021-06-08 18:22:46 -0400136 struct Queue {};
137 struct Hashtable {};
138 using Container = boost::multi_index_container<
Davide Pesaventod4123932021-05-28 18:48:37 -0400139 Entry,
140 boost::multi_index::indexed_by<
Davide Pesaventoe08cc4c2021-06-08 18:22:46 -0400141 boost::multi_index::sequenced<boost::multi_index::tag<Queue>>,
142 boost::multi_index::hashed_non_unique<boost::multi_index::tag<Hashtable>,
143 boost::multi_index::identity<Entry>>
Davide Pesaventod4123932021-05-28 18:48:37 -0400144 >
145 >;
Davide Pesaventod4123932021-05-28 18:48:37 -0400146
Davide Pesaventoe08cc4c2021-06-08 18:22:46 -0400147 Container m_index;
148 Container::index<Queue>::type& m_queue = m_index.get<Queue>();
149 Container::index<Hashtable>::type& m_ht = m_index.get<Hashtable>();
Junxiao Shi0e42c572014-10-05 20:33:48 -0700150
Davide Pesaventod4123932021-05-28 18:48:37 -0400151NFD_PUBLIC_WITH_TESTS_ELSE_PRIVATE:
Junxiao Shi0e42c572014-10-05 20:33:48 -0700152
153 // ---- current capacity and hard limits
154
Davide Pesavento50a6af32019-02-21 00:04:40 -0500155 /** \brief Current capacity of index
Junxiao Shi0e42c572014-10-05 20:33:48 -0700156 *
157 * The index size is maintained to be near this capacity.
158 *
159 * The capacity is adjusted so that every Entry is expected to be kept for m_lifetime.
160 * This is achieved by mark() and adjustCapacity().
161 */
162 size_t m_capacity;
163
Davide Pesavento7647cc72021-06-10 14:30:04 -0400164 static constexpr size_t INITIAL_CAPACITY = 1 << 14;
Junxiao Shi0e42c572014-10-05 20:33:48 -0700165
Davide Pesavento50a6af32019-02-21 00:04:40 -0500166 /** \brief Minimum capacity
Junxiao Shi0e42c572014-10-05 20:33:48 -0700167 *
168 * This is to ensure correct algorithm operations.
169 */
Davide Pesavento7647cc72021-06-10 14:30:04 -0400170 static constexpr size_t MIN_CAPACITY = 1 << 10;
Junxiao Shi0e42c572014-10-05 20:33:48 -0700171
Davide Pesavento50a6af32019-02-21 00:04:40 -0500172 /** \brief Maximum capacity
Junxiao Shi0e42c572014-10-05 20:33:48 -0700173 *
174 * This is to limit memory usage.
175 */
Davide Pesaventod4123932021-05-28 18:48:37 -0400176 static constexpr size_t MAX_CAPACITY = 1 << 24;
Junxiao Shi0e42c572014-10-05 20:33:48 -0700177
178 // ---- actual entry lifetime estimation
179
Davide Pesavento50a6af32019-02-21 00:04:40 -0500180 /** \brief The MARK for capacity
Junxiao Shi0e42c572014-10-05 20:33:48 -0700181 *
182 * The MARK doesn't have a distinct type.
Davide Pesaventod4123932021-05-28 18:48:37 -0400183 * Entry is a hash. The hash function should be non-invertible, so that
184 * it's infeasible to craft a "normal" Entry that collides with the MARK.
Junxiao Shi0e42c572014-10-05 20:33:48 -0700185 */
Davide Pesaventod4123932021-05-28 18:48:37 -0400186 static constexpr Entry MARK = 0;
Junxiao Shi0e42c572014-10-05 20:33:48 -0700187
Davide Pesaventod4123932021-05-28 18:48:37 -0400188 /// Expected number of MARKs in the index
189 static constexpr size_t EXPECTED_MARK_COUNT = 5;
Junxiao Shi0e42c572014-10-05 20:33:48 -0700190
Davide Pesavento50a6af32019-02-21 00:04:40 -0500191 /** \brief Number of MARKs in the index after each MARK insertion
Junxiao Shi0e42c572014-10-05 20:33:48 -0700192 *
Davide Pesavento50a6af32019-02-21 00:04:40 -0500193 * adjustCapacity() uses this to determine whether and how to adjust capcity,
Junxiao Shi0e42c572014-10-05 20:33:48 -0700194 * and then clears this list.
195 */
196 std::multiset<size_t> m_actualMarkCounts;
197
Davide Pesaventod4123932021-05-28 18:48:37 -0400198 const time::nanoseconds m_markInterval;
Davide Pesavento2c9d2ca2024-01-27 16:36:51 -0500199 ndn::scheduler::ScopedEventId m_markEvent;
Junxiao Shi0e42c572014-10-05 20:33:48 -0700200
201 // ---- capacity adjustments
202
Davide Pesaventod4123932021-05-28 18:48:37 -0400203 static constexpr double CAPACITY_UP = 1.2;
204 static constexpr double CAPACITY_DOWN = 0.9;
205 const time::nanoseconds m_adjustCapacityInterval;
Davide Pesavento2c9d2ca2024-01-27 16:36:51 -0500206 ndn::scheduler::ScopedEventId m_adjustCapacityEvent;
Junxiao Shi0e42c572014-10-05 20:33:48 -0700207
Davide Pesaventod4123932021-05-28 18:48:37 -0400208 /// Maximum number of entries to evict at each operation if the index is over capacity
209 static constexpr size_t EVICT_LIMIT = 64;
Junxiao Shi0e42c572014-10-05 20:33:48 -0700210};
211
Junxiao Shi0e42c572014-10-05 20:33:48 -0700212} // namespace nfd
213
214#endif // NFD_DAEMON_TABLE_DEAD_NONCE_LIST_HPP