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 
33 namespace nfd::tools::nfdc {
34 
35 void
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 
73 void
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) {
86  case FindFace::Code::OK:
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 
107 void
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 
117 void
118 RibModule::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 
144 void
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) {
232  case FindFace::Code::OK:
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 
256 void
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) {
268  case FindFace::Code::OK:
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 
304 void
305 RibModule::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 
318 void
319 RibModule::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 
328 void
329 RibModule::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 
366 void
367 RibModule::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 
377 void
378 RibModule::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 
391 void
392 RibModule::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
std::optional< T > getOptional(std::string_view key) const
T get(std::string_view key, const T &defaultValue=T()) 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
uint64_t getFaceId() 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.
const std::string & getErrorReason() const
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.
Definition: rib-module.cpp:305
static void add(ExecuteContext &ctx)
The 'route add' command.
Definition: rib-module.cpp:145
static void show(ExecuteContext &ctx)
The 'route show' command.
Definition: rib-module.cpp:108
void formatStatusText(std::ostream &os) const override
Format collected status as text.
Definition: rib-module.cpp:367
void formatStatusXml(std::ostream &os) const override
Format collected status as XML.
Definition: rib-module.cpp:319
static void registerCommands(CommandParser &parser)
Register 'route list', 'route show', 'route add', 'route remove' commands.
Definition: rib-module.cpp:36
static void list(ExecuteContext &ctx)
The 'route list' command.
Definition: rib-module.cpp:74
static void remove(ExecuteContext &ctx)
The 'route remove' command.
Definition: rib-module.cpp:257
Print attributes of an item.
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.