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-2022, 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 namespace nfd::tools::nfdc {
32 
33 void
35 {
36  CommandDefinition defRouteList("route", "list");
37  defRouteList
38  .setTitle("print RIB routes")
41  parser.addCommand(defRouteList, &RibModule::list);
42  parser.addAlias("route", "list", "");
43 
44  CommandDefinition defRouteShow("route", "show");
45  defRouteShow
46  .setTitle("show routes toward a prefix")
48  parser.addCommand(defRouteShow, &RibModule::show);
49 
50  CommandDefinition defRouteAdd("route", "add");
51  defRouteAdd
52  .setTitle("add a route")
60  parser.addCommand(defRouteAdd, &RibModule::add);
61 
62  CommandDefinition defRouteRemove("route", "remove");
63  defRouteRemove
64  .setTitle("remove a route")
68  parser.addCommand(defRouteRemove, &RibModule::remove);
69 }
70 
71 void
73 {
74  auto nexthopIt = ctx.args.find("nexthop");
75  std::set<uint64_t> nexthops;
76  auto origin = ctx.args.getOptional<RouteOrigin>("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;
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&, const Route& route) {
100  return (nexthops.empty() || nexthops.count(route.getFaceId()) > 0) &&
101  (!origin || route.getOrigin() == *origin);
102  });
103 }
104 
105 void
107 {
108  auto prefix = ctx.args.get<Name>("prefix");
109 
110  listRoutesImpl(ctx, [&] (const RibEntry& entry, const Route&) {
111  return entry.getName() == prefix;
112  });
113 }
114 
115 void
116 RibModule::listRoutesImpl(ExecuteContext& ctx, const RoutePredicate& filter)
117 {
118  ctx.controller.fetch<ndn::nfd::RibDataset>(
119  [&] (const auto& 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 
142 void
144 {
145  auto prefix = ctx.args.get<Name>("prefix");
146  auto nexthop = ctx.args.at("nexthop");
147  auto origin = ctx.args.get<RouteOrigin>("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  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 ";
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 = [&] {
189  const FaceUri* faceUri = std::any_cast<FaceUri>(&nexthop);
190  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 
203  auto [canonized, error] = canonize(ctx, *faceUri);
204  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 
225  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:
231  registerRoute(findFace.getFaceId());
232  break;
235  ctx.err << findFace.getErrorReason() << '\n';
236  return;
238  // Attempt to create face if it doesn't exist
239  handleFaceNotFound();
240  break;
242  ctx.err << "Multiple faces match specified remote FaceUri. Re-run the command with a FaceId:";
244  ctx.err << '\n';
245  return;
246  default:
247  BOOST_ASSERT_MSG(false, "unexpected FindFace result");
248  return;
249  }
250 
251  ctx.face.processEvents();
252 }
253 
254 void
256 {
257  auto prefix = ctx.args.get<Name>("prefix");
258  auto nexthop = ctx.args.at("nexthop");
259  auto origin = ctx.args.get<RouteOrigin>("origin", ndn::nfd::ROUTE_ORIGIN_STATIC);
260 
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;
271  ctx.err << findFace.getErrorReason() << '\n';
272  return;
273  default:
274  BOOST_ASSERT_MSG(false, "unexpected FindFace result");
275  return;
276  }
277 
278  for (uint64_t faceId : findFace.getFaceIds()) {
279  ControlParameters unregisterParams;
280  unregisterParams
281  .setName(prefix)
282  .setFaceId(faceId)
283  .setOrigin(origin);
284 
285  ctx.controller.start<ndn::nfd::RibUnregisterCommand>(
286  unregisterParams,
287  [&] (const ControlParameters& resp) {
288  ctx.out << "route-removed ";
290  ctx.out << ia("prefix") << resp.getName()
291  << ia("nexthop") << resp.getFaceId()
292  << ia("origin") << resp.getOrigin()
293  << '\n';
294  },
295  ctx.makeCommandFailureHandler("removing route"),
296  ctx.makeCommandOptions());
297  }
298 
299  ctx.face.processEvents();
300 }
301 
302 void
303 RibModule::fetchStatus(Controller& controller,
304  const std::function<void()>& onSuccess,
305  const Controller::DatasetFailCallback& onFailure,
306  const CommandOptions& options)
307 {
308  controller.fetch<ndn::nfd::RibDataset>(
309  [this, onSuccess] (const auto& result) {
310  m_status = result;
311  onSuccess();
312  },
313  onFailure, options);
314 }
315 
316 void
317 RibModule::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 
326 void
327 RibModule::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>"
337  << "<origin>" << route.getOrigin() << "</origin>"
338  << "<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  }
352  if (route.hasExpirationPeriod()) {
353  os << "<expirationPeriod>"
354  << xml::formatDuration(time::duration_cast<time::seconds>(route.getExpirationPeriod()))
355  << "</expirationPeriod>";
356  }
357  os << "</route>";
358  }
359  os << "</routes>";
360 
361  os << "</ribEntry>";
362 }
363 
364 void
365 RibModule::formatStatusText(std::ostream& os) const
366 {
367  os << "RIB:\n";
368  for (const RibEntry& item : m_status) {
369  os << " ";
370  formatEntryText(os, item);
371  os << '\n';
372  }
373 }
374 
375 void
376 RibModule::formatEntryText(std::ostream& os, const RibEntry& entry)
377 {
378  os << entry.getName() << " routes={";
379 
380  text::Separator sep(", ");
381  for (const Route& route : entry.getRoutes()) {
382  os << sep;
383  formatRouteText(os, entry, route, false);
384  }
385 
386  os << "}";
387 }
388 
389 void
390 RibModule::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();
399  os << ia("origin") << route.getOrigin();
400  os << ia("cost") << route.getCost();
401  os << ia("flags") << static_cast<ndn::nfd::RouteFlags>(route.getFlags());
402  if (route.hasExpirationPeriod()) {
403  os << ia("expires") << text::formatDuration<time::seconds>(route.getExpirationPeriod());
404  }
405  else {
406  os << ia("expires") << "never";
407  }
408 }
409 
410 } // namespace nfd::tools::nfdc
Represents a route for a name prefix.
Definition: route.hpp:43
std::underlying_type_t< ndn::nfd::RouteFlags > getFlags() const
Definition: route.hpp:74
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
Controller::DatasetFailCallback makeDatasetFailureHandler(const std::string &datasetName)
const CommandArguments & args
Controller::CommandFailCallback 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.
static void add(ExecuteContext &ctx)
The 'route add' command.
Definition: rib-module.cpp:143
void fetchStatus(Controller &controller, const std::function< void()> &onSuccess, const Controller::DatasetFailCallback &onFailure, const CommandOptions &options) override
Collect status from NFD.
Definition: rib-module.cpp:303
static void show(ExecuteContext &ctx)
The 'route show' command.
Definition: rib-module.cpp:106
void formatStatusText(std::ostream &os) const override
Format collected status as text.
Definition: rib-module.cpp:365
void formatStatusXml(std::ostream &os) const override
Format collected status as XML.
Definition: rib-module.cpp:317
static void registerCommands(CommandParser &parser)
Register 'route list', 'route show', 'route add', 'route remove' commands.
Definition: rib-module.cpp:34
static void list(ExecuteContext &ctx)
The 'route list' command.
Definition: rib-module.cpp:72
static void remove(ExecuteContext &ctx)
The 'route remove' command.
Definition: rib-module.cpp:255
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.