blob: d2ef5853f2be257611f6f3cf40b5dfe16c996a3b [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 Pesavento264af772021-02-09 21:48:24 -05003 * Copyright (c) 2014-2021, 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 Pesavento50a6af32019-02-21 00:04:40 -050031#include <boost/multi_index_container.hpp>
32#include <boost/multi_index/hashed_index.hpp>
33#include <boost/multi_index/sequenced_index.hpp>
34
Junxiao Shi0e42c572014-10-05 20:33:48 -070035namespace nfd {
36
Davide Pesaventod4123932021-05-28 18:48:37 -040037/**
38 * \brief Represents the Dead Nonce List.
Junxiao Shi0e42c572014-10-05 20:33:48 -070039 *
Davide Pesaventod4123932021-05-28 18:48:37 -040040 * The Dead Nonce List is a global table that supplements the PIT for loop detection.
41 * When a Nonce is erased (dead) from a PIT entry, the Nonce and the Interest Name are added to
42 * 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 -070043 *
Davide Pesaventod4123932021-05-28 18:48:37 -040044 * To reduce memory usage, the Interest Name and Nonce are stored as a 64-bit hash.
45 * The probability of false positives (a non-looping Interest considered as looping) is small
46 * and a collision is recoverable when the consumer retransmits with a different Nonce.
Junxiao Shi0e42c572014-10-05 20:33:48 -070047 *
Davide Pesaventod4123932021-05-28 18:48:37 -040048 * To reduce memory usage, entries do not have associated timestamps. Instead, the lifetime
49 * of the entries is controlled by dynamically adjusting the capacity of the container.
50 * At fixed intervals, a MARK (an entry with a special value) is inserted into the container.
51 * The number of MARKs stored in the container reflects the lifetime of the entries,
52 * because MARKs are inserted at fixed intervals.
Junxiao Shi0e42c572014-10-05 20:33:48 -070053 */
54class DeadNonceList : noncopyable
55{
56public:
Davide Pesaventod4123932021-05-28 18:48:37 -040057 /**
58 * \brief Constructs the Dead Nonce List
59 * \param lifetime expected lifetime of each nonce, must be no less than #MIN_LIFETIME.
60 * This should be set to a duration over which most loops would have occured.
61 * A loop cannot be detected if the total delay of the cycle is greater than lifetime.
62 * \throw std::invalid_argument if lifetime is less than #MIN_LIFETIME
Junxiao Shi0e42c572014-10-05 20:33:48 -070063 */
64 explicit
Davide Pesavento50a6af32019-02-21 00:04:40 -050065 DeadNonceList(time::nanoseconds lifetime = DEFAULT_LIFETIME);
Junxiao Shi0e42c572014-10-05 20:33:48 -070066
Davide Pesaventod4123932021-05-28 18:48:37 -040067 /**
68 * \brief Determines if name+nonce is in the list
69 * \return true if name+nonce exists, false otherwise
Junxiao Shi0e42c572014-10-05 20:33:48 -070070 */
71 bool
Davide Pesavento51cf75c2020-03-11 22:21:13 -040072 has(const Name& name, Interest::Nonce nonce) const;
Junxiao Shi0e42c572014-10-05 20:33:48 -070073
Davide Pesaventod4123932021-05-28 18:48:37 -040074 /**
75 * \brief Adds name+nonce to the list
Junxiao Shi0e42c572014-10-05 20:33:48 -070076 */
77 void
Davide Pesavento51cf75c2020-03-11 22:21:13 -040078 add(const Name& name, Interest::Nonce nonce);
Junxiao Shi0e42c572014-10-05 20:33:48 -070079
Davide Pesaventod4123932021-05-28 18:48:37 -040080 /**
81 * \brief Returns the number of stored nonces
82 * \note The return value does not contain non-Nonce entries in the index, if any.
Junxiao Shi0e42c572014-10-05 20:33:48 -070083 */
84 size_t
85 size() const;
86
Davide Pesaventod4123932021-05-28 18:48:37 -040087 /**
88 * \brief Returns the expected nonce lifetime
Junxiao Shia110f262014-10-12 12:35:20 -070089 */
Davide Pesavento50a6af32019-02-21 00:04:40 -050090 time::nanoseconds
91 getLifetime() const
92 {
93 return m_lifetime;
94 }
Junxiao Shia110f262014-10-12 12:35:20 -070095
Davide Pesaventod4123932021-05-28 18:48:37 -040096private:
97 using Entry = uint64_t;
Junxiao Shi0e42c572014-10-05 20:33:48 -070098
99 static Entry
Davide Pesavento51cf75c2020-03-11 22:21:13 -0400100 makeEntry(const Name& name, Interest::Nonce nonce);
Junxiao Shi0e42c572014-10-05 20:33:48 -0700101
Davide Pesavento50a6af32019-02-21 00:04:40 -0500102 /** \brief Return the number of MARKs in the index
Junxiao Shi0e42c572014-10-05 20:33:48 -0700103 */
104 size_t
105 countMarks() const;
106
Davide Pesavento50a6af32019-02-21 00:04:40 -0500107 /** \brief Add a MARK, then record number of MARKs in m_actualMarkCounts
Junxiao Shi0e42c572014-10-05 20:33:48 -0700108 */
109 void
110 mark();
111
Davide Pesavento50a6af32019-02-21 00:04:40 -0500112 /** \brief Adjust capacity according to m_actualMarkCounts
Junxiao Shi0e42c572014-10-05 20:33:48 -0700113 *
114 * If all counts are above EXPECTED_MARK_COUNT, reduce capacity to m_capacity * CAPACITY_DOWN.
115 * If all counts are below EXPECTED_MARK_COUNT, increase capacity to m_capacity * CAPACITY_UP.
116 */
117 void
118 adjustCapacity();
119
Davide Pesavento50a6af32019-02-21 00:04:40 -0500120 /** \brief Evict some entries if index is over capacity
Junxiao Shi0e42c572014-10-05 20:33:48 -0700121 */
122 void
123 evictEntries();
124
125public:
Davide Pesavento50a6af32019-02-21 00:04:40 -0500126 /// Default entry lifetime
Davide Pesaventod4123932021-05-28 18:48:37 -0400127 static constexpr time::nanoseconds DEFAULT_LIFETIME = 6_s;
Davide Pesavento50a6af32019-02-21 00:04:40 -0500128 /// Minimum entry lifetime
Davide Pesavento7647cc72021-06-10 14:30:04 -0400129 static constexpr time::nanoseconds MIN_LIFETIME = 50_ms;
Junxiao Shi0e42c572014-10-05 20:33:48 -0700130
131private:
Davide Pesaventod4123932021-05-28 18:48:37 -0400132 const time::nanoseconds m_lifetime;
133
Davide Pesaventoe08cc4c2021-06-08 18:22:46 -0400134 struct Queue {};
135 struct Hashtable {};
136 using Container = boost::multi_index_container<
Davide Pesaventod4123932021-05-28 18:48:37 -0400137 Entry,
138 boost::multi_index::indexed_by<
Davide Pesaventoe08cc4c2021-06-08 18:22:46 -0400139 boost::multi_index::sequenced<boost::multi_index::tag<Queue>>,
140 boost::multi_index::hashed_non_unique<boost::multi_index::tag<Hashtable>,
141 boost::multi_index::identity<Entry>>
Davide Pesaventod4123932021-05-28 18:48:37 -0400142 >
143 >;
Davide Pesaventod4123932021-05-28 18:48:37 -0400144
Davide Pesaventoe08cc4c2021-06-08 18:22:46 -0400145 Container m_index;
146 Container::index<Queue>::type& m_queue = m_index.get<Queue>();
147 Container::index<Hashtable>::type& m_ht = m_index.get<Hashtable>();
Junxiao Shi0e42c572014-10-05 20:33:48 -0700148
Davide Pesaventod4123932021-05-28 18:48:37 -0400149NFD_PUBLIC_WITH_TESTS_ELSE_PRIVATE:
Junxiao Shi0e42c572014-10-05 20:33:48 -0700150
151 // ---- current capacity and hard limits
152
Davide Pesavento50a6af32019-02-21 00:04:40 -0500153 /** \brief Current capacity of index
Junxiao Shi0e42c572014-10-05 20:33:48 -0700154 *
155 * The index size is maintained to be near this capacity.
156 *
157 * The capacity is adjusted so that every Entry is expected to be kept for m_lifetime.
158 * This is achieved by mark() and adjustCapacity().
159 */
160 size_t m_capacity;
161
Davide Pesavento7647cc72021-06-10 14:30:04 -0400162 static constexpr size_t INITIAL_CAPACITY = 1 << 14;
Junxiao Shi0e42c572014-10-05 20:33:48 -0700163
Davide Pesavento50a6af32019-02-21 00:04:40 -0500164 /** \brief Minimum capacity
Junxiao Shi0e42c572014-10-05 20:33:48 -0700165 *
166 * This is to ensure correct algorithm operations.
167 */
Davide Pesavento7647cc72021-06-10 14:30:04 -0400168 static constexpr size_t MIN_CAPACITY = 1 << 10;
Junxiao Shi0e42c572014-10-05 20:33:48 -0700169
Davide Pesavento50a6af32019-02-21 00:04:40 -0500170 /** \brief Maximum capacity
Junxiao Shi0e42c572014-10-05 20:33:48 -0700171 *
172 * This is to limit memory usage.
173 */
Davide Pesaventod4123932021-05-28 18:48:37 -0400174 static constexpr size_t MAX_CAPACITY = 1 << 24;
Junxiao Shi0e42c572014-10-05 20:33:48 -0700175
176 // ---- actual entry lifetime estimation
177
Davide Pesavento50a6af32019-02-21 00:04:40 -0500178 /** \brief The MARK for capacity
Junxiao Shi0e42c572014-10-05 20:33:48 -0700179 *
180 * The MARK doesn't have a distinct type.
Davide Pesaventod4123932021-05-28 18:48:37 -0400181 * Entry is a hash. The hash function should be non-invertible, so that
182 * it's infeasible to craft a "normal" Entry that collides with the MARK.
Junxiao Shi0e42c572014-10-05 20:33:48 -0700183 */
Davide Pesaventod4123932021-05-28 18:48:37 -0400184 static constexpr Entry MARK = 0;
Junxiao Shi0e42c572014-10-05 20:33:48 -0700185
Davide Pesaventod4123932021-05-28 18:48:37 -0400186 /// Expected number of MARKs in the index
187 static constexpr size_t EXPECTED_MARK_COUNT = 5;
Junxiao Shi0e42c572014-10-05 20:33:48 -0700188
Davide Pesavento50a6af32019-02-21 00:04:40 -0500189 /** \brief Number of MARKs in the index after each MARK insertion
Junxiao Shi0e42c572014-10-05 20:33:48 -0700190 *
Davide Pesavento50a6af32019-02-21 00:04:40 -0500191 * adjustCapacity() uses this to determine whether and how to adjust capcity,
Junxiao Shi0e42c572014-10-05 20:33:48 -0700192 * and then clears this list.
193 */
194 std::multiset<size_t> m_actualMarkCounts;
195
Davide Pesaventod4123932021-05-28 18:48:37 -0400196 const time::nanoseconds m_markInterval;
197 scheduler::ScopedEventId m_markEvent;
Junxiao Shi0e42c572014-10-05 20:33:48 -0700198
199 // ---- capacity adjustments
200
Davide Pesaventod4123932021-05-28 18:48:37 -0400201 static constexpr double CAPACITY_UP = 1.2;
202 static constexpr double CAPACITY_DOWN = 0.9;
203 const time::nanoseconds m_adjustCapacityInterval;
204 scheduler::ScopedEventId m_adjustCapacityEvent;
Junxiao Shi0e42c572014-10-05 20:33:48 -0700205
Davide Pesaventod4123932021-05-28 18:48:37 -0400206 /// Maximum number of entries to evict at each operation if the index is over capacity
207 static constexpr size_t EVICT_LIMIT = 64;
Junxiao Shi0e42c572014-10-05 20:33:48 -0700208};
209
Junxiao Shi0e42c572014-10-05 20:33:48 -0700210} // namespace nfd
211
212#endif // NFD_DAEMON_TABLE_DEAD_NONCE_LIST_HPP