blob: 108709bc44ff08e91ecb5aa59aaaed7790c1dcc5 [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 Pesavento40641272023-03-16 13:31:12 -04003 * Copyright (c) 2014-2023, 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 Pesavento40641272023-03-16 13:31:12 -040031#include <ndn-cxx/mgmt/nfd/status-dataset.hpp>
32
Davide Pesaventoe422f9e2022-06-03 01:30:23 -040033namespace nfd::tools::nfdc {
Junxiao Shi38f4ce92016-08-04 10:01:52 +000034
35void
Junxiao Shi918e5d42017-02-25 03:58:21 +000036RibModule::registerCommands(CommandParser& parser)
37{
Junxiao Shi1d62e622017-03-08 22:39:28 +000038 CommandDefinition defRouteList("route", "list");
39 defRouteList
40 .setTitle("print RIB routes")
41 .addArg("nexthop", ArgValueType::FACE_ID_OR_URI, Required::NO, Positional::YES)
Junxiao Shi8eda6822017-04-12 02:53:14 +000042 .addArg("origin", ArgValueType::ROUTE_ORIGIN, Required::NO, Positional::NO);
Junxiao Shi1d62e622017-03-08 22:39:28 +000043 parser.addCommand(defRouteList, &RibModule::list);
Davide Pesaventod2147442018-02-19 23:58:17 -050044 parser.addAlias("route", "list", "");
Junxiao Shi1d62e622017-03-08 22:39:28 +000045
46 CommandDefinition defRouteShow("route", "show");
47 defRouteShow
48 .setTitle("show routes toward a prefix")
49 .addArg("prefix", ArgValueType::NAME, Required::YES, Positional::YES);
50 parser.addCommand(defRouteShow, &RibModule::show);
51
Junxiao Shi918e5d42017-02-25 03:58:21 +000052 CommandDefinition defRouteAdd("route", "add");
53 defRouteAdd
54 .setTitle("add a route")
55 .addArg("prefix", ArgValueType::NAME, Required::YES, Positional::YES)
56 .addArg("nexthop", ArgValueType::FACE_ID_OR_URI, Required::YES, Positional::YES)
Junxiao Shi8eda6822017-04-12 02:53:14 +000057 .addArg("origin", ArgValueType::ROUTE_ORIGIN, Required::NO, Positional::NO)
Junxiao Shi918e5d42017-02-25 03:58:21 +000058 .addArg("cost", ArgValueType::UNSIGNED, Required::NO, Positional::NO)
59 .addArg("no-inherit", ArgValueType::NONE, Required::NO, Positional::NO)
60 .addArg("capture", ArgValueType::NONE, Required::NO, Positional::NO)
61 .addArg("expires", ArgValueType::UNSIGNED, Required::NO, Positional::NO);
62 parser.addCommand(defRouteAdd, &RibModule::add);
Junxiao Shi084b7952017-02-26 22:00:53 +000063
64 CommandDefinition defRouteRemove("route", "remove");
65 defRouteRemove
66 .setTitle("remove a route")
67 .addArg("prefix", ArgValueType::NAME, Required::YES, Positional::YES)
68 .addArg("nexthop", ArgValueType::FACE_ID_OR_URI, Required::YES, Positional::YES)
Junxiao Shi8eda6822017-04-12 02:53:14 +000069 .addArg("origin", ArgValueType::ROUTE_ORIGIN, Required::NO, Positional::NO);
Junxiao Shi084b7952017-02-26 22:00:53 +000070 parser.addCommand(defRouteRemove, &RibModule::remove);
Junxiao Shi918e5d42017-02-25 03:58:21 +000071}
72
73void
Junxiao Shi1d62e622017-03-08 22:39:28 +000074RibModule::list(ExecuteContext& ctx)
75{
76 auto nexthopIt = ctx.args.find("nexthop");
77 std::set<uint64_t> nexthops;
Junxiao Shi8eda6822017-04-12 02:53:14 +000078 auto origin = ctx.args.getOptional<RouteOrigin>("origin");
Junxiao Shi1d62e622017-03-08 22:39:28 +000079
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;
88 case FindFace::Code::ERROR:
89 case FindFace::Code::CANONIZE_ERROR:
90 case FindFace::Code::NOT_FOUND:
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
Davide Pesavento990620a2022-06-05 00:25:53 -0400101 listRoutesImpl(ctx, [&] (const RibEntry&, const Route& route) {
Junxiao Shi1d62e622017-03-08 22:39:28 +0000102 return (nexthops.empty() || nexthops.count(route.getFaceId()) > 0) &&
103 (!origin || route.getOrigin() == *origin);
104 });
105}
106
107void
108RibModule::show(ExecuteContext& ctx)
109{
110 auto prefix = ctx.args.get<Name>("prefix");
111
Davide Pesavento990620a2022-06-05 00:25:53 -0400112 listRoutesImpl(ctx, [&] (const RibEntry& entry, const Route&) {
Junxiao Shi1d62e622017-03-08 22:39:28 +0000113 return entry.getName() == prefix;
114 });
115}
116
117void
118RibModule::listRoutesImpl(ExecuteContext& ctx, const RoutePredicate& filter)
119{
120 ctx.controller.fetch<ndn::nfd::RibDataset>(
Davide Pesavento990620a2022-06-05 00:25:53 -0400121 [&] (const auto& dataset) {
Junxiao Shi1d62e622017-03-08 22:39:28 +0000122 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
Junxiao Shi918e5d42017-02-25 03:58:21 +0000145RibModule::add(ExecuteContext& ctx)
146{
147 auto prefix = ctx.args.get<Name>("prefix");
Davide Pesavento8b663a92018-11-21 22:57:20 -0500148 auto nexthop = ctx.args.at("nexthop");
Junxiao Shi8eda6822017-04-12 02:53:14 +0000149 auto origin = ctx.args.get<RouteOrigin>("origin", ndn::nfd::ROUTE_ORIGIN_STATIC);
Junxiao Shi918e5d42017-02-25 03:58:21 +0000150 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
Eric Newberryd656aff2020-04-03 00:30:38 -0700155 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 ";
173 text::ItemAttributes ia;
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 = [&] {
Davide Pesaventob7bfcb92022-05-22 23:55:23 -0400191 const FaceUri* faceUri = std::any_cast<FaceUri>(&nexthop);
Eric Newberryd656aff2020-04-03 00:30:38 -0700192 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
Davide Pesaventoa3a7a4e2022-05-29 16:06:22 -0400205 auto [canonized, error] = canonize(ctx, *faceUri);
Eric Newberryd656aff2020-04-03 00:30:38 -0700206 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
Junxiao Shi918e5d42017-02-25 03:58:21 +0000227 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:
Eric Newberryd656aff2020-04-03 00:30:38 -0700233 registerRoute(findFace.getFaceId());
Junxiao Shi918e5d42017-02-25 03:58:21 +0000234 break;
235 case FindFace::Code::ERROR:
236 case FindFace::Code::CANONIZE_ERROR:
Junxiao Shi918e5d42017-02-25 03:58:21 +0000237 ctx.err << findFace.getErrorReason() << '\n';
238 return;
Eric Newberryd656aff2020-04-03 00:30:38 -0700239 case FindFace::Code::NOT_FOUND:
240 // Attempt to create face if it doesn't exist
241 handleFaceNotFound();
242 break;
Junxiao Shi918e5d42017-02-25 03:58:21 +0000243 case FindFace::Code::AMBIGUOUS:
244 ctx.err << "Multiple faces match specified remote FaceUri. Re-run the command with a FaceId:";
245 findFace.printDisambiguation(ctx.err, FindFace::DisambiguationStyle::LOCAL_URI);
246 ctx.err << '\n';
247 return;
248 default:
249 BOOST_ASSERT_MSG(false, "unexpected FindFace result");
250 return;
251 }
252
Junxiao Shi918e5d42017-02-25 03:58:21 +0000253 ctx.face.processEvents();
254}
255
256void
Junxiao Shi084b7952017-02-26 22:00:53 +0000257RibModule::remove(ExecuteContext& ctx)
258{
259 auto prefix = ctx.args.get<Name>("prefix");
Davide Pesavento8b663a92018-11-21 22:57:20 -0500260 auto nexthop = ctx.args.at("nexthop");
Junxiao Shi8eda6822017-04-12 02:53:14 +0000261 auto origin = ctx.args.get<RouteOrigin>("origin", ndn::nfd::ROUTE_ORIGIN_STATIC);
Junxiao Shi084b7952017-02-26 22:00:53 +0000262
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;
270 case FindFace::Code::ERROR:
271 case FindFace::Code::CANONIZE_ERROR:
272 case FindFace::Code::NOT_FOUND:
273 ctx.err << findFace.getErrorReason() << '\n';
274 return;
275 default:
276 BOOST_ASSERT_MSG(false, "unexpected FindFace result");
277 return;
278 }
279
Junxiao Shi1d62e622017-03-08 22:39:28 +0000280 for (uint64_t faceId : findFace.getFaceIds()) {
Junxiao Shi084b7952017-02-26 22:00:53 +0000281 ControlParameters unregisterParams;
282 unregisterParams
283 .setName(prefix)
Junxiao Shi1d62e622017-03-08 22:39:28 +0000284 .setFaceId(faceId)
Junxiao Shi084b7952017-02-26 22:00:53 +0000285 .setOrigin(origin);
286
287 ctx.controller.start<ndn::nfd::RibUnregisterCommand>(
288 unregisterParams,
289 [&] (const ControlParameters& resp) {
290 ctx.out << "route-removed ";
291 text::ItemAttributes ia;
292 ctx.out << ia("prefix") << resp.getName()
293 << ia("nexthop") << resp.getFaceId()
Davide Pesavento22db5392017-04-14 00:56:43 -0400294 << ia("origin") << resp.getOrigin()
Junxiao Shi084b7952017-02-26 22:00:53 +0000295 << '\n';
296 },
297 ctx.makeCommandFailureHandler("removing route"),
298 ctx.makeCommandOptions());
299 }
300
301 ctx.face.processEvents();
302}
303
304void
Davide Pesavento63b3ae82023-03-24 23:53:24 -0400305RibModule::fetchStatus(ndn::nfd::Controller& controller,
Davide Pesavento87fc0f82018-04-11 23:43:51 -0400306 const std::function<void()>& onSuccess,
Davide Pesavento63b3ae82023-03-24 23:53:24 -0400307 const ndn::nfd::DatasetFailureCallback& onFailure,
Junxiao Shi38f4ce92016-08-04 10:01:52 +0000308 const CommandOptions& options)
309{
310 controller.fetch<ndn::nfd::RibDataset>(
Davide Pesavento990620a2022-06-05 00:25:53 -0400311 [this, onSuccess] (const auto& result) {
Junxiao Shi38f4ce92016-08-04 10:01:52 +0000312 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>"
Davide Pesavento22db5392017-04-14 00:56:43 -0400339 << "<origin>" << route.getOrigin() << "</origin>"
Junxiao Shi38f4ce92016-08-04 10:01:52 +0000340 << "<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 }
Davide Pesavento26cbdbd2017-02-19 21:37:43 -0500354 if (route.hasExpirationPeriod()) {
Junxiao Shi38f4ce92016-08-04 10:01:52 +0000355 os << "<expirationPeriod>"
Eric Newberryde332452018-01-30 11:45:32 -0700356 << xml::formatDuration(time::duration_cast<time::seconds>(route.getExpirationPeriod()))
Junxiao Shi38f4ce92016-08-04 10:01:52 +0000357 << "</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) {
Junxiao Shi1d62e622017-03-08 22:39:28 +0000371 os << " ";
372 formatEntryText(os, item);
373 os << '\n';
Junxiao Shi38f4ce92016-08-04 10:01:52 +0000374 }
375}
376
377void
Junxiao Shi1d62e622017-03-08 22:39:28 +0000378RibModule::formatEntryText(std::ostream& os, const RibEntry& entry)
Junxiao Shi38f4ce92016-08-04 10:01:52 +0000379{
Junxiao Shi1d62e622017-03-08 22:39:28 +0000380 os << entry.getName() << " routes={";
Junxiao Shi38f4ce92016-08-04 10:01:52 +0000381
382 text::Separator sep(", ");
Junxiao Shi1d62e622017-03-08 22:39:28 +0000383 for (const Route& route : entry.getRoutes()) {
384 os << sep;
385 formatRouteText(os, entry, route, false);
Junxiao Shi38f4ce92016-08-04 10:01:52 +0000386 }
387
388 os << "}";
Junxiao Shi1d62e622017-03-08 22:39:28 +0000389}
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();
Davide Pesavento22db5392017-04-14 00:56:43 -0400401 os << ia("origin") << route.getOrigin();
Junxiao Shi1d62e622017-03-08 22:39:28 +0000402 os << ia("cost") << route.getCost();
403 os << ia("flags") << static_cast<ndn::nfd::RouteFlags>(route.getFlags());
Junxiao Shi1d62e622017-03-08 22:39:28 +0000404 if (route.hasExpirationPeriod()) {
Eric Newberryde332452018-01-30 11:45:32 -0700405 os << ia("expires") << text::formatDuration<time::seconds>(route.getExpirationPeriod());
Junxiao Shi1d62e622017-03-08 22:39:28 +0000406 }
407 else {
408 os << ia("expires") << "never";
409 }
Junxiao Shi38f4ce92016-08-04 10:01:52 +0000410}
411
Davide Pesaventoe422f9e2022-06-03 01:30:23 -0400412} // namespace nfd::tools::nfdc