blob: c42a968c7f17c8b38ebe19df25d75628cca319eb [file] [log] [blame]
Junxiao Shid3c792f2014-01-30 00:46:13 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
Eric Newberry41aba102017-11-01 16:42:13 -07002/*
Davide Pesavento2c9d2ca2024-01-27 16:36:51 -05003 * Copyright (c) 2014-2024, Regents of the University of California,
Junxiao Shifaf3eb02015-02-16 10:50:36 -07004 * 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.
Alexander Afanasyev9bcbc7c2014-04-06 19:37:37 -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/>.
Junxiao Shi82e7f582014-09-07 15:15:40 -070024 */
Junxiao Shid3c792f2014-01-30 00:46:13 -070025
Alexander Afanasyev613e2a92014-04-15 13:36:58 -070026#ifndef NFD_DAEMON_FW_STRATEGY_HPP
27#define NFD_DAEMON_FW_STRATEGY_HPP
Junxiao Shid3c792f2014-01-30 00:46:13 -070028
Junxiao Shi2d9bdc82014-03-02 20:55:42 -070029#include "forwarder.hpp"
Junxiao Shidbe71732014-02-21 22:23:28 -070030#include "table/measurements-accessor.hpp"
Junxiao Shid3c792f2014-01-30 00:46:13 -070031
Davide Pesaventoa9b09b62022-06-04 14:07:25 -040032#include <boost/lexical_cast/try_lexical_convert.hpp>
33
Davide Pesavento2c9d2ca2024-01-27 16:36:51 -050034#include <functional>
35#include <map>
36#include <set>
37
Davide Pesaventoe422f9e2022-06-03 01:30:23 -040038namespace nfd::fw {
Junxiao Shi8c8d2182014-01-30 22:33:00 -070039
Ashlesh Gawande1ef93d02022-04-08 00:25:06 -040040class StrategyParameters;
41
Davide Pesavento0498ce82021-06-14 02:02:21 -040042/**
Davide Pesaventoaa9e3b22022-10-21 17:00:07 -040043 * \brief Base class of all forwarding strategies.
Junxiao Shid3c792f2014-01-30 00:46:13 -070044 */
Junxiao Shic34d1672016-12-09 15:57:59 +000045class Strategy : noncopyable
Junxiao Shid3c792f2014-01-30 00:46:13 -070046{
Junxiao Shic34d1672016-12-09 15:57:59 +000047public: // registry
Davide Pesaventoaa9e3b22022-10-21 17:00:07 -040048 /**
49 * \brief Register a strategy type.
50 * \tparam S subclass of Strategy
51 * \param strategyName strategy program name, must contain version
52 * \note It is permitted to register the same strategy type under multiple names,
53 * which is useful in tests and for creating aliases.
Junxiao Shic34d1672016-12-09 15:57:59 +000054 */
55 template<typename S>
56 static void
Junxiao Shi037f4ab2016-12-13 04:27:06 +000057 registerType(const Name& strategyName = S::getStrategyName())
Junxiao Shic34d1672016-12-09 15:57:59 +000058 {
Junxiao Shi91f6ee02016-12-29 21:44:44 +000059 BOOST_ASSERT(strategyName.size() > 1);
Junxiao Shi18739c42016-12-22 08:03:00 +000060 BOOST_ASSERT(strategyName.at(-1).isVersion());
Davide Pesaventoa3a7a4e2022-05-29 16:06:22 -040061 auto r = getRegistry().insert_or_assign(strategyName, [] (auto&&... args) {
Davide Pesavento3dade002019-03-19 11:29:56 -060062 return make_unique<S>(std::forward<decltype(args)>(args)...);
Davide Pesaventoa3a7a4e2022-05-29 16:06:22 -040063 });
64 BOOST_VERIFY(r.second);
Junxiao Shic34d1672016-12-09 15:57:59 +000065 }
66
Davide Pesaventoaa9e3b22022-10-21 17:00:07 -040067 /**
68 * \brief Returns whether a strategy instance can be created from \p instanceName.
69 * \param instanceName strategy instance name, may contain version and parameters
70 * \note This function finds a strategy type using the same rules as create(),
71 * but does not attempt to construct an instance.
Junxiao Shic34d1672016-12-09 15:57:59 +000072 */
73 static bool
Junxiao Shi18739c42016-12-22 08:03:00 +000074 canCreate(const Name& instanceName);
Junxiao Shic34d1672016-12-09 15:57:59 +000075
Davide Pesaventoaa9e3b22022-10-21 17:00:07 -040076 /**
77 * \brief Returns a strategy instance created from \p instanceName.
78 * \retval nullptr if `canCreate(instanceName) == false`
79 * \throw std::invalid_argument strategy type constructor does not accept the
80 * specified version or parameters
Junxiao Shic34d1672016-12-09 15:57:59 +000081 */
82 static unique_ptr<Strategy>
Junxiao Shi18739c42016-12-22 08:03:00 +000083 create(const Name& instanceName, Forwarder& forwarder);
Junxiao Shic34d1672016-12-09 15:57:59 +000084
Davide Pesaventoaa9e3b22022-10-21 17:00:07 -040085 /**
86 * \brief Returns whether two names will instantiate the same strategy type.
Junxiao Shi55e21b92017-01-23 03:27:47 +000087 */
88 static bool
89 areSameType(const Name& instanceNameA, const Name& instanceNameB);
90
Davide Pesaventoaa9e3b22022-10-21 17:00:07 -040091 /**
92 * \brief Returns all registered versioned strategy names.
Junxiao Shic34d1672016-12-09 15:57:59 +000093 */
Davide Pesaventoaa9e3b22022-10-21 17:00:07 -040094 [[nodiscard]] static std::set<Name>
Junxiao Shic34d1672016-12-09 15:57:59 +000095 listRegistered();
96
Ju Pan2feb4592019-09-16 20:56:38 +000097public: // constructor, destructor, strategy info
Davide Pesavento3dade002019-03-19 11:29:56 -060098 /** \brief Construct a strategy instance.
Junxiao Shi18739c42016-12-22 08:03:00 +000099 * \param forwarder a reference to the forwarder, used to enable actions and accessors.
Davide Pesavento3dade002019-03-19 11:29:56 -0600100 * \note Strategy subclass constructor must not retain a reference to \p forwarder.
Junxiao Shie93d6a32014-09-07 16:13:22 -0700101 */
Junxiao Shi18739c42016-12-22 08:03:00 +0000102 explicit
103 Strategy(Forwarder& forwarder);
Junxiao Shidbe71732014-02-21 22:23:28 -0700104
Junxiao Shid3c792f2014-01-30 00:46:13 -0700105 virtual
106 ~Strategy();
Junxiao Shidbe71732014-02-21 22:23:28 -0700107
Junxiao Shi037f4ab2016-12-13 04:27:06 +0000108#ifdef DOXYGEN
Davide Pesaventoaa9e3b22022-10-21 17:00:07 -0400109 /**
110 * \brief Returns the strategy's program name.
Junxiao Shi18739c42016-12-22 08:03:00 +0000111 *
Davide Pesaventoaa9e3b22022-10-21 17:00:07 -0400112 * The strategy name is defined by the strategy program.
113 * It must end with a version component.
Junxiao Shi037f4ab2016-12-13 04:27:06 +0000114 */
115 static const Name&
116 getStrategyName();
117#endif
118
Davide Pesaventoaa9e3b22022-10-21 17:00:07 -0400119 /**
120 * \brief Returns the strategy's instance name.
Junxiao Shi18739c42016-12-22 08:03:00 +0000121 *
Davide Pesaventoaa9e3b22022-10-21 17:00:07 -0400122 * The instance name is assigned during instantiation.
123 * It contains a version component and may have extra parameter components.
Junxiao Shib9420cf2016-08-13 04:38:52 +0000124 */
Junxiao Shibb5105f2014-03-03 12:06:45 -0700125 const Name&
Davide Pesaventoaa9e3b22022-10-21 17:00:07 -0400126 getInstanceName() const noexcept
Junxiao Shib9420cf2016-08-13 04:38:52 +0000127 {
128 return m_name;
129 }
Junxiao Shibb5105f2014-03-03 12:06:45 -0700130
Junxiao Shi679e9272014-02-15 20:10:21 -0700131public: // triggers
Davide Pesavento0498ce82021-06-14 02:02:21 -0400132 /**
133 * \brief Trigger after an Interest is received.
Junxiao Shi679e9272014-02-15 20:10:21 -0700134 *
Davide Pesavento0498ce82021-06-14 02:02:21 -0400135 * The Interest:
Eric Newberryc68b2e82020-04-16 12:40:30 -0700136 * - has not exceeded HopLimit
Junxiao Shi679e9272014-02-15 20:10:21 -0700137 * - does not violate Scope
Mark Theeranantachai195b78a2024-02-14 20:54:44 -0500138 * - has not looped
Junxiao Shi679e9272014-02-15 20:10:21 -0700139 * - cannot be satisfied by ContentStore
140 * - is under a namespace managed by this strategy
141 *
Davide Pesavento0498ce82021-06-14 02:02:21 -0400142 * The PIT entry is set to expire after InterestLifetime has elapsed at each downstream.
Teng Liang7003e0b2018-03-03 16:03:30 -0700143 *
Davide Pesavento0498ce82021-06-14 02:02:21 -0400144 * The strategy should decide whether and where to forward this Interest.
Junxiao Shi679e9272014-02-15 20:10:21 -0700145 * - If the strategy decides to forward this Interest,
Davide Pesavento0498ce82021-06-14 02:02:21 -0400146 * invoke sendInterest() for each upstream, either now or shortly after via a scheduler event,
147 * but before the PIT entry expires.
148 * Optionally, the strategy can invoke setExpiryTimer() to adjust how long it would wait for a response.
149 * - If the strategy has already forwarded this Interest previously and decides to continue
150 * waiting, do nothing.
151 * Optionally, the strategy can invoke setExpiryTimer() to adjust how long it would wait for a response.
Teng Liang7003e0b2018-03-03 16:03:30 -0700152 * - If the strategy concludes that this Interest cannot be satisfied,
Davide Pesavento0498ce82021-06-14 02:02:21 -0400153 * invoke rejectPendingInterest() to erase the PIT entry.
Junxiao Shi82e7f582014-09-07 15:15:40 -0700154 *
Davide Pesavento0498ce82021-06-14 02:02:21 -0400155 * \warning The strategy must not retain a copy of the \p pitEntry shared_ptr after this function
156 * returns, otherwise undefined behavior may occur. However, the strategy is allowed to
157 * construct and keep a weak_ptr to \p pitEntry.
Junxiao Shi679e9272014-02-15 20:10:21 -0700158 */
Junxiao Shid3c792f2014-01-30 00:46:13 -0700159 virtual void
Davide Pesavento0498ce82021-06-14 02:02:21 -0400160 afterReceiveInterest(const Interest& interest, const FaceEndpoint& ingress,
Junxiao Shi15e98b02016-08-12 11:21:44 +0000161 const shared_ptr<pit::Entry>& pitEntry) = 0;
Junxiao Shidbe71732014-02-21 22:23:28 -0700162
Davide Pesavento0498ce82021-06-14 02:02:21 -0400163 /**
Mark Theeranantachai195b78a2024-02-14 20:54:44 -0500164 * \brief Trigger after an Interest loop is detected.
165 *
166 * The Interest:
167 * - has not exceeded HopLimit
168 * - does not violate Scope
169 * - has looped
170 * - is under a namespace managed by this strategy
171 *
172 * In the base class, this method sends a Nack with reason DUPLICATE to \p ingress.
173 */
174 virtual void
175 onInterestLoop(const Interest& interest, const FaceEndpoint& ingress);
176
177 /**
Davide Pesavento0498ce82021-06-14 02:02:21 -0400178 * \brief Trigger after a matching Data is found in the Content Store.
Junxiao Shi22be22c2014-02-16 22:53:48 -0700179 *
Davide Pesavento0498ce82021-06-14 02:02:21 -0400180 * In the base class, this method sends \p data to \p ingress.
Teng Liang7003e0b2018-03-03 16:03:30 -0700181 *
Davide Pesavento0498ce82021-06-14 02:02:21 -0400182 * \warning The strategy must not retain a copy of the \p pitEntry shared_ptr after this function
183 * returns, otherwise undefined behavior may occur. However, the strategy is allowed to
184 * construct and keep a weak_ptr to \p pitEntry.
Junxiao Shi22be22c2014-02-16 22:53:48 -0700185 */
186 virtual void
Davide Pesavento0498ce82021-06-14 02:02:21 -0400187 afterContentStoreHit(const Data& data, const FaceEndpoint& ingress,
188 const shared_ptr<pit::Entry>& pitEntry);
Junxiao Shidbe71732014-02-21 22:23:28 -0700189
Davide Pesavento0498ce82021-06-14 02:02:21 -0400190 /**
191 * \brief Trigger before a PIT entry is satisfied.
Teng Liang85a36632018-03-21 05:59:34 -0700192 *
Davide Pesavento0498ce82021-06-14 02:02:21 -0400193 * This trigger is invoked when an incoming Data satisfies more than one PIT entry.
194 * The strategy can collect measurements information, but cannot manipulate Data forwarding.
195 * When an incoming Data satisfies only one PIT entry, afterReceiveData() is invoked instead
196 * and given full control over Data forwarding. If a strategy does not override afterReceiveData(),
197 * the default implementation invokes beforeSatisfyInterest().
198 *
199 * Normally, PIT entries are erased after receiving the first matching Data.
200 * If the strategy wishes to collect responses from additional upstream nodes,
201 * it should invoke setExpiryTimer() within this function to prolong the PIT entry lifetime.
202 * If a Data arrives from another upstream during the extended PIT entry lifetime, this trigger
203 * will be invoked again. At that time, the strategy must invoke setExpiryTimer() again to
204 * continue collecting more responses.
205 *
206 * In the base class, this method does nothing.
207 *
208 * \warning The strategy must not retain a copy of the \p pitEntry shared_ptr after this function
209 * returns, otherwise undefined behavior may occur. However, the strategy is allowed to
210 * construct and keep a weak_ptr to \p pitEntry.
Teng Liang85a36632018-03-21 05:59:34 -0700211 */
212 virtual void
Davide Pesavento0498ce82021-06-14 02:02:21 -0400213 beforeSatisfyInterest(const Data& data, const FaceEndpoint& ingress,
214 const shared_ptr<pit::Entry>& pitEntry);
Teng Liang85a36632018-03-21 05:59:34 -0700215
Davide Pesavento0498ce82021-06-14 02:02:21 -0400216 /**
217 * \brief Trigger after Data is received.
Teng Liang43bb2312018-03-26 04:16:42 -0700218 *
Davide Pesavento0498ce82021-06-14 02:02:21 -0400219 * This trigger is invoked when an incoming Data satisfies exactly one PIT entry,
220 * and gives the strategy full control over Data forwarding.
Teng Liang43bb2312018-03-26 04:16:42 -0700221 *
Davide Pesavento0498ce82021-06-14 02:02:21 -0400222 * When this trigger is invoked:
Teng Liang43bb2312018-03-26 04:16:42 -0700223 * - The Data has been verified to satisfy the PIT entry.
224 * - The PIT entry expiry timer is set to now
225 *
Davide Pesavento0498ce82021-06-14 02:02:21 -0400226 * Inside this function:
227 * - A strategy should return Data to downstream nodes via sendData() or sendDataToAll().
Teng Liang43bb2312018-03-26 04:16:42 -0700228 * - A strategy can modify the Data as long as it still satisfies the PIT entry, such as
229 * adding or removing congestion marks.
Davide Pesavento0498ce82021-06-14 02:02:21 -0400230 * - A strategy can delay Data forwarding by prolonging the PIT entry lifetime via setExpiryTimer(),
231 * and later forward the Data before the PIT entry is erased.
Teng Liang43bb2312018-03-26 04:16:42 -0700232 * - A strategy can collect measurements about the upstream.
233 * - A strategy can collect responses from additional upstream nodes by prolonging the PIT entry
Davide Pesavento0498ce82021-06-14 02:02:21 -0400234 * lifetime via setExpiryTimer() every time a Data is received. Note that only one Data should
Teng Liang43bb2312018-03-26 04:16:42 -0700235 * be returned to each downstream node.
236 *
Davide Pesavento0498ce82021-06-14 02:02:21 -0400237 * In the base class, this method invokes beforeSatisfyInterest() and then returns the Data
238 * to all downstream faces via sendDataToAll().
239 *
240 * \warning The strategy must not retain a copy of the \p pitEntry shared_ptr after this function
241 * returns, otherwise undefined behavior may occur. However, the strategy is allowed to
242 * construct and keep a weak_ptr to \p pitEntry.
Teng Liang43bb2312018-03-26 04:16:42 -0700243 */
244 virtual void
Davide Pesavento0498ce82021-06-14 02:02:21 -0400245 afterReceiveData(const Data& data, const FaceEndpoint& ingress,
Junxiao Shi15e98b02016-08-12 11:21:44 +0000246 const shared_ptr<pit::Entry>& pitEntry);
Junxiao Shi5e5e4452015-09-24 16:56:52 -0700247
Davide Pesavento0498ce82021-06-14 02:02:21 -0400248 /**
249 * \brief Trigger after a Nack is received.
Eric Newberry41aba102017-11-01 16:42:13 -0700250 *
Davide Pesavento0498ce82021-06-14 02:02:21 -0400251 * This trigger is invoked when an incoming Nack is received in response to
252 * an forwarded Interest.
253 * The Nack has been confirmed to be a response to the last Interest forwarded
254 * to that upstream, i.e. the PIT out-record exists and has a matching Nonce.
255 * The NackHeader has been recorded in the PIT out-record.
256 *
257 * If the PIT entry is not yet satisfied, its expiry timer remains unchanged.
258 * Otherwise, the PIT entry will normally expire immediately after this function returns.
259 *
260 * If the strategy wishes to collect responses from additional upstream nodes,
261 * it should invoke setExpiryTimer() within this function to prolong the PIT entry lifetime.
262 * If a Nack arrives from another upstream during the extended PIT entry lifetime, this trigger
263 * will be invoked again. At that time, the strategy must invoke setExpiryTimer() again to
264 * continue collecting more responses.
265 *
266 * In the base class, this method does nothing.
267 *
268 * \warning The strategy must not retain a copy of the \p pitEntry shared_ptr after this function
269 * returns, otherwise undefined behavior may occur. However, the strategy is allowed to
270 * construct and keep a weak_ptr to \p pitEntry.
Eric Newberry41aba102017-11-01 16:42:13 -0700271 */
272 virtual void
Davide Pesavento0498ce82021-06-14 02:02:21 -0400273 afterReceiveNack(const lp::Nack& nack, const FaceEndpoint& ingress,
274 const shared_ptr<pit::Entry>& pitEntry);
Eric Newberry41aba102017-11-01 16:42:13 -0700275
Davide Pesavento0498ce82021-06-14 02:02:21 -0400276 /**
277 * \brief Trigger after an Interest is dropped (e.g., for exceeding allowed retransmissions).
Ju Pan2feb4592019-09-16 20:56:38 +0000278 *
Davide Pesavento0498ce82021-06-14 02:02:21 -0400279 * In the base class, this method does nothing.
280 */
281 virtual void
282 onDroppedInterest(const Interest& interest, Face& egress);
283
284 /**
285 * \brief Trigger after a new nexthop is added.
286 *
287 * The strategy should decide whether to send the buffered Interests to the new nexthop.
288 *
289 * In the base class, this method does nothing.
Ju Pan2feb4592019-09-16 20:56:38 +0000290 */
291 virtual void
292 afterNewNextHop(const fib::NextHop& nextHop, const shared_ptr<pit::Entry>& pitEntry);
293
Junxiao Shid3c792f2014-01-30 00:46:13 -0700294protected: // actions
Davide Pesavento0498ce82021-06-14 02:02:21 -0400295 /**
296 * \brief Send an Interest packet.
297 * \param interest the Interest packet
298 * \param egress face through which to send out the Interest
299 * \param pitEntry the PIT entry
300 * \return A pointer to the out-record created or nullptr if the Interest was dropped
Junxiao Shi5e5e4452015-09-24 16:56:52 -0700301 */
Davide Pesavento264af772021-02-09 21:48:24 -0500302 NFD_VIRTUAL_WITH_TESTS pit::OutRecord*
Davide Pesavento0498ce82021-06-14 02:02:21 -0400303 sendInterest(const Interest& interest, Face& egress, const shared_ptr<pit::Entry>& pitEntry);
Junxiao Shidbe71732014-02-21 22:23:28 -0700304
Davide Pesavento0498ce82021-06-14 02:02:21 -0400305 /**
306 * \brief Send a Data packet.
307 * \param data the Data packet
308 * \param egress face through which to send out the Data
309 * \param pitEntry the PIT entry
310 * \return Whether the Data was sent (true) or dropped (false)
Teng Liang85a36632018-03-21 05:59:34 -0700311 */
Davide Pesavento264af772021-02-09 21:48:24 -0500312 NFD_VIRTUAL_WITH_TESTS bool
Davide Pesavento0498ce82021-06-14 02:02:21 -0400313 sendData(const Data& data, Face& egress, const shared_ptr<pit::Entry>& pitEntry);
Teng Liang85a36632018-03-21 05:59:34 -0700314
Davide Pesavento0498ce82021-06-14 02:02:21 -0400315 /**
316 * \brief Send a Data packet to all matched and qualified faces.
Teng Liang43bb2312018-03-26 04:16:42 -0700317 *
Davide Pesavento0498ce82021-06-14 02:02:21 -0400318 * A matched face qualifies if it is ad-hoc OR it is NOT \p inFace.
Teng Liang43bb2312018-03-26 04:16:42 -0700319 *
Davide Pesavento0498ce82021-06-14 02:02:21 -0400320 * \param data the Data packet
321 * \param pitEntry the PIT entry
322 * \param inFace face on which the Data arrived
Teng Liang43bb2312018-03-26 04:16:42 -0700323 */
Davide Pesavento264af772021-02-09 21:48:24 -0500324 NFD_VIRTUAL_WITH_TESTS void
Davide Pesavento0498ce82021-06-14 02:02:21 -0400325 sendDataToAll(const Data& data, const shared_ptr<pit::Entry>& pitEntry, const Face& inFace);
Teng Liang85a36632018-03-21 05:59:34 -0700326
Davide Pesavento0498ce82021-06-14 02:02:21 -0400327 /**
328 * \brief Schedule the PIT entry for immediate deletion.
Junxiao Shi679e9272014-02-15 20:10:21 -0700329 *
Davide Pesavento0498ce82021-06-14 02:02:21 -0400330 * This helper function sets the PIT entry expiry time to zero.
331 * The strategy should invoke this function when it concludes that the Interest cannot
332 * be forwarded and it does not want to wait for responses from existing upstream nodes.
Junxiao Shid3c792f2014-01-30 00:46:13 -0700333 */
Davide Pesavento264af772021-02-09 21:48:24 -0500334 NFD_VIRTUAL_WITH_TESTS void
Junxiao Shib9420cf2016-08-13 04:38:52 +0000335 rejectPendingInterest(const shared_ptr<pit::Entry>& pitEntry)
336 {
Teng Liang7003e0b2018-03-03 16:03:30 -0700337 this->setExpiryTimer(pitEntry, 0_ms);
Junxiao Shib9420cf2016-08-13 04:38:52 +0000338 }
Junxiao Shidbe71732014-02-21 22:23:28 -0700339
Davide Pesavento0498ce82021-06-14 02:02:21 -0400340 /**
341 * \brief Send a Nack packet.
Junxiao Shi5e5e4452015-09-24 16:56:52 -0700342 *
Davide Pesavento0498ce82021-06-14 02:02:21 -0400343 * The egress face must have a PIT in-record, otherwise this method has no effect.
Teng Liangebc20f62020-06-23 16:55:20 -0700344 *
Davide Pesavento0498ce82021-06-14 02:02:21 -0400345 * \param header the Nack header
346 * \param egress face through which to send out the Nack
347 * \param pitEntry the PIT entry
348 * \return Whether the Nack was sent (true) or dropped (false)
Junxiao Shi5e5e4452015-09-24 16:56:52 -0700349 */
Davide Pesavento264af772021-02-09 21:48:24 -0500350 NFD_VIRTUAL_WITH_TESTS bool
Davide Pesavento0498ce82021-06-14 02:02:21 -0400351 sendNack(const lp::NackHeader& header, Face& egress, const shared_ptr<pit::Entry>& pitEntry)
Junxiao Shib9420cf2016-08-13 04:38:52 +0000352 {
Davide Pesavento0498ce82021-06-14 02:02:21 -0400353 return m_forwarder.onOutgoingNack(header, egress, pitEntry);
Junxiao Shib9420cf2016-08-13 04:38:52 +0000354 }
Junxiao Shi5e5e4452015-09-24 16:56:52 -0700355
Davide Pesavento0498ce82021-06-14 02:02:21 -0400356 /**
Mark Theeranantachai195b78a2024-02-14 20:54:44 -0500357 * \brief Send a Nack packet without going through the outgoing Nack pipeline.
358 *
359 * \param nack the Nack packet
360 * \param egress face through which to send out the Nack
361 * \return Whether the Nack was sent (true) or dropped (false)
362 */
363 NFD_VIRTUAL_WITH_TESTS bool
364 sendNack(const lp::Nack& nack, Face& egress)
365 {
366 egress.sendNack(nack);
367 ++m_forwarder.m_counters.nOutNacks;
368 return true;
369 }
370
371 /**
Davide Pesavento0498ce82021-06-14 02:02:21 -0400372 * \brief Send Nack to every face that has an in-record, except those in \p exceptFaces
373 * \param header the Nack header
374 * \param pitEntry the PIT entry
375 * \param exceptFaces list of faces that should be excluded from sending Nacks
376 * \note This is not an action, but a helper that invokes the sendNack() action.
Junxiao Shi5e5e4452015-09-24 16:56:52 -0700377 */
378 void
Davide Pesavento0498ce82021-06-14 02:02:21 -0400379 sendNacks(const lp::NackHeader& header, const shared_ptr<pit::Entry>& pitEntry,
Teng Liangebc20f62020-06-23 16:55:20 -0700380 std::initializer_list<const Face*> exceptFaces = {});
Junxiao Shi5e5e4452015-09-24 16:56:52 -0700381
Davide Pesavento0498ce82021-06-14 02:02:21 -0400382 /**
383 * \brief Schedule the PIT entry to be erased after \p duration.
Teng Liang7003e0b2018-03-03 16:03:30 -0700384 */
385 void
386 setExpiryTimer(const shared_ptr<pit::Entry>& pitEntry, time::milliseconds duration)
387 {
388 m_forwarder.setExpiryTimer(pitEntry, duration);
389 }
390
Junxiao Shidbe71732014-02-21 22:23:28 -0700391protected: // accessors
Davide Pesavento0498ce82021-06-14 02:02:21 -0400392 /**
393 * \brief Performs a FIB lookup, considering Link object if present.
Junxiao Shicf0f3ce2016-09-02 13:01:59 +0000394 */
Junxiao Shi8d843142016-07-11 22:42:42 +0000395 const fib::Entry&
Junxiao Shicf0f3ce2016-09-02 13:01:59 +0000396 lookupFib(const pit::Entry& pitEntry) const;
Junxiao Shi8d843142016-07-11 22:42:42 +0000397
Junxiao Shidbe71732014-02-21 22:23:28 -0700398 MeasurementsAccessor&
Davide Pesaventoaa9e3b22022-10-21 17:00:07 -0400399 getMeasurements() noexcept
Junxiao Shib9420cf2016-08-13 04:38:52 +0000400 {
401 return m_measurements;
402 }
Junxiao Shidbe71732014-02-21 22:23:28 -0700403
Junxiao Shi5b43f9a2016-07-19 13:15:56 +0000404 Face*
Davide Pesaventoaa9e3b22022-10-21 17:00:07 -0400405 getFace(FaceId id) const noexcept
Junxiao Shib9420cf2016-08-13 04:38:52 +0000406 {
Davide Pesaventoa4abfb02019-10-06 16:02:56 -0400407 return getFaceTable().get(id);
Junxiao Shib9420cf2016-08-13 04:38:52 +0000408 }
Junxiao Shi2d9bdc82014-03-02 20:55:42 -0700409
Junxiao Shi49e11e72014-12-14 19:46:05 -0700410 const FaceTable&
Davide Pesaventoaa9e3b22022-10-21 17:00:07 -0400411 getFaceTable() const noexcept
Junxiao Shib9420cf2016-08-13 04:38:52 +0000412 {
Davide Pesaventoa4abfb02019-10-06 16:02:56 -0400413 return m_forwarder.m_faceTable;
Junxiao Shib9420cf2016-08-13 04:38:52 +0000414 }
Junxiao Shi49e11e72014-12-14 19:46:05 -0700415
Junxiao Shi18739c42016-12-22 08:03:00 +0000416protected: // instance name
417 struct ParsedInstanceName
418 {
Ashlesh Gawande1ef93d02022-04-08 00:25:06 -0400419 Name strategyName; ///< Strategy name without parameters
Davide Pesaventob7bfcb92022-05-22 23:55:23 -0400420 std::optional<uint64_t> version; ///< The strategy version number, if present
Ashlesh Gawande1ef93d02022-04-08 00:25:06 -0400421 PartialName parameters; ///< Parameter components, may be empty
Junxiao Shi18739c42016-12-22 08:03:00 +0000422 };
423
Eric Newberryc68b2e82020-04-16 12:40:30 -0700424 /** \brief Parse a strategy instance name
Junxiao Shi18739c42016-12-22 08:03:00 +0000425 * \param input strategy instance name, may contain version and parameters
426 * \throw std::invalid_argument input format is unacceptable
427 */
428 static ParsedInstanceName
429 parseInstanceName(const Name& input);
430
Eric Newberryc68b2e82020-04-16 12:40:30 -0700431 /** \brief Construct a strategy instance name
Junxiao Shi18739c42016-12-22 08:03:00 +0000432 * \param input strategy instance name, may contain version and parameters
433 * \param strategyName strategy name with version but without parameters;
434 * typically this should be \p getStrategyName()
435 *
436 * If \p input contains a version component, return \p input unchanged.
437 * Otherwise, return \p input plus the version component taken from \p strategyName.
438 * This allows a strategy instance to be constructed with an unversioned name,
439 * but its final instance name should contain the version.
440 */
441 static Name
442 makeInstanceName(const Name& input, const Name& strategyName);
443
Eric Newberryc68b2e82020-04-16 12:40:30 -0700444 /** \brief Set strategy instance name
Junxiao Shi18739c42016-12-22 08:03:00 +0000445 * \note This must be called by strategy subclass constructor.
446 */
447 void
Davide Pesaventoaa9e3b22022-10-21 17:00:07 -0400448 setInstanceName(const Name& name) noexcept
Junxiao Shi18739c42016-12-22 08:03:00 +0000449 {
450 m_name = name;
451 }
Junxiao Shi49e11e72014-12-14 19:46:05 -0700452
Ashlesh Gawande1ef93d02022-04-08 00:25:06 -0400453NFD_PUBLIC_WITH_TESTS_ELSE_PROTECTED:
454 /**
455 * \brief Parse strategy parameters encoded in a strategy instance name
456 * \param params encoded parameters, typically obtained from a call to parseInstanceName()
457 * \throw std::invalid_argument the encoding format is invalid or unsupported by this implementation
458 */
459 static StrategyParameters
460 parseParameters(const PartialName& params);
461
Junxiao Shic34d1672016-12-09 15:57:59 +0000462private: // registry
Davide Pesavento0498ce82021-06-14 02:02:21 -0400463 using CreateFunc = std::function<unique_ptr<Strategy>(Forwarder&, const Name& /*strategyName*/)>;
464 using Registry = std::map<Name, CreateFunc>; // indexed by strategy name
Junxiao Shic34d1672016-12-09 15:57:59 +0000465
466 static Registry&
467 getRegistry();
468
469 static Registry::const_iterator
Junxiao Shi18739c42016-12-22 08:03:00 +0000470 find(const Name& instanceName);
471
472protected: // accessors
Davide Pesaventoa4abfb02019-10-06 16:02:56 -0400473 signal::Signal<FaceTable, Face>& afterAddFace;
474 signal::Signal<FaceTable, Face>& beforeRemoveFace;
Junxiao Shic34d1672016-12-09 15:57:59 +0000475
476private: // instance fields
Junxiao Shibb5105f2014-03-03 12:06:45 -0700477 Name m_name;
Junxiao Shi8c8d2182014-01-30 22:33:00 -0700478 Forwarder& m_forwarder;
Junxiao Shidbe71732014-02-21 22:23:28 -0700479 MeasurementsAccessor m_measurements;
Junxiao Shid3c792f2014-01-30 00:46:13 -0700480};
481
Ashlesh Gawande1ef93d02022-04-08 00:25:06 -0400482class StrategyParameters : public std::map<std::string, std::string>
483{
484public:
485 // Note: only arithmetic types are supported by getOrDefault() for now
486
487 template<typename T>
Davide Pesaventob7bfcb92022-05-22 23:55:23 -0400488 std::enable_if_t<std::is_signed_v<T>, T>
Ashlesh Gawande1ef93d02022-04-08 00:25:06 -0400489 getOrDefault(const key_type& key, const T& defaultVal) const
490 {
491 auto it = find(key);
492 if (it == end()) {
493 return defaultVal;
494 }
495
496 T val{};
497 if (!boost::conversion::try_lexical_convert(it->second, val)) {
498 NDN_THROW(std::invalid_argument(key + " value is malformed"));
499 }
500 return val;
501 }
502
503 template<typename T>
Davide Pesaventob7bfcb92022-05-22 23:55:23 -0400504 std::enable_if_t<std::is_unsigned_v<T>, T>
Ashlesh Gawande1ef93d02022-04-08 00:25:06 -0400505 getOrDefault(const key_type& key, const T& defaultVal) const
506 {
507 auto it = find(key);
508 if (it == end()) {
509 return defaultVal;
510 }
511
512 if (it->second.find('-') != std::string::npos) {
513 NDN_THROW(std::invalid_argument(key + " cannot be negative"));
514 }
515
516 T val{};
517 if (!boost::conversion::try_lexical_convert(it->second, val)) {
518 NDN_THROW(std::invalid_argument(key + " value is malformed"));
519 }
520 return val;
521 }
522};
523
Davide Pesaventoe422f9e2022-06-03 01:30:23 -0400524} // namespace nfd::fw
Junxiao Shid3c792f2014-01-30 00:46:13 -0700525
Alex Lane653eb072023-07-27 22:11:46 -0400526/**
527 * \brief Registers a forwarding strategy.
Junxiao Shic34d1672016-12-09 15:57:59 +0000528 *
Alex Lane653eb072023-07-27 22:11:46 -0400529 * This macro should appear once in the `.cpp` of each strategy.
Junxiao Shic34d1672016-12-09 15:57:59 +0000530 */
531#define NFD_REGISTER_STRATEGY(S) \
532static class NfdAuto ## S ## StrategyRegistrationClass \
533{ \
534public: \
535 NfdAuto ## S ## StrategyRegistrationClass() \
536 { \
537 ::nfd::fw::Strategy::registerType<S>(); \
538 } \
539} g_nfdAuto ## S ## StrategyRegistrationVariable
540
Alex Lane653eb072023-07-27 22:11:46 -0400541/// Logs the reception of \p interest on \p ingress, followed by \p msg, at DEBUG level.
542#define NFD_LOG_INTEREST_FROM(interest, ingress, msg) \
543 NFD_LOG_DEBUG("interest=" << (interest).getName() << \
544 " nonce=" << (interest).getNonce() << \
545 " from=" << (ingress) << \
546 ' ' << msg)
547
548/// Logs the reception of \p data on \p ingress, followed by \p msg, at DEBUG level.
549#define NFD_LOG_DATA_FROM(data, ingress, msg) \
550 NFD_LOG_DEBUG("data=" << (data).getName() << \
551 " from=" << (ingress) << \
552 ' ' << msg)
553
554/// Logs the reception of \p nack on \p ingress, followed by \p msg, at DEBUG level.
555#define NFD_LOG_NACK_FROM(nack, ingress, msg) \
556 NFD_LOG_DEBUG("nack=" << (nack).getInterest().getName() << \
557 " nonce=" << (nack).getInterest().getNonce() << \
558 " reason=" << (nack).getReason() << \
559 " from=" << (ingress) << \
560 ' ' << msg)
561
Alexander Afanasyev613e2a92014-04-15 13:36:58 -0700562#endif // NFD_DAEMON_FW_STRATEGY_HPP