blob: d7f04cd5bcd58260c24baa44c3cd4a50efa15c7c [file] [log] [blame]
Junxiao Shi38f4ce92016-08-04 10:01:52 +00001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
Davide Pesavento26cbdbd2017-02-19 21:37:43 -05003 * Copyright (c) 2014-2017, Regents of the University of California,
Junxiao Shi38f4ce92016-08-04 10:01:52 +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.
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 "rib-module.hpp"
Junxiao Shi918e5d42017-02-25 03:58:21 +000027#include "find-face.hpp"
Junxiao Shi38f4ce92016-08-04 10:01:52 +000028#include "format-helpers.hpp"
29
30namespace nfd {
31namespace tools {
Junxiao Shi331ade72016-08-19 14:07:19 +000032namespace nfdc {
Junxiao Shi38f4ce92016-08-04 10:01:52 +000033
34void
Junxiao Shi918e5d42017-02-25 03:58:21 +000035RibModule::registerCommands(CommandParser& parser)
36{
Junxiao Shi1d62e622017-03-08 22:39:28 +000037 CommandDefinition defRouteList("route", "list");
38 defRouteList
39 .setTitle("print RIB routes")
40 .addArg("nexthop", ArgValueType::FACE_ID_OR_URI, Required::NO, Positional::YES)
41 .addArg("origin", ArgValueType::UNSIGNED, Required::NO, Positional::NO);
42 parser.addCommand(defRouteList, &RibModule::list);
43
44 CommandDefinition defRouteShow("route", "show");
45 defRouteShow
46 .setTitle("show routes toward a prefix")
47 .addArg("prefix", ArgValueType::NAME, Required::YES, Positional::YES);
48 parser.addCommand(defRouteShow, &RibModule::show);
49
Junxiao Shi918e5d42017-02-25 03:58:21 +000050 CommandDefinition defRouteAdd("route", "add");
51 defRouteAdd
52 .setTitle("add a route")
53 .addArg("prefix", ArgValueType::NAME, Required::YES, Positional::YES)
54 .addArg("nexthop", ArgValueType::FACE_ID_OR_URI, Required::YES, Positional::YES)
55 .addArg("origin", ArgValueType::UNSIGNED, Required::NO, Positional::NO)
56 .addArg("cost", ArgValueType::UNSIGNED, Required::NO, Positional::NO)
57 .addArg("no-inherit", ArgValueType::NONE, Required::NO, Positional::NO)
58 .addArg("capture", ArgValueType::NONE, Required::NO, Positional::NO)
59 .addArg("expires", ArgValueType::UNSIGNED, Required::NO, Positional::NO);
60 parser.addCommand(defRouteAdd, &RibModule::add);
Junxiao Shi084b7952017-02-26 22:00:53 +000061
62 CommandDefinition defRouteRemove("route", "remove");
63 defRouteRemove
64 .setTitle("remove a route")
65 .addArg("prefix", ArgValueType::NAME, Required::YES, Positional::YES)
66 .addArg("nexthop", ArgValueType::FACE_ID_OR_URI, Required::YES, Positional::YES)
67 .addArg("origin", ArgValueType::UNSIGNED, Required::NO, Positional::NO);
68 parser.addCommand(defRouteRemove, &RibModule::remove);
Junxiao Shi918e5d42017-02-25 03:58:21 +000069}
70
71void
Junxiao Shi1d62e622017-03-08 22:39:28 +000072RibModule::list(ExecuteContext& ctx)
73{
74 auto nexthopIt = ctx.args.find("nexthop");
75 std::set<uint64_t> nexthops;
76 auto origin = ctx.args.getOptional<uint64_t>("origin");
77
78 if (nexthopIt != ctx.args.end()) {
79 FindFace findFace(ctx);
80 FindFace::Code res = findFace.execute(nexthopIt->second, true);
81
82 ctx.exitCode = static_cast<int>(res);
83 switch (res) {
84 case FindFace::Code::OK:
85 break;
86 case FindFace::Code::ERROR:
87 case FindFace::Code::CANONIZE_ERROR:
88 case FindFace::Code::NOT_FOUND:
89 ctx.err << findFace.getErrorReason() << '\n';
90 return;
91 default:
92 BOOST_ASSERT_MSG(false, "unexpected FindFace result");
93 return;
94 }
95
96 nexthops = findFace.getFaceIds();
97 }
98
99 listRoutesImpl(ctx, [&] (const RibEntry& entry, const Route& route) {
100 return (nexthops.empty() || nexthops.count(route.getFaceId()) > 0) &&
101 (!origin || route.getOrigin() == *origin);
102 });
103}
104
105void
106RibModule::show(ExecuteContext& ctx)
107{
108 auto prefix = ctx.args.get<Name>("prefix");
109
110 listRoutesImpl(ctx, [&] (const RibEntry& entry, const Route& route) {
111 return entry.getName() == prefix;
112 });
113}
114
115void
116RibModule::listRoutesImpl(ExecuteContext& ctx, const RoutePredicate& filter)
117{
118 ctx.controller.fetch<ndn::nfd::RibDataset>(
119 [&] (const std::vector<RibEntry>& dataset) {
120 bool hasRoute = false;
121 for (const RibEntry& entry : dataset) {
122 for (const Route& route : entry.getRoutes()) {
123 if (filter(entry, route)) {
124 hasRoute = true;
125 formatRouteText(ctx.out, entry, route, true);
126 ctx.out << '\n';
127 }
128 }
129 }
130
131 if (!hasRoute) {
132 ctx.exitCode = 6;
133 ctx.err << "Route not found\n";
134 }
135 },
136 ctx.makeDatasetFailureHandler("RIB dataset"),
137 ctx.makeCommandOptions());
138
139 ctx.face.processEvents();
140}
141
142void
Junxiao Shi918e5d42017-02-25 03:58:21 +0000143RibModule::add(ExecuteContext& ctx)
144{
145 auto prefix = ctx.args.get<Name>("prefix");
146 const boost::any& nexthop = ctx.args.at("nexthop");
147 auto origin = ctx.args.get<uint64_t>("origin", ndn::nfd::ROUTE_ORIGIN_STATIC);
148 auto cost = ctx.args.get<uint64_t>("cost", 0);
149 bool wantChildInherit = !ctx.args.get<bool>("no-inherit", false);
150 bool wantCapture = ctx.args.get<bool>("capture", false);
151 auto expiresMillis = ctx.args.getOptional<uint64_t>("expires");
152
153 FindFace findFace(ctx);
154 FindFace::Code res = findFace.execute(nexthop);
155
156 ctx.exitCode = static_cast<int>(res);
157 switch (res) {
158 case FindFace::Code::OK:
159 break;
160 case FindFace::Code::ERROR:
161 case FindFace::Code::CANONIZE_ERROR:
162 case FindFace::Code::NOT_FOUND:
163 ctx.err << findFace.getErrorReason() << '\n';
164 return;
165 case FindFace::Code::AMBIGUOUS:
166 ctx.err << "Multiple faces match specified remote FaceUri. Re-run the command with a FaceId:";
167 findFace.printDisambiguation(ctx.err, FindFace::DisambiguationStyle::LOCAL_URI);
168 ctx.err << '\n';
169 return;
170 default:
171 BOOST_ASSERT_MSG(false, "unexpected FindFace result");
172 return;
173 }
174
175 ControlParameters registerParams;
176 registerParams
177 .setName(prefix)
178 .setFaceId(findFace.getFaceId())
179 .setOrigin(origin)
180 .setCost(cost)
Ashlesh Gawandef7bf4092017-03-04 03:06:26 +0000181 .setFlags((wantChildInherit ? ndn::nfd::ROUTE_FLAG_CHILD_INHERIT : ndn::nfd::ROUTE_FLAGS_NONE) |
182 (wantCapture ? ndn::nfd::ROUTE_FLAG_CAPTURE : ndn::nfd::ROUTE_FLAGS_NONE));
Junxiao Shi918e5d42017-02-25 03:58:21 +0000183 if (expiresMillis) {
184 registerParams.setExpirationPeriod(time::milliseconds(*expiresMillis));
185 }
186
187 ctx.controller.start<ndn::nfd::RibRegisterCommand>(
188 registerParams,
189 [&] (const ControlParameters& resp) {
190 ctx.out << "route-add-accepted ";
191 text::ItemAttributes ia;
192 ctx.out << ia("prefix") << resp.getName()
193 << ia("nexthop") << resp.getFaceId()
194 << ia("origin") << resp.getOrigin()
195 << ia("cost") << resp.getCost()
196 << ia("flags") << static_cast<ndn::nfd::RouteFlags>(resp.getFlags());
197 if (resp.hasExpirationPeriod()) {
198 ctx.out << ia("expires") << resp.getExpirationPeriod().count() << "ms\n";
199 }
200 else {
201 ctx.out<< ia("expires") << "never\n";
202 }
203 },
204 ctx.makeCommandFailureHandler("adding route"),
205 ctx.makeCommandOptions());
206
207 ctx.face.processEvents();
208}
209
210void
Junxiao Shi084b7952017-02-26 22:00:53 +0000211RibModule::remove(ExecuteContext& ctx)
212{
213 auto prefix = ctx.args.get<Name>("prefix");
214 const boost::any& nexthop = ctx.args.at("nexthop");
215 auto origin = ctx.args.get<uint64_t>("origin", ndn::nfd::ROUTE_ORIGIN_STATIC);
216
217 FindFace findFace(ctx);
218 FindFace::Code res = findFace.execute(nexthop, true);
219
220 ctx.exitCode = static_cast<int>(res);
221 switch (res) {
222 case FindFace::Code::OK:
223 break;
224 case FindFace::Code::ERROR:
225 case FindFace::Code::CANONIZE_ERROR:
226 case FindFace::Code::NOT_FOUND:
227 ctx.err << findFace.getErrorReason() << '\n';
228 return;
229 default:
230 BOOST_ASSERT_MSG(false, "unexpected FindFace result");
231 return;
232 }
233
Junxiao Shi1d62e622017-03-08 22:39:28 +0000234 for (uint64_t faceId : findFace.getFaceIds()) {
Junxiao Shi084b7952017-02-26 22:00:53 +0000235 ControlParameters unregisterParams;
236 unregisterParams
237 .setName(prefix)
Junxiao Shi1d62e622017-03-08 22:39:28 +0000238 .setFaceId(faceId)
Junxiao Shi084b7952017-02-26 22:00:53 +0000239 .setOrigin(origin);
240
241 ctx.controller.start<ndn::nfd::RibUnregisterCommand>(
242 unregisterParams,
243 [&] (const ControlParameters& resp) {
244 ctx.out << "route-removed ";
245 text::ItemAttributes ia;
246 ctx.out << ia("prefix") << resp.getName()
247 << ia("nexthop") << resp.getFaceId()
248 << ia("origin") << resp.getOrigin()
249 << '\n';
250 },
251 ctx.makeCommandFailureHandler("removing route"),
252 ctx.makeCommandOptions());
253 }
254
255 ctx.face.processEvents();
256}
257
258void
Junxiao Shi38f4ce92016-08-04 10:01:52 +0000259RibModule::fetchStatus(Controller& controller,
260 const function<void()>& onSuccess,
Junxiao Shi29b41282016-08-22 03:47:02 +0000261 const Controller::DatasetFailCallback& onFailure,
Junxiao Shi38f4ce92016-08-04 10:01:52 +0000262 const CommandOptions& options)
263{
264 controller.fetch<ndn::nfd::RibDataset>(
265 [this, onSuccess] (const std::vector<RibEntry>& result) {
266 m_status = result;
267 onSuccess();
268 },
269 onFailure, options);
270}
271
272void
273RibModule::formatStatusXml(std::ostream& os) const
274{
275 os << "<rib>";
276 for (const RibEntry& item : m_status) {
277 this->formatItemXml(os, item);
278 }
279 os << "</rib>";
280}
281
282void
283RibModule::formatItemXml(std::ostream& os, const RibEntry& item) const
284{
285 os << "<ribEntry>";
286
287 os << "<prefix>" << xml::Text{item.getName().toUri()} << "</prefix>";
288
289 os << "<routes>";
290 for (const Route& route : item.getRoutes()) {
291 os << "<route>"
292 << "<faceId>" << route.getFaceId() << "</faceId>"
293 << "<origin>" << route.getOrigin() << "</origin>"
294 << "<cost>" << route.getCost() << "</cost>";
295 if (route.getFlags() == ndn::nfd::ROUTE_FLAGS_NONE) {
296 os << "<flags/>";
297 }
298 else {
299 os << "<flags>";
300 if (route.isChildInherit()) {
301 os << "<childInherit/>";
302 }
303 if (route.isRibCapture()) {
304 os << "<ribCapture/>";
305 }
306 os << "</flags>";
307 }
Davide Pesavento26cbdbd2017-02-19 21:37:43 -0500308 if (route.hasExpirationPeriod()) {
Junxiao Shi38f4ce92016-08-04 10:01:52 +0000309 os << "<expirationPeriod>"
310 << xml::formatDuration(route.getExpirationPeriod())
311 << "</expirationPeriod>";
312 }
313 os << "</route>";
314 }
315 os << "</routes>";
316
317 os << "</ribEntry>";
318}
319
320void
321RibModule::formatStatusText(std::ostream& os) const
322{
323 os << "RIB:\n";
324 for (const RibEntry& item : m_status) {
Junxiao Shi1d62e622017-03-08 22:39:28 +0000325 os << " ";
326 formatEntryText(os, item);
327 os << '\n';
Junxiao Shi38f4ce92016-08-04 10:01:52 +0000328 }
329}
330
331void
Junxiao Shi1d62e622017-03-08 22:39:28 +0000332RibModule::formatEntryText(std::ostream& os, const RibEntry& entry)
Junxiao Shi38f4ce92016-08-04 10:01:52 +0000333{
Junxiao Shi1d62e622017-03-08 22:39:28 +0000334 os << entry.getName() << " routes={";
Junxiao Shi38f4ce92016-08-04 10:01:52 +0000335
336 text::Separator sep(", ");
Junxiao Shi1d62e622017-03-08 22:39:28 +0000337 for (const Route& route : entry.getRoutes()) {
338 os << sep;
339 formatRouteText(os, entry, route, false);
Junxiao Shi38f4ce92016-08-04 10:01:52 +0000340 }
341
342 os << "}";
Junxiao Shi1d62e622017-03-08 22:39:28 +0000343}
344
345void
346RibModule::formatRouteText(std::ostream& os, const RibEntry& entry, const Route& route,
347 bool includePrefix)
348{
349 text::ItemAttributes ia;
350
351 if (includePrefix) {
352 os << ia("prefix") << entry.getName();
353 }
354 os << ia("nexthop") << route.getFaceId();
355 os << ia("origin") << static_cast<uint64_t>(route.getOrigin());
356 os << ia("cost") << route.getCost();
357 os << ia("flags") << static_cast<ndn::nfd::RouteFlags>(route.getFlags());
358
359 // 'origin' field is printed as a number, because printing 'origin' as string may mislead user
360 // into passing strings to 'origin' command line argument which currently only accepts numbers.
361 ///\todo #3987 print 'origin' with RouteOrigin stream insertion operator
362
363 if (route.hasExpirationPeriod()) {
364 os << ia("expires") << text::formatDuration(route.getExpirationPeriod());
365 }
366 else {
367 os << ia("expires") << "never";
368 }
Junxiao Shi38f4ce92016-08-04 10:01:52 +0000369}
370
Junxiao Shi331ade72016-08-19 14:07:19 +0000371} // namespace nfdc
Junxiao Shi38f4ce92016-08-04 10:01:52 +0000372} // namespace tools
373} // namespace nfd