blob: c585bf555f0da4b08b24453e60ba16886caa3f98 [file] [log] [blame]
Junxiao Shi89c0ea02017-03-06 19:52:05 +00001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
3 * Copyright (c) 2014-2017, 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 "readvertise.hpp"
27#include "core/logger.hpp"
28#include "core/random.hpp"
29
30namespace nfd {
31namespace rib {
32
33NFD_LOG_INIT("Readvertise");
34
35const time::milliseconds Readvertise::RETRY_DELAY_MIN = time::seconds(50);
36const time::milliseconds Readvertise::RETRY_DELAY_MAX = time::seconds(3600);
37
38static time::milliseconds
39randomizeTimer(time::milliseconds baseTimer)
40{
41 std::uniform_int_distribution<uint64_t> dist(-5, 5);
42 time::milliseconds newTime = baseTimer + time::milliseconds(dist(getGlobalRng()));
43 return std::max(newTime, time::milliseconds(0));
44}
45
46Readvertise::Readvertise(Rib& rib, unique_ptr<ReadvertisePolicy> policy,
47 unique_ptr<ReadvertiseDestination> destination)
48 : m_policy(std::move(policy))
49 , m_destination(std::move(destination))
50{
51 m_addRouteConn = rib.afterAddRoute.connect(bind(&Readvertise::afterAddRoute, this, _1));
52 m_removeRouteConn = rib.beforeRemoveRoute.connect(bind(&Readvertise::beforeRemoveRoute, this, _1));
53
54 m_destination->afterAvailabilityChange.connect([this] (bool isAvailable) {
55 if (isAvailable) {
56 this->afterDestinationAvailable();
57 }
58 else {
59 this->afterDestinationUnavailable();
60 }
61 });
62}
63
64void
65Readvertise::afterAddRoute(const RibRouteRef& ribRoute)
66{
67 ndn::optional<ReadvertiseAction> action = m_policy->handleNewRoute(ribRoute);
68 if (!action) {
69 NFD_LOG_DEBUG("add-route " << ribRoute.entry->getName() << '(' << ribRoute.route->faceId <<
70 ',' << ribRoute.route->origin << ") not-readvertising");
71 return;
72 }
73
74 ReadvertisedRouteContainer::iterator rrIt;
75 bool isNew = false;
76 std::tie(rrIt, isNew) = m_rrs.emplace(action->prefix);
77
78 if (!isNew && rrIt->signer != action->signer) {
79 NFD_LOG_WARN("add-route " << ribRoute.entry->getName() << '(' << ribRoute.route->faceId <<
80 ',' << ribRoute.route->origin << ") readvertising-as " << action->prefix <<
81 " old-signer " << rrIt->signer << " new-signer " << action->signer);
82 }
83 rrIt->signer = action->signer;
84
85 RouteRrIndex::iterator indexIt;
86 std::tie(indexIt, isNew) = m_routeToRr.emplace(ribRoute, rrIt);
87 BOOST_ASSERT(isNew);
88
89 if (rrIt->nRibRoutes++ > 0) {
90 NFD_LOG_DEBUG("add-route " << ribRoute.entry->getName() << '(' << ribRoute.route->faceId <<
91 ',' << ribRoute.route->origin << ") already-readvertised-as " << action->prefix);
92 return;
93 }
94
95 NFD_LOG_DEBUG("add-route " << ribRoute.entry->getName() << '(' << ribRoute.route->faceId <<
96 ',' << ribRoute.route->origin << ") readvertising-as " << action->prefix <<
97 " signer " << action->signer);
98 rrIt->retryDelay = RETRY_DELAY_MIN;
99 this->advertise(rrIt);
100}
101
102void
103Readvertise::beforeRemoveRoute(const RibRouteRef& ribRoute)
104{
105 auto indexIt = m_routeToRr.find(ribRoute);
106 if (indexIt == m_routeToRr.end()) {
107 NFD_LOG_DEBUG("remove-route " << ribRoute.entry->getName() << '(' << ribRoute.route->faceId <<
108 ',' << ribRoute.route->origin << ") not-readvertised");
109 return;
110 }
111
112 auto rrIt = indexIt->second;
113 m_routeToRr.erase(indexIt);
114
115 if (--rrIt->nRibRoutes > 0) {
116 NFD_LOG_DEBUG("remove-route " << ribRoute.entry->getName() << '(' << ribRoute.route->faceId <<
117 ',' << ribRoute.route->origin << ") needed-by " << rrIt->nRibRoutes);
118 return;
119 }
120
121 rrIt->retryDelay = RETRY_DELAY_MIN;
122 this->withdraw(rrIt);
123}
124
125void
126Readvertise::afterDestinationAvailable()
127{
128 for (auto rrIt = m_rrs.begin(); rrIt != m_rrs.end(); ++rrIt) {
129 rrIt->retryDelay = RETRY_DELAY_MIN;
130 this->advertise(rrIt);
131 }
132}
133
134void
135Readvertise::afterDestinationUnavailable()
136{
137 for (auto rrIt = m_rrs.begin(); rrIt != m_rrs.end();) {
138 if (rrIt->nRibRoutes > 0) {
139 rrIt->retryEvt.cancel(); // stop retrying or refreshing
140 ++rrIt;
141 }
142 else {
143 rrIt = m_rrs.erase(rrIt); // assume withdraw has completed
144 }
145 }
146}
147
148void
149Readvertise::advertise(ReadvertisedRouteContainer::iterator rrIt)
150{
151 BOOST_ASSERT(rrIt->nRibRoutes > 0);
152
153 if (!m_destination->isAvailable()) {
154 NFD_LOG_DEBUG("advertise " << rrIt->prefix << " destination-unavailable");
155 return;
156 }
157
158 m_destination->advertise(*rrIt,
159 [this, rrIt] {
160 NFD_LOG_DEBUG("advertise " << rrIt->prefix << " success");
161 rrIt->retryDelay = RETRY_DELAY_MIN;
162 rrIt->retryEvt = scheduler::schedule(randomizeTimer(m_policy->getRefreshInterval()),
163 bind(&Readvertise::advertise, this, rrIt));
164 },
165 [this, rrIt] (const std::string& msg) {
166 NFD_LOG_DEBUG("advertise " << rrIt->prefix << " failure " << msg);
167 rrIt->retryDelay = std::min(RETRY_DELAY_MAX, rrIt->retryDelay * 2);
168 rrIt->retryEvt = scheduler::schedule(randomizeTimer(rrIt->retryDelay),
169 bind(&Readvertise::advertise, this, rrIt));
170 });
171}
172
173void
174Readvertise::withdraw(ReadvertisedRouteContainer::iterator rrIt)
175{
176 BOOST_ASSERT(rrIt->nRibRoutes == 0);
177
178 if (!m_destination->isAvailable()) {
179 NFD_LOG_DEBUG("withdraw " << rrIt->prefix << " destination-unavailable");
180 m_rrs.erase(rrIt);
181 return;
182 }
183
184 m_destination->withdraw(*rrIt,
185 [this, rrIt] {
186 NFD_LOG_DEBUG("withdraw " << rrIt->prefix << " success");
187 m_rrs.erase(rrIt);
188 },
189 [this, rrIt] (const std::string& msg) {
190 NFD_LOG_DEBUG("withdraw " << rrIt->prefix << " failure " << msg);
191 rrIt->retryDelay = std::min(RETRY_DELAY_MAX, rrIt->retryDelay * 2);
192 rrIt->retryEvt = scheduler::schedule(randomizeTimer(rrIt->retryDelay),
193 bind(&Readvertise::withdraw, this, rrIt));
194 });
195}
196
197} // namespace rib
198} // namespace nfd