blob: 5a43a26499dfdd46138d128b6bfa37efb5c8561f [file] [log] [blame]
Teng Liang39465c22018-11-12 19:43:04 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/*
3 * Copyright (c) 2014-2019, Regents of the University of California,
4 * 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.
10 *
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#include "self-learning-strategy.hpp"
27#include "algorithm.hpp"
28
29#include "core/logger.hpp"
30#include "core/global-io.hpp"
31#include "rib/service.hpp"
32
33#include <ndn-cxx/lp/empty-value.hpp>
34#include <ndn-cxx/lp/prefix-announcement-header.hpp>
35#include <ndn-cxx/lp/tags.hpp>
36
37#include <boost/range/adaptor/reversed.hpp>
38
39namespace nfd {
40namespace fw {
41
42NFD_LOG_INIT(SelfLearningStrategy);
43NFD_REGISTER_STRATEGY(SelfLearningStrategy);
44
45const time::milliseconds SelfLearningStrategy::ROUTE_RENEW_LIFETIME(600_s);
46
47SelfLearningStrategy::SelfLearningStrategy(Forwarder& forwarder, const Name& name)
48 : Strategy(forwarder)
49{
50 ParsedInstanceName parsed = parseInstanceName(name);
51 if (!parsed.parameters.empty()) {
52 BOOST_THROW_EXCEPTION(std::invalid_argument("SelfLearningStrategy does not accept parameters"));
53 }
54 if (parsed.version && *parsed.version != getStrategyName()[-1].toVersion()) {
55 BOOST_THROW_EXCEPTION(std::invalid_argument(
56 "SelfLearningStrategy does not support version " + to_string(*parsed.version)));
57 }
58 this->setInstanceName(makeInstanceName(name, getStrategyName()));
59}
60
61const Name&
62SelfLearningStrategy::getStrategyName()
63{
64 static Name strategyName("/localhost/nfd/strategy/self-learning/%FD%01");
65 return strategyName;
66}
67
68void
69SelfLearningStrategy::afterReceiveInterest(const Face& inFace, const Interest& interest,
70 const shared_ptr<pit::Entry>& pitEntry)
71{
72 const fib::Entry& fibEntry = this->lookupFib(*pitEntry);
73 const fib::NextHopList& nexthops = fibEntry.getNextHops();
74
75 bool isNonDiscovery = interest.getTag<lp::NonDiscoveryTag>() != nullptr;
76 auto inRecordInfo = pitEntry->getInRecord(inFace)->insertStrategyInfo<InRecordInfo>().first;
77 if (isNonDiscovery) { // "non-discovery" Interest
78 inRecordInfo->isNonDiscoveryInterest = true;
79 if (nexthops.empty()) { // return NACK if no matching FIB entry exists
80 NFD_LOG_DEBUG("NACK non-discovery Interest=" << interest << " from=" << inFace.getId() << " noNextHop");
81 lp::NackHeader nackHeader;
82 nackHeader.setReason(lp::NackReason::NO_ROUTE);
83 this->sendNack(pitEntry, inFace, nackHeader);
84 this->rejectPendingInterest(pitEntry);
85 }
86 else { // multicast it if matching FIB entry exists
87 multicastInterest(interest, inFace, pitEntry, nexthops);
88 }
89 }
90 else { // "discovery" Interest
91 inRecordInfo->isNonDiscoveryInterest = false;
92 if (nexthops.empty()) { // broadcast it if no matching FIB entry exists
93 broadcastInterest(interest, inFace, pitEntry);
94 }
95 else { // multicast it with "non-discovery" mark if matching FIB entry exists
96 interest.setTag(make_shared<lp::NonDiscoveryTag>(lp::EmptyValue{}));
97 multicastInterest(interest, inFace, pitEntry, nexthops);
98 }
99 }
100}
101
102void
103SelfLearningStrategy::afterReceiveData(const shared_ptr<pit::Entry>& pitEntry,
104 const Face& inFace, const Data& data)
105{
106 OutRecordInfo* outRecordInfo = pitEntry->getOutRecord(inFace)->getStrategyInfo<OutRecordInfo>();
107 if (outRecordInfo && outRecordInfo->isNonDiscoveryInterest) { // outgoing Interest was non-discovery
108 if (!needPrefixAnn(pitEntry)) { // no need to attach a PA (common cases)
109 sendDataToAll(pitEntry, inFace, data);
110 }
111 else { // needs a PA (to respond discovery Interest)
112 asyncProcessData(pitEntry, inFace, data);
113 }
114 }
115 else { // outgoing Interest was discovery
116 auto paTag = data.getTag<lp::PrefixAnnouncementTag>();
117 if (paTag != nullptr) {
118 addRoute(pitEntry, inFace, data, *paTag->get().getPrefixAnn());
119 }
120 else { // Data contains no PrefixAnnouncement, upstreams do not support self-learning
121 }
122 sendDataToAll(pitEntry, inFace, data);
123 }
124}
125
126void
127SelfLearningStrategy::afterReceiveNack(const Face& inFace, const lp::Nack& nack,
128 const shared_ptr<pit::Entry>& pitEntry)
129{
130 NFD_LOG_DEBUG("Nack for " << nack.getInterest() << " from=" << inFace.getId() << ": " << nack.getReason());
131 if (nack.getReason() == lp::NackReason::NO_ROUTE) { // remove FIB entries
132 BOOST_ASSERT(this->lookupFib(*pitEntry).hasNextHops());
133 NFD_LOG_DEBUG("Send NACK to all downstreams");
134 this->sendNacks(pitEntry, nack.getHeader());
135 renewRoute(nack.getInterest().getName(), inFace.getId(), 0_ms);
136 }
137}
138
139void
140SelfLearningStrategy::broadcastInterest(const Interest& interest, const Face& inFace,
141 const shared_ptr<pit::Entry>& pitEntry)
142{
143 for (auto& outFace : this->getFaceTable() | boost::adaptors::reversed) {
144 if ((outFace.getId() == inFace.getId() && outFace.getLinkType() != ndn::nfd::LINK_TYPE_AD_HOC) ||
145 wouldViolateScope(inFace, interest, outFace) || outFace.getScope() == ndn::nfd::FACE_SCOPE_LOCAL) {
146 continue;
147 }
148 this->sendInterest(pitEntry, outFace, interest);
149 pitEntry->getOutRecord(outFace)->insertStrategyInfo<OutRecordInfo>().first->isNonDiscoveryInterest = false;
150 NFD_LOG_DEBUG("send discovery Interest=" << interest << " from="
151 << inFace.getId() << " to=" << outFace.getId());
152 }
153}
154
155void
156SelfLearningStrategy::multicastInterest(const Interest& interest, const Face& inFace,
157 const shared_ptr<pit::Entry>& pitEntry,
158 const fib::NextHopList& nexthops)
159{
160 for (const auto& nexthop : nexthops) {
161 Face& outFace = nexthop.getFace();
162 if ((outFace.getId() == inFace.getId() && outFace.getLinkType() != ndn::nfd::LINK_TYPE_AD_HOC) ||
163 wouldViolateScope(inFace, interest, outFace)) {
164 continue;
165 }
166 this->sendInterest(pitEntry, outFace, interest);
167 pitEntry->getOutRecord(outFace)->insertStrategyInfo<OutRecordInfo>().first->isNonDiscoveryInterest = true;
168 NFD_LOG_DEBUG("send non-discovery Interest=" << interest << " from="
169 << inFace.getId() << " to=" << outFace.getId());
170 }
171}
172
173void
174SelfLearningStrategy::asyncProcessData(const shared_ptr<pit::Entry>& pitEntry, const Face& inFace, const Data& data)
175{
176 // Given that this processing is asynchronous, the PIT entry's expiry timer is extended first
177 // to ensure that the entry will not be removed before the whole processing is finished
178 // (the PIT entry's expiry timer was set to 0 before dispatching)
179 this->setExpiryTimer(pitEntry, 1_s);
180
181 runOnRibIoService([pitEntryWeak = weak_ptr<pit::Entry>{pitEntry}, inFaceId = inFace.getId(), data, this] {
182 rib::Service::get().getRibManager().slFindAnn(data.getName(),
183 [pitEntryWeak, inFaceId, data, this] (optional<ndn::PrefixAnnouncement> paOpt) {
184 if (paOpt) {
185 runOnMainIoService([pitEntryWeak, inFaceId, data, pa = std::move(*paOpt), this] {
186 auto pitEntry = pitEntryWeak.lock();
187 auto inFace = this->getFace(inFaceId);
188 if (pitEntry && inFace) {
189 NFD_LOG_DEBUG("found PrefixAnnouncement=" << pa.getAnnouncedName());
190 data.setTag(make_shared<lp::PrefixAnnouncementTag>(lp::PrefixAnnouncementHeader(pa)));
191 this->sendDataToAll(pitEntry, *inFace, data);
192 this->setExpiryTimer(pitEntry, 0_ms);
193 }
194 else {
195 NFD_LOG_DEBUG("PIT entry or Face no longer exists");
196 }
197 });
198 }
199 });
200 });
201}
202
203bool
204SelfLearningStrategy::needPrefixAnn(const shared_ptr<pit::Entry>& pitEntry)
205{
206 bool hasDiscoveryInterest = false;
207 bool directToConsumer = true;
208
209 auto now = time::steady_clock::now();
210 for (const auto& inRecord : pitEntry->getInRecords()) {
211 if (inRecord.getExpiry() > now) {
212 InRecordInfo* inRecordInfo = inRecord.getStrategyInfo<InRecordInfo>();
213 if (inRecordInfo && !inRecordInfo->isNonDiscoveryInterest) {
214 hasDiscoveryInterest = true;
215 }
216 if (inRecord.getFace().getScope() != ndn::nfd::FACE_SCOPE_LOCAL) {
217 directToConsumer = false;
218 }
219 }
220 }
221 return hasDiscoveryInterest && !directToConsumer;
222}
223
224void
225SelfLearningStrategy::addRoute(const shared_ptr<pit::Entry>& pitEntry, const Face& inFace,
226 const Data& data, const ndn::PrefixAnnouncement& pa)
227{
228 runOnRibIoService([pitEntryWeak = weak_ptr<pit::Entry>{pitEntry}, inFaceId = inFace.getId(), data, pa] {
229 rib::Service::get().getRibManager().slAnnounce(pa, inFaceId, ROUTE_RENEW_LIFETIME,
230 [] (rib::RibManager::SlAnnounceResult res) {
231 NFD_LOG_DEBUG("Add route via PrefixAnnouncement with result=" << res);
232 });
233 });
234}
235
236void
237SelfLearningStrategy::renewRoute(const Name& name, FaceId inFaceId, time::milliseconds maxLifetime)
238{
239 // renew route with PA or ignore PA (if route has no PA)
240 runOnRibIoService([name, inFaceId, maxLifetime] {
241 rib::Service::get().getRibManager().slRenew(name, inFaceId, maxLifetime,
242 [] (rib::RibManager::SlAnnounceResult res) {
243 NFD_LOG_DEBUG("Renew route with result=" << res);
244 });
245 });
246}
247
248} // namespace fw
249} // namespace nfd