blob: d0489cf2fb1c29c3d211bc1ba95e59b209c9294f [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 "canonizer.hpp"
28#include "face-module.hpp"
Junxiao Shi918e5d42017-02-25 03:58:21 +000029#include "find-face.hpp"
Junxiao Shi38f4ce92016-08-04 10:01:52 +000030#include "format-helpers.hpp"
31
Davide Pesaventoe422f9e2022-06-03 01:30:23 -040032namespace nfd::tools::nfdc {
Junxiao Shi38f4ce92016-08-04 10:01:52 +000033
34void
Junxiao Shi918e5d42017-02-25 03:58:21 +000035RibModule::registerCommands(CommandParser& parser)
36{
Junxiao Shi1d62e622017-03-08 22:39:28 +000037 CommandDefinition defRouteList("route", "list");
38 defRouteList
39 .setTitle("print RIB routes")
40 .addArg("nexthop", ArgValueType::FACE_ID_OR_URI, Required::NO, Positional::YES)
Junxiao Shi8eda6822017-04-12 02:53:14 +000041 .addArg("origin", ArgValueType::ROUTE_ORIGIN, Required::NO, Positional::NO);
Junxiao Shi1d62e622017-03-08 22:39:28 +000042 parser.addCommand(defRouteList, &RibModule::list);
Davide Pesaventod2147442018-02-19 23:58:17 -050043 parser.addAlias("route", "list", "");
Junxiao Shi1d62e622017-03-08 22:39:28 +000044
45 CommandDefinition defRouteShow("route", "show");
46 defRouteShow
47 .setTitle("show routes toward a prefix")
48 .addArg("prefix", ArgValueType::NAME, Required::YES, Positional::YES);
49 parser.addCommand(defRouteShow, &RibModule::show);
50
Junxiao Shi918e5d42017-02-25 03:58:21 +000051 CommandDefinition defRouteAdd("route", "add");
52 defRouteAdd
53 .setTitle("add a route")
54 .addArg("prefix", ArgValueType::NAME, Required::YES, Positional::YES)
55 .addArg("nexthop", ArgValueType::FACE_ID_OR_URI, Required::YES, Positional::YES)
Junxiao Shi8eda6822017-04-12 02:53:14 +000056 .addArg("origin", ArgValueType::ROUTE_ORIGIN, Required::NO, Positional::NO)
Junxiao Shi918e5d42017-02-25 03:58:21 +000057 .addArg("cost", ArgValueType::UNSIGNED, Required::NO, Positional::NO)
58 .addArg("no-inherit", ArgValueType::NONE, Required::NO, Positional::NO)
59 .addArg("capture", ArgValueType::NONE, Required::NO, Positional::NO)
60 .addArg("expires", ArgValueType::UNSIGNED, Required::NO, Positional::NO);
61 parser.addCommand(defRouteAdd, &RibModule::add);
Junxiao Shi084b7952017-02-26 22:00:53 +000062
63 CommandDefinition defRouteRemove("route", "remove");
64 defRouteRemove
65 .setTitle("remove a route")
66 .addArg("prefix", ArgValueType::NAME, Required::YES, Positional::YES)
67 .addArg("nexthop", ArgValueType::FACE_ID_OR_URI, Required::YES, Positional::YES)
Junxiao Shi8eda6822017-04-12 02:53:14 +000068 .addArg("origin", ArgValueType::ROUTE_ORIGIN, Required::NO, Positional::NO);
Junxiao Shi084b7952017-02-26 22:00:53 +000069 parser.addCommand(defRouteRemove, &RibModule::remove);
Junxiao Shi918e5d42017-02-25 03:58:21 +000070}
71
72void
Junxiao Shi1d62e622017-03-08 22:39:28 +000073RibModule::list(ExecuteContext& ctx)
74{
75 auto nexthopIt = ctx.args.find("nexthop");
76 std::set<uint64_t> nexthops;
Junxiao Shi8eda6822017-04-12 02:53:14 +000077 auto origin = ctx.args.getOptional<RouteOrigin>("origin");
Junxiao Shi1d62e622017-03-08 22:39:28 +000078
79 if (nexthopIt != ctx.args.end()) {
80 FindFace findFace(ctx);
81 FindFace::Code res = findFace.execute(nexthopIt->second, true);
82
83 ctx.exitCode = static_cast<int>(res);
84 switch (res) {
85 case FindFace::Code::OK:
86 break;
87 case FindFace::Code::ERROR:
88 case FindFace::Code::CANONIZE_ERROR:
89 case FindFace::Code::NOT_FOUND:
90 ctx.err << findFace.getErrorReason() << '\n';
91 return;
92 default:
93 BOOST_ASSERT_MSG(false, "unexpected FindFace result");
94 return;
95 }
96
97 nexthops = findFace.getFaceIds();
98 }
99
100 listRoutesImpl(ctx, [&] (const RibEntry& entry, const Route& route) {
101 return (nexthops.empty() || nexthops.count(route.getFaceId()) > 0) &&
102 (!origin || route.getOrigin() == *origin);
103 });
104}
105
106void
107RibModule::show(ExecuteContext& ctx)
108{
109 auto prefix = ctx.args.get<Name>("prefix");
110
111 listRoutesImpl(ctx, [&] (const RibEntry& entry, const Route& route) {
112 return entry.getName() == prefix;
113 });
114}
115
116void
117RibModule::listRoutesImpl(ExecuteContext& ctx, const RoutePredicate& filter)
118{
119 ctx.controller.fetch<ndn::nfd::RibDataset>(
120 [&] (const std::vector<RibEntry>& dataset) {
121 bool hasRoute = false;
122 for (const RibEntry& entry : dataset) {
123 for (const Route& route : entry.getRoutes()) {
124 if (filter(entry, route)) {
125 hasRoute = true;
126 formatRouteText(ctx.out, entry, route, true);
127 ctx.out << '\n';
128 }
129 }
130 }
131
132 if (!hasRoute) {
133 ctx.exitCode = 6;
134 ctx.err << "Route not found\n";
135 }
136 },
137 ctx.makeDatasetFailureHandler("RIB dataset"),
138 ctx.makeCommandOptions());
139
140 ctx.face.processEvents();
141}
142
143void
Junxiao Shi918e5d42017-02-25 03:58:21 +0000144RibModule::add(ExecuteContext& ctx)
145{
146 auto prefix = ctx.args.get<Name>("prefix");
Davide Pesavento8b663a92018-11-21 22:57:20 -0500147 auto nexthop = ctx.args.at("nexthop");
Junxiao Shi8eda6822017-04-12 02:53:14 +0000148 auto origin = ctx.args.get<RouteOrigin>("origin", ndn::nfd::ROUTE_ORIGIN_STATIC);
Junxiao Shi918e5d42017-02-25 03:58:21 +0000149 auto cost = ctx.args.get<uint64_t>("cost", 0);
150 bool wantChildInherit = !ctx.args.get<bool>("no-inherit", false);
151 bool wantCapture = ctx.args.get<bool>("capture", false);
152 auto expiresMillis = ctx.args.getOptional<uint64_t>("expires");
153
Eric Newberryd656aff2020-04-03 00:30:38 -0700154 auto registerRoute = [&] (uint64_t faceId) {
155 ControlParameters registerParams;
156 registerParams
157 .setName(prefix)
158 .setFaceId(faceId)
159 .setOrigin(origin)
160 .setCost(cost)
161 .setFlags((wantChildInherit ? ndn::nfd::ROUTE_FLAG_CHILD_INHERIT : ndn::nfd::ROUTE_FLAGS_NONE) |
162 (wantCapture ? ndn::nfd::ROUTE_FLAG_CAPTURE : ndn::nfd::ROUTE_FLAGS_NONE));
163 if (expiresMillis) {
164 registerParams.setExpirationPeriod(time::milliseconds(*expiresMillis));
165 }
166
167 ctx.controller.start<ndn::nfd::RibRegisterCommand>(
168 registerParams,
169 [&] (const ControlParameters& resp) {
170 ctx.exitCode = static_cast<int>(FindFace::Code::OK);
171 ctx.out << "route-add-accepted ";
172 text::ItemAttributes ia;
173 ctx.out << ia("prefix") << resp.getName()
174 << ia("nexthop") << resp.getFaceId()
175 << ia("origin") << resp.getOrigin()
176 << ia("cost") << resp.getCost()
177 << ia("flags") << static_cast<ndn::nfd::RouteFlags>(resp.getFlags());
178 if (resp.hasExpirationPeriod()) {
179 ctx.out << ia("expires") << text::formatDuration<time::milliseconds>(resp.getExpirationPeriod()) << "\n";
180 }
181 else {
182 ctx.out<< ia("expires") << "never\n";
183 }
184 },
185 ctx.makeCommandFailureHandler("adding route"),
186 ctx.makeCommandOptions());
187 };
188
189 auto handleFaceNotFound = [&] {
Davide Pesaventob7bfcb92022-05-22 23:55:23 -0400190 const FaceUri* faceUri = std::any_cast<FaceUri>(&nexthop);
Eric Newberryd656aff2020-04-03 00:30:38 -0700191 if (faceUri == nullptr) {
192 ctx.err << "Face not found\n";
193 return;
194 }
195
196 if (faceUri->getScheme() == "ether") {
197 // Unicast Ethernet faces require a LocalUri, which hasn't been provided
198 // Multicast Ethernet faces cannot be created via management (already exist on each interface)
199 ctx.err << "Unable to implicitly create Ethernet faces\n";
200 ctx.err << "Please create the face with 'nfdc face create' before adding the route\n";
201 return;
202 }
203
Davide Pesaventoa3a7a4e2022-05-29 16:06:22 -0400204 auto [canonized, error] = canonize(ctx, *faceUri);
Eric Newberryd656aff2020-04-03 00:30:38 -0700205 if (!canonized) {
206 // Canonization failed
207 auto canonizationError = canonizeErrorHelper(*faceUri, error);
208 ctx.exitCode = static_cast<int>(canonizationError.first);
209 ctx.err << canonizationError.second << '\n';
210 return;
211 }
212
213 ControlParameters faceCreateParams;
214 faceCreateParams.setUri(canonized->toString());
215
216 ctx.controller.start<ndn::nfd::FaceCreateCommand>(
217 faceCreateParams,
218 [&] (const ControlParameters& resp) {
219 FaceModule::printSuccess(ctx.out, "face-created", resp);
220 registerRoute(resp.getFaceId());
221 },
222 ctx.makeCommandFailureHandler("implicitly creating face"),
223 ctx.makeCommandOptions());
224 };
225
Junxiao Shi918e5d42017-02-25 03:58:21 +0000226 FindFace findFace(ctx);
227 FindFace::Code res = findFace.execute(nexthop);
228
229 ctx.exitCode = static_cast<int>(res);
230 switch (res) {
231 case FindFace::Code::OK:
Eric Newberryd656aff2020-04-03 00:30:38 -0700232 registerRoute(findFace.getFaceId());
Junxiao Shi918e5d42017-02-25 03:58:21 +0000233 break;
234 case FindFace::Code::ERROR:
235 case FindFace::Code::CANONIZE_ERROR:
Junxiao Shi918e5d42017-02-25 03:58:21 +0000236 ctx.err << findFace.getErrorReason() << '\n';
237 return;
Eric Newberryd656aff2020-04-03 00:30:38 -0700238 case FindFace::Code::NOT_FOUND:
239 // Attempt to create face if it doesn't exist
240 handleFaceNotFound();
241 break;
Junxiao Shi918e5d42017-02-25 03:58:21 +0000242 case FindFace::Code::AMBIGUOUS:
243 ctx.err << "Multiple faces match specified remote FaceUri. Re-run the command with a FaceId:";
244 findFace.printDisambiguation(ctx.err, FindFace::DisambiguationStyle::LOCAL_URI);
245 ctx.err << '\n';
246 return;
247 default:
248 BOOST_ASSERT_MSG(false, "unexpected FindFace result");
249 return;
250 }
251
Junxiao Shi918e5d42017-02-25 03:58:21 +0000252 ctx.face.processEvents();
253}
254
255void
Junxiao Shi084b7952017-02-26 22:00:53 +0000256RibModule::remove(ExecuteContext& ctx)
257{
258 auto prefix = ctx.args.get<Name>("prefix");
Davide Pesavento8b663a92018-11-21 22:57:20 -0500259 auto nexthop = ctx.args.at("nexthop");
Junxiao Shi8eda6822017-04-12 02:53:14 +0000260 auto origin = ctx.args.get<RouteOrigin>("origin", ndn::nfd::ROUTE_ORIGIN_STATIC);
Junxiao Shi084b7952017-02-26 22:00:53 +0000261
262 FindFace findFace(ctx);
263 FindFace::Code res = findFace.execute(nexthop, true);
264
265 ctx.exitCode = static_cast<int>(res);
266 switch (res) {
267 case FindFace::Code::OK:
268 break;
269 case FindFace::Code::ERROR:
270 case FindFace::Code::CANONIZE_ERROR:
271 case FindFace::Code::NOT_FOUND:
272 ctx.err << findFace.getErrorReason() << '\n';
273 return;
274 default:
275 BOOST_ASSERT_MSG(false, "unexpected FindFace result");
276 return;
277 }
278
Junxiao Shi1d62e622017-03-08 22:39:28 +0000279 for (uint64_t faceId : findFace.getFaceIds()) {
Junxiao Shi084b7952017-02-26 22:00:53 +0000280 ControlParameters unregisterParams;
281 unregisterParams
282 .setName(prefix)
Junxiao Shi1d62e622017-03-08 22:39:28 +0000283 .setFaceId(faceId)
Junxiao Shi084b7952017-02-26 22:00:53 +0000284 .setOrigin(origin);
285
286 ctx.controller.start<ndn::nfd::RibUnregisterCommand>(
287 unregisterParams,
288 [&] (const ControlParameters& resp) {
289 ctx.out << "route-removed ";
290 text::ItemAttributes ia;
291 ctx.out << ia("prefix") << resp.getName()
292 << ia("nexthop") << resp.getFaceId()
Davide Pesavento22db5392017-04-14 00:56:43 -0400293 << ia("origin") << resp.getOrigin()
Junxiao Shi084b7952017-02-26 22:00:53 +0000294 << '\n';
295 },
296 ctx.makeCommandFailureHandler("removing route"),
297 ctx.makeCommandOptions());
298 }
299
300 ctx.face.processEvents();
301}
302
303void
Junxiao Shi38f4ce92016-08-04 10:01:52 +0000304RibModule::fetchStatus(Controller& controller,
Davide Pesavento87fc0f82018-04-11 23:43:51 -0400305 const std::function<void()>& onSuccess,
Junxiao Shi29b41282016-08-22 03:47:02 +0000306 const Controller::DatasetFailCallback& onFailure,
Junxiao Shi38f4ce92016-08-04 10:01:52 +0000307 const CommandOptions& options)
308{
309 controller.fetch<ndn::nfd::RibDataset>(
310 [this, onSuccess] (const std::vector<RibEntry>& result) {
311 m_status = result;
312 onSuccess();
313 },
314 onFailure, options);
315}
316
317void
318RibModule::formatStatusXml(std::ostream& os) const
319{
320 os << "<rib>";
321 for (const RibEntry& item : m_status) {
322 this->formatItemXml(os, item);
323 }
324 os << "</rib>";
325}
326
327void
328RibModule::formatItemXml(std::ostream& os, const RibEntry& item) const
329{
330 os << "<ribEntry>";
331
332 os << "<prefix>" << xml::Text{item.getName().toUri()} << "</prefix>";
333
334 os << "<routes>";
335 for (const Route& route : item.getRoutes()) {
336 os << "<route>"
337 << "<faceId>" << route.getFaceId() << "</faceId>"
Davide Pesavento22db5392017-04-14 00:56:43 -0400338 << "<origin>" << route.getOrigin() << "</origin>"
Junxiao Shi38f4ce92016-08-04 10:01:52 +0000339 << "<cost>" << route.getCost() << "</cost>";
340 if (route.getFlags() == ndn::nfd::ROUTE_FLAGS_NONE) {
341 os << "<flags/>";
342 }
343 else {
344 os << "<flags>";
345 if (route.isChildInherit()) {
346 os << "<childInherit/>";
347 }
348 if (route.isRibCapture()) {
349 os << "<ribCapture/>";
350 }
351 os << "</flags>";
352 }
Davide Pesavento26cbdbd2017-02-19 21:37:43 -0500353 if (route.hasExpirationPeriod()) {
Junxiao Shi38f4ce92016-08-04 10:01:52 +0000354 os << "<expirationPeriod>"
Eric Newberryde332452018-01-30 11:45:32 -0700355 << xml::formatDuration(time::duration_cast<time::seconds>(route.getExpirationPeriod()))
Junxiao Shi38f4ce92016-08-04 10:01:52 +0000356 << "</expirationPeriod>";
357 }
358 os << "</route>";
359 }
360 os << "</routes>";
361
362 os << "</ribEntry>";
363}
364
365void
366RibModule::formatStatusText(std::ostream& os) const
367{
368 os << "RIB:\n";
369 for (const RibEntry& item : m_status) {
Junxiao Shi1d62e622017-03-08 22:39:28 +0000370 os << " ";
371 formatEntryText(os, item);
372 os << '\n';
Junxiao Shi38f4ce92016-08-04 10:01:52 +0000373 }
374}
375
376void
Junxiao Shi1d62e622017-03-08 22:39:28 +0000377RibModule::formatEntryText(std::ostream& os, const RibEntry& entry)
Junxiao Shi38f4ce92016-08-04 10:01:52 +0000378{
Junxiao Shi1d62e622017-03-08 22:39:28 +0000379 os << entry.getName() << " routes={";
Junxiao Shi38f4ce92016-08-04 10:01:52 +0000380
381 text::Separator sep(", ");
Junxiao Shi1d62e622017-03-08 22:39:28 +0000382 for (const Route& route : entry.getRoutes()) {
383 os << sep;
384 formatRouteText(os, entry, route, false);
Junxiao Shi38f4ce92016-08-04 10:01:52 +0000385 }
386
387 os << "}";
Junxiao Shi1d62e622017-03-08 22:39:28 +0000388}
389
390void
391RibModule::formatRouteText(std::ostream& os, const RibEntry& entry, const Route& route,
392 bool includePrefix)
393{
394 text::ItemAttributes ia;
395
396 if (includePrefix) {
397 os << ia("prefix") << entry.getName();
398 }
399 os << ia("nexthop") << route.getFaceId();
Davide Pesavento22db5392017-04-14 00:56:43 -0400400 os << ia("origin") << route.getOrigin();
Junxiao Shi1d62e622017-03-08 22:39:28 +0000401 os << ia("cost") << route.getCost();
402 os << ia("flags") << static_cast<ndn::nfd::RouteFlags>(route.getFlags());
Junxiao Shi1d62e622017-03-08 22:39:28 +0000403 if (route.hasExpirationPeriod()) {
Eric Newberryde332452018-01-30 11:45:32 -0700404 os << ia("expires") << text::formatDuration<time::seconds>(route.getExpirationPeriod());
Junxiao Shi1d62e622017-03-08 22:39:28 +0000405 }
406 else {
407 os << ia("expires") << "never";
408 }
Junxiao Shi38f4ce92016-08-04 10:01:52 +0000409}
410
Davide Pesaventoe422f9e2022-06-03 01:30:23 -0400411} // namespace nfd::tools::nfdc