blob: bf3b66227630063753d27f9e387e6426d518ae15 [file] [log] [blame]
Junxiao Shi38f4ce92016-08-04 10:01:52 +00001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
Eric Newberryde332452018-01-30 11:45:32 -07002/*
Davide Pesaventob7bfcb92022-05-22 23:55:23 -04003 * Copyright (c) 2014-2022, 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"
Eric Newberryd656aff2020-04-03 00:30:38 -070027#include "face-module.hpp"
Davide Pesavento990620a2022-06-05 00:25:53 -040028#include "face-helpers.hpp"
Junxiao Shi38f4ce92016-08-04 10:01:52 +000029#include "format-helpers.hpp"
30
Davide Pesaventoe422f9e2022-06-03 01:30:23 -040031namespace nfd::tools::nfdc {
Junxiao Shi38f4ce92016-08-04 10:01:52 +000032
33void
Junxiao Shi918e5d42017-02-25 03:58:21 +000034RibModule::registerCommands(CommandParser& parser)
35{
Junxiao Shi1d62e622017-03-08 22:39:28 +000036 CommandDefinition defRouteList("route", "list");
37 defRouteList
38 .setTitle("print RIB routes")
39 .addArg("nexthop", ArgValueType::FACE_ID_OR_URI, Required::NO, Positional::YES)
Junxiao Shi8eda6822017-04-12 02:53:14 +000040 .addArg("origin", ArgValueType::ROUTE_ORIGIN, Required::NO, Positional::NO);
Junxiao Shi1d62e622017-03-08 22:39:28 +000041 parser.addCommand(defRouteList, &RibModule::list);
Davide Pesaventod2147442018-02-19 23:58:17 -050042 parser.addAlias("route", "list", "");
Junxiao Shi1d62e622017-03-08 22:39:28 +000043
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)
Junxiao Shi8eda6822017-04-12 02:53:14 +000055 .addArg("origin", ArgValueType::ROUTE_ORIGIN, Required::NO, Positional::NO)
Junxiao Shi918e5d42017-02-25 03:58:21 +000056 .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)
Junxiao Shi8eda6822017-04-12 02:53:14 +000067 .addArg("origin", ArgValueType::ROUTE_ORIGIN, Required::NO, Positional::NO);
Junxiao Shi084b7952017-02-26 22:00:53 +000068 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;
Junxiao Shi8eda6822017-04-12 02:53:14 +000076 auto origin = ctx.args.getOptional<RouteOrigin>("origin");
Junxiao Shi1d62e622017-03-08 22:39:28 +000077
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
Davide Pesavento990620a2022-06-05 00:25:53 -040099 listRoutesImpl(ctx, [&] (const RibEntry&, const Route& route) {
Junxiao Shi1d62e622017-03-08 22:39:28 +0000100 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
Davide Pesavento990620a2022-06-05 00:25:53 -0400110 listRoutesImpl(ctx, [&] (const RibEntry& entry, const Route&) {
Junxiao Shi1d62e622017-03-08 22:39:28 +0000111 return entry.getName() == prefix;
112 });
113}
114
115void
116RibModule::listRoutesImpl(ExecuteContext& ctx, const RoutePredicate& filter)
117{
118 ctx.controller.fetch<ndn::nfd::RibDataset>(
Davide Pesavento990620a2022-06-05 00:25:53 -0400119 [&] (const auto& dataset) {
Junxiao Shi1d62e622017-03-08 22:39:28 +0000120 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");
Davide Pesavento8b663a92018-11-21 22:57:20 -0500146 auto nexthop = ctx.args.at("nexthop");
Junxiao Shi8eda6822017-04-12 02:53:14 +0000147 auto origin = ctx.args.get<RouteOrigin>("origin", ndn::nfd::ROUTE_ORIGIN_STATIC);
Junxiao Shi918e5d42017-02-25 03:58:21 +0000148 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
Eric Newberryd656aff2020-04-03 00:30:38 -0700153 auto registerRoute = [&] (uint64_t faceId) {
154 ControlParameters registerParams;
155 registerParams
156 .setName(prefix)
157 .setFaceId(faceId)
158 .setOrigin(origin)
159 .setCost(cost)
160 .setFlags((wantChildInherit ? ndn::nfd::ROUTE_FLAG_CHILD_INHERIT : ndn::nfd::ROUTE_FLAGS_NONE) |
161 (wantCapture ? ndn::nfd::ROUTE_FLAG_CAPTURE : ndn::nfd::ROUTE_FLAGS_NONE));
162 if (expiresMillis) {
163 registerParams.setExpirationPeriod(time::milliseconds(*expiresMillis));
164 }
165
166 ctx.controller.start<ndn::nfd::RibRegisterCommand>(
167 registerParams,
168 [&] (const ControlParameters& resp) {
169 ctx.exitCode = static_cast<int>(FindFace::Code::OK);
170 ctx.out << "route-add-accepted ";
171 text::ItemAttributes ia;
172 ctx.out << ia("prefix") << resp.getName()
173 << ia("nexthop") << resp.getFaceId()
174 << ia("origin") << resp.getOrigin()
175 << ia("cost") << resp.getCost()
176 << ia("flags") << static_cast<ndn::nfd::RouteFlags>(resp.getFlags());
177 if (resp.hasExpirationPeriod()) {
178 ctx.out << ia("expires") << text::formatDuration<time::milliseconds>(resp.getExpirationPeriod()) << "\n";
179 }
180 else {
181 ctx.out<< ia("expires") << "never\n";
182 }
183 },
184 ctx.makeCommandFailureHandler("adding route"),
185 ctx.makeCommandOptions());
186 };
187
188 auto handleFaceNotFound = [&] {
Davide Pesaventob7bfcb92022-05-22 23:55:23 -0400189 const FaceUri* faceUri = std::any_cast<FaceUri>(&nexthop);
Eric Newberryd656aff2020-04-03 00:30:38 -0700190 if (faceUri == nullptr) {
191 ctx.err << "Face not found\n";
192 return;
193 }
194
195 if (faceUri->getScheme() == "ether") {
196 // Unicast Ethernet faces require a LocalUri, which hasn't been provided
197 // Multicast Ethernet faces cannot be created via management (already exist on each interface)
198 ctx.err << "Unable to implicitly create Ethernet faces\n";
199 ctx.err << "Please create the face with 'nfdc face create' before adding the route\n";
200 return;
201 }
202
Davide Pesaventoa3a7a4e2022-05-29 16:06:22 -0400203 auto [canonized, error] = canonize(ctx, *faceUri);
Eric Newberryd656aff2020-04-03 00:30:38 -0700204 if (!canonized) {
205 // Canonization failed
206 auto canonizationError = canonizeErrorHelper(*faceUri, error);
207 ctx.exitCode = static_cast<int>(canonizationError.first);
208 ctx.err << canonizationError.second << '\n';
209 return;
210 }
211
212 ControlParameters faceCreateParams;
213 faceCreateParams.setUri(canonized->toString());
214
215 ctx.controller.start<ndn::nfd::FaceCreateCommand>(
216 faceCreateParams,
217 [&] (const ControlParameters& resp) {
218 FaceModule::printSuccess(ctx.out, "face-created", resp);
219 registerRoute(resp.getFaceId());
220 },
221 ctx.makeCommandFailureHandler("implicitly creating face"),
222 ctx.makeCommandOptions());
223 };
224
Junxiao Shi918e5d42017-02-25 03:58:21 +0000225 FindFace findFace(ctx);
226 FindFace::Code res = findFace.execute(nexthop);
227
228 ctx.exitCode = static_cast<int>(res);
229 switch (res) {
230 case FindFace::Code::OK:
Eric Newberryd656aff2020-04-03 00:30:38 -0700231 registerRoute(findFace.getFaceId());
Junxiao Shi918e5d42017-02-25 03:58:21 +0000232 break;
233 case FindFace::Code::ERROR:
234 case FindFace::Code::CANONIZE_ERROR:
Junxiao Shi918e5d42017-02-25 03:58:21 +0000235 ctx.err << findFace.getErrorReason() << '\n';
236 return;
Eric Newberryd656aff2020-04-03 00:30:38 -0700237 case FindFace::Code::NOT_FOUND:
238 // Attempt to create face if it doesn't exist
239 handleFaceNotFound();
240 break;
Junxiao Shi918e5d42017-02-25 03:58:21 +0000241 case FindFace::Code::AMBIGUOUS:
242 ctx.err << "Multiple faces match specified remote FaceUri. Re-run the command with a FaceId:";
243 findFace.printDisambiguation(ctx.err, FindFace::DisambiguationStyle::LOCAL_URI);
244 ctx.err << '\n';
245 return;
246 default:
247 BOOST_ASSERT_MSG(false, "unexpected FindFace result");
248 return;
249 }
250
Junxiao Shi918e5d42017-02-25 03:58:21 +0000251 ctx.face.processEvents();
252}
253
254void
Junxiao Shi084b7952017-02-26 22:00:53 +0000255RibModule::remove(ExecuteContext& ctx)
256{
257 auto prefix = ctx.args.get<Name>("prefix");
Davide Pesavento8b663a92018-11-21 22:57:20 -0500258 auto nexthop = ctx.args.at("nexthop");
Junxiao Shi8eda6822017-04-12 02:53:14 +0000259 auto origin = ctx.args.get<RouteOrigin>("origin", ndn::nfd::ROUTE_ORIGIN_STATIC);
Junxiao Shi084b7952017-02-26 22:00:53 +0000260
261 FindFace findFace(ctx);
262 FindFace::Code res = findFace.execute(nexthop, true);
263
264 ctx.exitCode = static_cast<int>(res);
265 switch (res) {
266 case FindFace::Code::OK:
267 break;
268 case FindFace::Code::ERROR:
269 case FindFace::Code::CANONIZE_ERROR:
270 case FindFace::Code::NOT_FOUND:
271 ctx.err << findFace.getErrorReason() << '\n';
272 return;
273 default:
274 BOOST_ASSERT_MSG(false, "unexpected FindFace result");
275 return;
276 }
277
Junxiao Shi1d62e622017-03-08 22:39:28 +0000278 for (uint64_t faceId : findFace.getFaceIds()) {
Junxiao Shi084b7952017-02-26 22:00:53 +0000279 ControlParameters unregisterParams;
280 unregisterParams
281 .setName(prefix)
Junxiao Shi1d62e622017-03-08 22:39:28 +0000282 .setFaceId(faceId)
Junxiao Shi084b7952017-02-26 22:00:53 +0000283 .setOrigin(origin);
284
285 ctx.controller.start<ndn::nfd::RibUnregisterCommand>(
286 unregisterParams,
287 [&] (const ControlParameters& resp) {
288 ctx.out << "route-removed ";
289 text::ItemAttributes ia;
290 ctx.out << ia("prefix") << resp.getName()
291 << ia("nexthop") << resp.getFaceId()
Davide Pesavento22db5392017-04-14 00:56:43 -0400292 << ia("origin") << resp.getOrigin()
Junxiao Shi084b7952017-02-26 22:00:53 +0000293 << '\n';
294 },
295 ctx.makeCommandFailureHandler("removing route"),
296 ctx.makeCommandOptions());
297 }
298
299 ctx.face.processEvents();
300}
301
302void
Junxiao Shi38f4ce92016-08-04 10:01:52 +0000303RibModule::fetchStatus(Controller& controller,
Davide Pesavento87fc0f82018-04-11 23:43:51 -0400304 const std::function<void()>& onSuccess,
Junxiao Shi29b41282016-08-22 03:47:02 +0000305 const Controller::DatasetFailCallback& onFailure,
Junxiao Shi38f4ce92016-08-04 10:01:52 +0000306 const CommandOptions& options)
307{
308 controller.fetch<ndn::nfd::RibDataset>(
Davide Pesavento990620a2022-06-05 00:25:53 -0400309 [this, onSuccess] (const auto& result) {
Junxiao Shi38f4ce92016-08-04 10:01:52 +0000310 m_status = result;
311 onSuccess();
312 },
313 onFailure, options);
314}
315
316void
317RibModule::formatStatusXml(std::ostream& os) const
318{
319 os << "<rib>";
320 for (const RibEntry& item : m_status) {
321 this->formatItemXml(os, item);
322 }
323 os << "</rib>";
324}
325
326void
327RibModule::formatItemXml(std::ostream& os, const RibEntry& item) const
328{
329 os << "<ribEntry>";
330
331 os << "<prefix>" << xml::Text{item.getName().toUri()} << "</prefix>";
332
333 os << "<routes>";
334 for (const Route& route : item.getRoutes()) {
335 os << "<route>"
336 << "<faceId>" << route.getFaceId() << "</faceId>"
Davide Pesavento22db5392017-04-14 00:56:43 -0400337 << "<origin>" << route.getOrigin() << "</origin>"
Junxiao Shi38f4ce92016-08-04 10:01:52 +0000338 << "<cost>" << route.getCost() << "</cost>";
339 if (route.getFlags() == ndn::nfd::ROUTE_FLAGS_NONE) {
340 os << "<flags/>";
341 }
342 else {
343 os << "<flags>";
344 if (route.isChildInherit()) {
345 os << "<childInherit/>";
346 }
347 if (route.isRibCapture()) {
348 os << "<ribCapture/>";
349 }
350 os << "</flags>";
351 }
Davide Pesavento26cbdbd2017-02-19 21:37:43 -0500352 if (route.hasExpirationPeriod()) {
Junxiao Shi38f4ce92016-08-04 10:01:52 +0000353 os << "<expirationPeriod>"
Eric Newberryde332452018-01-30 11:45:32 -0700354 << xml::formatDuration(time::duration_cast<time::seconds>(route.getExpirationPeriod()))
Junxiao Shi38f4ce92016-08-04 10:01:52 +0000355 << "</expirationPeriod>";
356 }
357 os << "</route>";
358 }
359 os << "</routes>";
360
361 os << "</ribEntry>";
362}
363
364void
365RibModule::formatStatusText(std::ostream& os) const
366{
367 os << "RIB:\n";
368 for (const RibEntry& item : m_status) {
Junxiao Shi1d62e622017-03-08 22:39:28 +0000369 os << " ";
370 formatEntryText(os, item);
371 os << '\n';
Junxiao Shi38f4ce92016-08-04 10:01:52 +0000372 }
373}
374
375void
Junxiao Shi1d62e622017-03-08 22:39:28 +0000376RibModule::formatEntryText(std::ostream& os, const RibEntry& entry)
Junxiao Shi38f4ce92016-08-04 10:01:52 +0000377{
Junxiao Shi1d62e622017-03-08 22:39:28 +0000378 os << entry.getName() << " routes={";
Junxiao Shi38f4ce92016-08-04 10:01:52 +0000379
380 text::Separator sep(", ");
Junxiao Shi1d62e622017-03-08 22:39:28 +0000381 for (const Route& route : entry.getRoutes()) {
382 os << sep;
383 formatRouteText(os, entry, route, false);
Junxiao Shi38f4ce92016-08-04 10:01:52 +0000384 }
385
386 os << "}";
Junxiao Shi1d62e622017-03-08 22:39:28 +0000387}
388
389void
390RibModule::formatRouteText(std::ostream& os, const RibEntry& entry, const Route& route,
391 bool includePrefix)
392{
393 text::ItemAttributes ia;
394
395 if (includePrefix) {
396 os << ia("prefix") << entry.getName();
397 }
398 os << ia("nexthop") << route.getFaceId();
Davide Pesavento22db5392017-04-14 00:56:43 -0400399 os << ia("origin") << route.getOrigin();
Junxiao Shi1d62e622017-03-08 22:39:28 +0000400 os << ia("cost") << route.getCost();
401 os << ia("flags") << static_cast<ndn::nfd::RouteFlags>(route.getFlags());
Junxiao Shi1d62e622017-03-08 22:39:28 +0000402 if (route.hasExpirationPeriod()) {
Eric Newberryde332452018-01-30 11:45:32 -0700403 os << ia("expires") << text::formatDuration<time::seconds>(route.getExpirationPeriod());
Junxiao Shi1d62e622017-03-08 22:39:28 +0000404 }
405 else {
406 os << ia("expires") << "never";
407 }
Junxiao Shi38f4ce92016-08-04 10:01:52 +0000408}
409
Davide Pesaventoe422f9e2022-06-03 01:30:23 -0400410} // namespace nfd::tools::nfdc