NFD: Named Data Networking Forwarding Daemon 24.07-28-gdcc0e6e0
Loading...
Searching...
No Matches
rib-module.cpp
Go to the documentation of this file.
1/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/*
3 * Copyright (c) 2014-2023, 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 "rib-module.hpp"
27#include "face-module.hpp"
28#include "face-helpers.hpp"
29#include "format-helpers.hpp"
30
31#include <ndn-cxx/mgmt/nfd/status-dataset.hpp>
32
33namespace nfd::tools::nfdc {
34
35void
37{
38 CommandDefinition defRouteList("route", "list");
39 defRouteList
40 .setTitle("print RIB routes")
43 parser.addCommand(defRouteList, &RibModule::list);
44 parser.addAlias("route", "list", "");
45
46 CommandDefinition defRouteShow("route", "show");
47 defRouteShow
48 .setTitle("show routes toward a prefix")
50 parser.addCommand(defRouteShow, &RibModule::show);
51
52 CommandDefinition defRouteAdd("route", "add");
53 defRouteAdd
54 .setTitle("add a route")
62 parser.addCommand(defRouteAdd, &RibModule::add);
63
64 CommandDefinition defRouteRemove("route", "remove");
65 defRouteRemove
66 .setTitle("remove a route")
70 parser.addCommand(defRouteRemove, &RibModule::remove);
71}
72
73void
75{
76 auto nexthopIt = ctx.args.find("nexthop");
77 std::set<uint64_t> nexthops;
78 auto origin = ctx.args.getOptional<RouteOrigin>("origin");
79
80 if (nexthopIt != ctx.args.end()) {
81 FindFace findFace(ctx);
82 FindFace::Code res = findFace.execute(nexthopIt->second, true);
83
84 ctx.exitCode = static_cast<int>(res);
85 switch (res) {
87 break;
91 ctx.err << findFace.getErrorReason() << '\n';
92 return;
93 default:
94 BOOST_ASSERT_MSG(false, "unexpected FindFace result");
95 return;
96 }
97
98 nexthops = findFace.getFaceIds();
99 }
100
101 listRoutesImpl(ctx, [&] (const RibEntry&, const Route& route) {
102 return (nexthops.empty() || nexthops.count(route.getFaceId()) > 0) &&
103 (!origin || route.getOrigin() == *origin);
104 });
105}
106
107void
109{
110 auto prefix = ctx.args.get<Name>("prefix");
111
112 listRoutesImpl(ctx, [&] (const RibEntry& entry, const Route&) {
113 return entry.getName() == prefix;
114 });
115}
116
117void
118RibModule::listRoutesImpl(ExecuteContext& ctx, const RoutePredicate& filter)
119{
120 ctx.controller.fetch<ndn::nfd::RibDataset>(
121 [&] (const auto& dataset) {
122 bool hasRoute = false;
123 for (const RibEntry& entry : dataset) {
124 for (const Route& route : entry.getRoutes()) {
125 if (filter(entry, route)) {
126 hasRoute = true;
127 formatRouteText(ctx.out, entry, route, true);
128 ctx.out << '\n';
129 }
130 }
131 }
132
133 if (!hasRoute) {
134 ctx.exitCode = 6;
135 ctx.err << "Route not found\n";
136 }
137 },
138 ctx.makeDatasetFailureHandler("RIB dataset"),
139 ctx.makeCommandOptions());
140
141 ctx.face.processEvents();
142}
143
144void
146{
147 auto prefix = ctx.args.get<Name>("prefix");
148 auto nexthop = ctx.args.at("nexthop");
149 auto origin = ctx.args.get<RouteOrigin>("origin", ndn::nfd::ROUTE_ORIGIN_STATIC);
150 auto cost = ctx.args.get<uint64_t>("cost", 0);
151 bool wantChildInherit = !ctx.args.get<bool>("no-inherit", false);
152 bool wantCapture = ctx.args.get<bool>("capture", false);
153 auto expiresMillis = ctx.args.getOptional<uint64_t>("expires");
154
155 auto registerRoute = [&] (uint64_t faceId) {
156 ControlParameters registerParams;
157 registerParams
158 .setName(prefix)
159 .setFaceId(faceId)
160 .setOrigin(origin)
161 .setCost(cost)
162 .setFlags((wantChildInherit ? ndn::nfd::ROUTE_FLAG_CHILD_INHERIT : ndn::nfd::ROUTE_FLAGS_NONE) |
163 (wantCapture ? ndn::nfd::ROUTE_FLAG_CAPTURE : ndn::nfd::ROUTE_FLAGS_NONE));
164 if (expiresMillis) {
165 registerParams.setExpirationPeriod(time::milliseconds(*expiresMillis));
166 }
167
168 ctx.controller.start<ndn::nfd::RibRegisterCommand>(
169 registerParams,
170 [&] (const ControlParameters& resp) {
171 ctx.exitCode = static_cast<int>(FindFace::Code::OK);
172 ctx.out << "route-add-accepted ";
174 ctx.out << ia("prefix") << resp.getName()
175 << ia("nexthop") << resp.getFaceId()
176 << ia("origin") << resp.getOrigin()
177 << ia("cost") << resp.getCost()
178 << ia("flags") << static_cast<ndn::nfd::RouteFlags>(resp.getFlags());
179 if (resp.hasExpirationPeriod()) {
180 ctx.out << ia("expires") << text::formatDuration<time::milliseconds>(resp.getExpirationPeriod()) << "\n";
181 }
182 else {
183 ctx.out<< ia("expires") << "never\n";
184 }
185 },
186 ctx.makeCommandFailureHandler("adding route"),
187 ctx.makeCommandOptions());
188 };
189
190 auto handleFaceNotFound = [&] {
191 const FaceUri* faceUri = std::any_cast<FaceUri>(&nexthop);
192 if (faceUri == nullptr) {
193 ctx.err << "Face not found\n";
194 return;
195 }
196
197 if (faceUri->getScheme() == "ether") {
198 // Unicast Ethernet faces require a LocalUri, which hasn't been provided
199 // Multicast Ethernet faces cannot be created via management (already exist on each interface)
200 ctx.err << "Unable to implicitly create Ethernet faces\n";
201 ctx.err << "Please create the face with 'nfdc face create' before adding the route\n";
202 return;
203 }
204
205 auto [canonized, error] = canonize(ctx, *faceUri);
206 if (!canonized) {
207 // Canonization failed
208 auto canonizationError = canonizeErrorHelper(*faceUri, error);
209 ctx.exitCode = static_cast<int>(canonizationError.first);
210 ctx.err << canonizationError.second << '\n';
211 return;
212 }
213
214 ControlParameters faceCreateParams;
215 faceCreateParams.setUri(canonized->toString());
216
217 ctx.controller.start<ndn::nfd::FaceCreateCommand>(
218 faceCreateParams,
219 [&] (const ControlParameters& resp) {
220 FaceModule::printSuccess(ctx.out, "face-created", resp);
221 registerRoute(resp.getFaceId());
222 },
223 ctx.makeCommandFailureHandler("implicitly creating face"),
224 ctx.makeCommandOptions());
225 };
226
227 FindFace findFace(ctx);
228 FindFace::Code res = findFace.execute(nexthop);
229
230 ctx.exitCode = static_cast<int>(res);
231 switch (res) {
233 registerRoute(findFace.getFaceId());
234 break;
237 ctx.err << findFace.getErrorReason() << '\n';
238 return;
240 // Attempt to create face if it doesn't exist
241 handleFaceNotFound();
242 break;
244 ctx.err << "Multiple faces match specified remote FaceUri. Re-run the command with a FaceId:";
246 ctx.err << '\n';
247 return;
248 default:
249 BOOST_ASSERT_MSG(false, "unexpected FindFace result");
250 return;
251 }
252
253 ctx.face.processEvents();
254}
255
256void
258{
259 auto prefix = ctx.args.get<Name>("prefix");
260 auto nexthop = ctx.args.at("nexthop");
261 auto origin = ctx.args.get<RouteOrigin>("origin", ndn::nfd::ROUTE_ORIGIN_STATIC);
262
263 FindFace findFace(ctx);
264 FindFace::Code res = findFace.execute(nexthop, true);
265
266 ctx.exitCode = static_cast<int>(res);
267 switch (res) {
269 break;
273 ctx.err << findFace.getErrorReason() << '\n';
274 return;
275 default:
276 BOOST_ASSERT_MSG(false, "unexpected FindFace result");
277 return;
278 }
279
280 for (uint64_t faceId : findFace.getFaceIds()) {
281 ControlParameters unregisterParams;
282 unregisterParams
283 .setName(prefix)
284 .setFaceId(faceId)
285 .setOrigin(origin);
286
287 ctx.controller.start<ndn::nfd::RibUnregisterCommand>(
288 unregisterParams,
289 [&] (const ControlParameters& resp) {
290 ctx.out << "route-removed ";
292 ctx.out << ia("prefix") << resp.getName()
293 << ia("nexthop") << resp.getFaceId()
294 << ia("origin") << resp.getOrigin()
295 << '\n';
296 },
297 ctx.makeCommandFailureHandler("removing route"),
298 ctx.makeCommandOptions());
299 }
300
301 ctx.face.processEvents();
302}
303
304void
305RibModule::fetchStatus(ndn::nfd::Controller& controller,
306 const std::function<void()>& onSuccess,
307 const ndn::nfd::DatasetFailureCallback& onFailure,
308 const CommandOptions& options)
309{
310 controller.fetch<ndn::nfd::RibDataset>(
311 [this, onSuccess] (const auto& result) {
312 m_status = result;
313 onSuccess();
314 },
315 onFailure, options);
316}
317
318void
319RibModule::formatStatusXml(std::ostream& os) const
320{
321 os << "<rib>";
322 for (const RibEntry& item : m_status) {
323 this->formatItemXml(os, item);
324 }
325 os << "</rib>";
326}
327
328void
329RibModule::formatItemXml(std::ostream& os, const RibEntry& item) const
330{
331 os << "<ribEntry>";
332
333 os << "<prefix>" << xml::Text{item.getName().toUri()} << "</prefix>";
334
335 os << "<routes>";
336 for (const Route& route : item.getRoutes()) {
337 os << "<route>"
338 << "<faceId>" << route.getFaceId() << "</faceId>"
339 << "<origin>" << route.getOrigin() << "</origin>"
340 << "<cost>" << route.getCost() << "</cost>";
341 if (route.getFlags() == ndn::nfd::ROUTE_FLAGS_NONE) {
342 os << "<flags/>";
343 }
344 else {
345 os << "<flags>";
346 if (route.isChildInherit()) {
347 os << "<childInherit/>";
348 }
349 if (route.isRibCapture()) {
350 os << "<ribCapture/>";
351 }
352 os << "</flags>";
353 }
354 if (route.hasExpirationPeriod()) {
355 os << "<expirationPeriod>"
356 << xml::formatDuration(time::duration_cast<time::seconds>(route.getExpirationPeriod()))
357 << "</expirationPeriod>";
358 }
359 os << "</route>";
360 }
361 os << "</routes>";
362
363 os << "</ribEntry>";
364}
365
366void
367RibModule::formatStatusText(std::ostream& os) const
368{
369 os << "RIB:\n";
370 for (const RibEntry& item : m_status) {
371 os << " ";
372 formatEntryText(os, item);
373 os << '\n';
374 }
375}
376
377void
378RibModule::formatEntryText(std::ostream& os, const RibEntry& entry)
379{
380 os << entry.getName() << " routes={";
381
382 text::Separator sep(", ");
383 for (const Route& route : entry.getRoutes()) {
384 os << sep;
385 formatRouteText(os, entry, route, false);
386 }
387
388 os << "}";
389}
390
391void
392RibModule::formatRouteText(std::ostream& os, const RibEntry& entry, const Route& route,
393 bool includePrefix)
394{
395 text::ItemAttributes ia;
396
397 if (includePrefix) {
398 os << ia("prefix") << entry.getName();
399 }
400 os << ia("nexthop") << route.getFaceId();
401 os << ia("origin") << route.getOrigin();
402 os << ia("cost") << route.getCost();
403 os << ia("flags") << static_cast<ndn::nfd::RouteFlags>(route.getFlags());
404 if (route.hasExpirationPeriod()) {
405 os << ia("expires") << text::formatDuration<time::seconds>(route.getExpirationPeriod());
406 }
407 else {
408 os << ia("expires") << "never";
409 }
410}
411
412} // namespace nfd::tools::nfdc
Represents a route for a name prefix.
Definition route.hpp:44
std::underlying_type_t< ndn::nfd::RouteFlags > getFlags() const
Definition route.hpp:75
T get(std::string_view key, const T &defaultValue=T()) const
std::optional< T > getOptional(std::string_view key) const
CommandDefinition & setTitle(std::string_view title)
Set one-line description.
CommandDefinition & addArg(const std::string &name, ArgValueType valueType, Required isRequired=Required::NO, Positional allowPositional=Positional::NO, const std::string &metavar="")
Declare an argument.
CommandParser & addCommand(const CommandDefinition &def, const ExecuteCommand &execute, std::underlying_type_t< AvailableIn > modes=AVAILABLE_IN_ALL)
Add an available command.
CommandParser & addAlias(const std::string &noun, const std::string &verb, const std::string &verb2)
Add an alias "noun verb2" to existing command "noun verb".
Context for command execution.
std::ostream & out
output stream
ndn::nfd::DatasetFailureCallback makeDatasetFailureHandler(const std::string &datasetName)
ndn::nfd::Controller & controller
const CommandArguments & args
ndn::nfd::CommandFailureCallback makeCommandFailureHandler(const std::string &commandName)
ndn::nfd::CommandOptions makeCommandOptions() const
std::ostream & err
error stream
static void printSuccess(std::ostream &os, const std::string &actionSummary, const ControlParameters &resp)
Print face action success message to specified ostream.
Procedure to find a face.
std::set< uint64_t > getFaceIds() const
const std::string & getErrorReason() const
@ AMBIGUOUS
found multiple faces and allowMulti is false
@ CANONIZE_ERROR
error during FaceUri canonization
@ OK
found exactly one face, or found multiple faces when allowMulti is true
Code execute(const FaceUri &faceUri, bool allowMulti=false)
Find face by FaceUri.
void printDisambiguation(std::ostream &os, DisambiguationStyle style) const
Print results for disambiguation.
void fetchStatus(ndn::nfd::Controller &controller, const std::function< void()> &onSuccess, const ndn::nfd::DatasetFailureCallback &onFailure, const CommandOptions &options) override
Collect status from NFD.
static void add(ExecuteContext &ctx)
The 'route add' command.
static void show(ExecuteContext &ctx)
The 'route show' command.
void formatStatusText(std::ostream &os) const override
Format collected status as text.
void formatStatusXml(std::ostream &os) const override
Format collected status as XML.
static void registerCommands(CommandParser &parser)
Register 'route list', 'route show', 'route add', 'route remove' commands.
static void list(ExecuteContext &ctx)
The 'route list' command.
static void remove(ExecuteContext &ctx)
The 'route remove' command.
Print different string on first and subsequent usage.
std::string formatDuration(time::nanoseconds d)
std::pair< FindFace::Code, std::string > canonizeErrorHelper(const FaceUri &uri, const std::string &error, const std::string &field)
Helper to generate exit code and error message for face canonization failures.
@ YES
argument is required
@ NO
argument is optional
std::pair< std::optional< FaceUri >, std::string > canonize(ExecuteContext &ctx, const FaceUri &uri)
Canonize a FaceUri.
@ YES
argument can be specified as positional
@ NO
argument must be named
@ FACE_ID_OR_URI
FaceId or FaceUri.
@ UNSIGNED
Non-negative integer.
@ NONE
Boolean argument without value.