blob: 5ed8553a5f7570e79cbcc42984c39aae4e8da3a3 [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
32namespace nfd {
33namespace tools {
Junxiao Shi331ade72016-08-19 14:07:19 +000034namespace nfdc {
Junxiao Shi38f4ce92016-08-04 10:01:52 +000035
36void
Junxiao Shi918e5d42017-02-25 03:58:21 +000037RibModule::registerCommands(CommandParser& parser)
38{
Junxiao Shi1d62e622017-03-08 22:39:28 +000039 CommandDefinition defRouteList("route", "list");
40 defRouteList
41 .setTitle("print RIB routes")
42 .addArg("nexthop", ArgValueType::FACE_ID_OR_URI, Required::NO, Positional::YES)
Junxiao Shi8eda6822017-04-12 02:53:14 +000043 .addArg("origin", ArgValueType::ROUTE_ORIGIN, Required::NO, Positional::NO);
Junxiao Shi1d62e622017-03-08 22:39:28 +000044 parser.addCommand(defRouteList, &RibModule::list);
Davide Pesaventod2147442018-02-19 23:58:17 -050045 parser.addAlias("route", "list", "");
Junxiao Shi1d62e622017-03-08 22:39:28 +000046
47 CommandDefinition defRouteShow("route", "show");
48 defRouteShow
49 .setTitle("show routes toward a prefix")
50 .addArg("prefix", ArgValueType::NAME, Required::YES, Positional::YES);
51 parser.addCommand(defRouteShow, &RibModule::show);
52
Junxiao Shi918e5d42017-02-25 03:58:21 +000053 CommandDefinition defRouteAdd("route", "add");
54 defRouteAdd
55 .setTitle("add a route")
56 .addArg("prefix", ArgValueType::NAME, Required::YES, Positional::YES)
57 .addArg("nexthop", ArgValueType::FACE_ID_OR_URI, Required::YES, Positional::YES)
Junxiao Shi8eda6822017-04-12 02:53:14 +000058 .addArg("origin", ArgValueType::ROUTE_ORIGIN, Required::NO, Positional::NO)
Junxiao Shi918e5d42017-02-25 03:58:21 +000059 .addArg("cost", ArgValueType::UNSIGNED, Required::NO, Positional::NO)
60 .addArg("no-inherit", ArgValueType::NONE, Required::NO, Positional::NO)
61 .addArg("capture", ArgValueType::NONE, Required::NO, Positional::NO)
62 .addArg("expires", ArgValueType::UNSIGNED, Required::NO, Positional::NO);
63 parser.addCommand(defRouteAdd, &RibModule::add);
Junxiao Shi084b7952017-02-26 22:00:53 +000064
65 CommandDefinition defRouteRemove("route", "remove");
66 defRouteRemove
67 .setTitle("remove a route")
68 .addArg("prefix", ArgValueType::NAME, Required::YES, Positional::YES)
69 .addArg("nexthop", ArgValueType::FACE_ID_OR_URI, Required::YES, Positional::YES)
Junxiao Shi8eda6822017-04-12 02:53:14 +000070 .addArg("origin", ArgValueType::ROUTE_ORIGIN, Required::NO, Positional::NO);
Junxiao Shi084b7952017-02-26 22:00:53 +000071 parser.addCommand(defRouteRemove, &RibModule::remove);
Junxiao Shi918e5d42017-02-25 03:58:21 +000072}
73
74void
Junxiao Shi1d62e622017-03-08 22:39:28 +000075RibModule::list(ExecuteContext& ctx)
76{
77 auto nexthopIt = ctx.args.find("nexthop");
78 std::set<uint64_t> nexthops;
Junxiao Shi8eda6822017-04-12 02:53:14 +000079 auto origin = ctx.args.getOptional<RouteOrigin>("origin");
Junxiao Shi1d62e622017-03-08 22:39:28 +000080
81 if (nexthopIt != ctx.args.end()) {
82 FindFace findFace(ctx);
83 FindFace::Code res = findFace.execute(nexthopIt->second, true);
84
85 ctx.exitCode = static_cast<int>(res);
86 switch (res) {
87 case FindFace::Code::OK:
88 break;
89 case FindFace::Code::ERROR:
90 case FindFace::Code::CANONIZE_ERROR:
91 case FindFace::Code::NOT_FOUND:
92 ctx.err << findFace.getErrorReason() << '\n';
93 return;
94 default:
95 BOOST_ASSERT_MSG(false, "unexpected FindFace result");
96 return;
97 }
98
99 nexthops = findFace.getFaceIds();
100 }
101
102 listRoutesImpl(ctx, [&] (const RibEntry& entry, const Route& route) {
103 return (nexthops.empty() || nexthops.count(route.getFaceId()) > 0) &&
104 (!origin || route.getOrigin() == *origin);
105 });
106}
107
108void
109RibModule::show(ExecuteContext& ctx)
110{
111 auto prefix = ctx.args.get<Name>("prefix");
112
113 listRoutesImpl(ctx, [&] (const RibEntry& entry, const Route& route) {
114 return entry.getName() == prefix;
115 });
116}
117
118void
119RibModule::listRoutesImpl(ExecuteContext& ctx, const RoutePredicate& filter)
120{
121 ctx.controller.fetch<ndn::nfd::RibDataset>(
122 [&] (const std::vector<RibEntry>& dataset) {
123 bool hasRoute = false;
124 for (const RibEntry& entry : dataset) {
125 for (const Route& route : entry.getRoutes()) {
126 if (filter(entry, route)) {
127 hasRoute = true;
128 formatRouteText(ctx.out, entry, route, true);
129 ctx.out << '\n';
130 }
131 }
132 }
133
134 if (!hasRoute) {
135 ctx.exitCode = 6;
136 ctx.err << "Route not found\n";
137 }
138 },
139 ctx.makeDatasetFailureHandler("RIB dataset"),
140 ctx.makeCommandOptions());
141
142 ctx.face.processEvents();
143}
144
145void
Junxiao Shi918e5d42017-02-25 03:58:21 +0000146RibModule::add(ExecuteContext& ctx)
147{
148 auto prefix = ctx.args.get<Name>("prefix");
Davide Pesavento8b663a92018-11-21 22:57:20 -0500149 auto nexthop = ctx.args.at("nexthop");
Junxiao Shi8eda6822017-04-12 02:53:14 +0000150 auto origin = ctx.args.get<RouteOrigin>("origin", ndn::nfd::ROUTE_ORIGIN_STATIC);
Junxiao Shi918e5d42017-02-25 03:58:21 +0000151 auto cost = ctx.args.get<uint64_t>("cost", 0);
152 bool wantChildInherit = !ctx.args.get<bool>("no-inherit", false);
153 bool wantCapture = ctx.args.get<bool>("capture", false);
154 auto expiresMillis = ctx.args.getOptional<uint64_t>("expires");
155
Eric Newberryd656aff2020-04-03 00:30:38 -0700156 auto registerRoute = [&] (uint64_t faceId) {
157 ControlParameters registerParams;
158 registerParams
159 .setName(prefix)
160 .setFaceId(faceId)
161 .setOrigin(origin)
162 .setCost(cost)
163 .setFlags((wantChildInherit ? ndn::nfd::ROUTE_FLAG_CHILD_INHERIT : ndn::nfd::ROUTE_FLAGS_NONE) |
164 (wantCapture ? ndn::nfd::ROUTE_FLAG_CAPTURE : ndn::nfd::ROUTE_FLAGS_NONE));
165 if (expiresMillis) {
166 registerParams.setExpirationPeriod(time::milliseconds(*expiresMillis));
167 }
168
169 ctx.controller.start<ndn::nfd::RibRegisterCommand>(
170 registerParams,
171 [&] (const ControlParameters& resp) {
172 ctx.exitCode = static_cast<int>(FindFace::Code::OK);
173 ctx.out << "route-add-accepted ";
174 text::ItemAttributes ia;
175 ctx.out << ia("prefix") << resp.getName()
176 << ia("nexthop") << resp.getFaceId()
177 << ia("origin") << resp.getOrigin()
178 << ia("cost") << resp.getCost()
179 << ia("flags") << static_cast<ndn::nfd::RouteFlags>(resp.getFlags());
180 if (resp.hasExpirationPeriod()) {
181 ctx.out << ia("expires") << text::formatDuration<time::milliseconds>(resp.getExpirationPeriod()) << "\n";
182 }
183 else {
184 ctx.out<< ia("expires") << "never\n";
185 }
186 },
187 ctx.makeCommandFailureHandler("adding route"),
188 ctx.makeCommandOptions());
189 };
190
191 auto handleFaceNotFound = [&] {
Davide Pesaventob7bfcb92022-05-22 23:55:23 -0400192 const FaceUri* faceUri = std::any_cast<FaceUri>(&nexthop);
Eric Newberryd656aff2020-04-03 00:30:38 -0700193 if (faceUri == nullptr) {
194 ctx.err << "Face not found\n";
195 return;
196 }
197
198 if (faceUri->getScheme() == "ether") {
199 // Unicast Ethernet faces require a LocalUri, which hasn't been provided
200 // Multicast Ethernet faces cannot be created via management (already exist on each interface)
201 ctx.err << "Unable to implicitly create Ethernet faces\n";
202 ctx.err << "Please create the face with 'nfdc face create' before adding the route\n";
203 return;
204 }
205
Davide Pesaventoa3a7a4e2022-05-29 16:06:22 -0400206 auto [canonized, error] = canonize(ctx, *faceUri);
Eric Newberryd656aff2020-04-03 00:30:38 -0700207 if (!canonized) {
208 // Canonization failed
209 auto canonizationError = canonizeErrorHelper(*faceUri, error);
210 ctx.exitCode = static_cast<int>(canonizationError.first);
211 ctx.err << canonizationError.second << '\n';
212 return;
213 }
214
215 ControlParameters faceCreateParams;
216 faceCreateParams.setUri(canonized->toString());
217
218 ctx.controller.start<ndn::nfd::FaceCreateCommand>(
219 faceCreateParams,
220 [&] (const ControlParameters& resp) {
221 FaceModule::printSuccess(ctx.out, "face-created", resp);
222 registerRoute(resp.getFaceId());
223 },
224 ctx.makeCommandFailureHandler("implicitly creating face"),
225 ctx.makeCommandOptions());
226 };
227
Junxiao Shi918e5d42017-02-25 03:58:21 +0000228 FindFace findFace(ctx);
229 FindFace::Code res = findFace.execute(nexthop);
230
231 ctx.exitCode = static_cast<int>(res);
232 switch (res) {
233 case FindFace::Code::OK:
Eric Newberryd656aff2020-04-03 00:30:38 -0700234 registerRoute(findFace.getFaceId());
Junxiao Shi918e5d42017-02-25 03:58:21 +0000235 break;
236 case FindFace::Code::ERROR:
237 case FindFace::Code::CANONIZE_ERROR:
Junxiao Shi918e5d42017-02-25 03:58:21 +0000238 ctx.err << findFace.getErrorReason() << '\n';
239 return;
Eric Newberryd656aff2020-04-03 00:30:38 -0700240 case FindFace::Code::NOT_FOUND:
241 // Attempt to create face if it doesn't exist
242 handleFaceNotFound();
243 break;
Junxiao Shi918e5d42017-02-25 03:58:21 +0000244 case FindFace::Code::AMBIGUOUS:
245 ctx.err << "Multiple faces match specified remote FaceUri. Re-run the command with a FaceId:";
246 findFace.printDisambiguation(ctx.err, FindFace::DisambiguationStyle::LOCAL_URI);
247 ctx.err << '\n';
248 return;
249 default:
250 BOOST_ASSERT_MSG(false, "unexpected FindFace result");
251 return;
252 }
253
Junxiao Shi918e5d42017-02-25 03:58:21 +0000254 ctx.face.processEvents();
255}
256
257void
Junxiao Shi084b7952017-02-26 22:00:53 +0000258RibModule::remove(ExecuteContext& ctx)
259{
260 auto prefix = ctx.args.get<Name>("prefix");
Davide Pesavento8b663a92018-11-21 22:57:20 -0500261 auto nexthop = ctx.args.at("nexthop");
Junxiao Shi8eda6822017-04-12 02:53:14 +0000262 auto origin = ctx.args.get<RouteOrigin>("origin", ndn::nfd::ROUTE_ORIGIN_STATIC);
Junxiao Shi084b7952017-02-26 22:00:53 +0000263
264 FindFace findFace(ctx);
265 FindFace::Code res = findFace.execute(nexthop, true);
266
267 ctx.exitCode = static_cast<int>(res);
268 switch (res) {
269 case FindFace::Code::OK:
270 break;
271 case FindFace::Code::ERROR:
272 case FindFace::Code::CANONIZE_ERROR:
273 case FindFace::Code::NOT_FOUND:
274 ctx.err << findFace.getErrorReason() << '\n';
275 return;
276 default:
277 BOOST_ASSERT_MSG(false, "unexpected FindFace result");
278 return;
279 }
280
Junxiao Shi1d62e622017-03-08 22:39:28 +0000281 for (uint64_t faceId : findFace.getFaceIds()) {
Junxiao Shi084b7952017-02-26 22:00:53 +0000282 ControlParameters unregisterParams;
283 unregisterParams
284 .setName(prefix)
Junxiao Shi1d62e622017-03-08 22:39:28 +0000285 .setFaceId(faceId)
Junxiao Shi084b7952017-02-26 22:00:53 +0000286 .setOrigin(origin);
287
288 ctx.controller.start<ndn::nfd::RibUnregisterCommand>(
289 unregisterParams,
290 [&] (const ControlParameters& resp) {
291 ctx.out << "route-removed ";
292 text::ItemAttributes ia;
293 ctx.out << ia("prefix") << resp.getName()
294 << ia("nexthop") << resp.getFaceId()
Davide Pesavento22db5392017-04-14 00:56:43 -0400295 << ia("origin") << resp.getOrigin()
Junxiao Shi084b7952017-02-26 22:00:53 +0000296 << '\n';
297 },
298 ctx.makeCommandFailureHandler("removing route"),
299 ctx.makeCommandOptions());
300 }
301
302 ctx.face.processEvents();
303}
304
305void
Junxiao Shi38f4ce92016-08-04 10:01:52 +0000306RibModule::fetchStatus(Controller& controller,
Davide Pesavento87fc0f82018-04-11 23:43:51 -0400307 const std::function<void()>& onSuccess,
Junxiao Shi29b41282016-08-22 03:47:02 +0000308 const Controller::DatasetFailCallback& onFailure,
Junxiao Shi38f4ce92016-08-04 10:01:52 +0000309 const CommandOptions& options)
310{
311 controller.fetch<ndn::nfd::RibDataset>(
312 [this, onSuccess] (const std::vector<RibEntry>& result) {
313 m_status = result;
314 onSuccess();
315 },
316 onFailure, options);
317}
318
319void
320RibModule::formatStatusXml(std::ostream& os) const
321{
322 os << "<rib>";
323 for (const RibEntry& item : m_status) {
324 this->formatItemXml(os, item);
325 }
326 os << "</rib>";
327}
328
329void
330RibModule::formatItemXml(std::ostream& os, const RibEntry& item) const
331{
332 os << "<ribEntry>";
333
334 os << "<prefix>" << xml::Text{item.getName().toUri()} << "</prefix>";
335
336 os << "<routes>";
337 for (const Route& route : item.getRoutes()) {
338 os << "<route>"
339 << "<faceId>" << route.getFaceId() << "</faceId>"
Davide Pesavento22db5392017-04-14 00:56:43 -0400340 << "<origin>" << route.getOrigin() << "</origin>"
Junxiao Shi38f4ce92016-08-04 10:01:52 +0000341 << "<cost>" << route.getCost() << "</cost>";
342 if (route.getFlags() == ndn::nfd::ROUTE_FLAGS_NONE) {
343 os << "<flags/>";
344 }
345 else {
346 os << "<flags>";
347 if (route.isChildInherit()) {
348 os << "<childInherit/>";
349 }
350 if (route.isRibCapture()) {
351 os << "<ribCapture/>";
352 }
353 os << "</flags>";
354 }
Davide Pesavento26cbdbd2017-02-19 21:37:43 -0500355 if (route.hasExpirationPeriod()) {
Junxiao Shi38f4ce92016-08-04 10:01:52 +0000356 os << "<expirationPeriod>"
Eric Newberryde332452018-01-30 11:45:32 -0700357 << xml::formatDuration(time::duration_cast<time::seconds>(route.getExpirationPeriod()))
Junxiao Shi38f4ce92016-08-04 10:01:52 +0000358 << "</expirationPeriod>";
359 }
360 os << "</route>";
361 }
362 os << "</routes>";
363
364 os << "</ribEntry>";
365}
366
367void
368RibModule::formatStatusText(std::ostream& os) const
369{
370 os << "RIB:\n";
371 for (const RibEntry& item : m_status) {
Junxiao Shi1d62e622017-03-08 22:39:28 +0000372 os << " ";
373 formatEntryText(os, item);
374 os << '\n';
Junxiao Shi38f4ce92016-08-04 10:01:52 +0000375 }
376}
377
378void
Junxiao Shi1d62e622017-03-08 22:39:28 +0000379RibModule::formatEntryText(std::ostream& os, const RibEntry& entry)
Junxiao Shi38f4ce92016-08-04 10:01:52 +0000380{
Junxiao Shi1d62e622017-03-08 22:39:28 +0000381 os << entry.getName() << " routes={";
Junxiao Shi38f4ce92016-08-04 10:01:52 +0000382
383 text::Separator sep(", ");
Junxiao Shi1d62e622017-03-08 22:39:28 +0000384 for (const Route& route : entry.getRoutes()) {
385 os << sep;
386 formatRouteText(os, entry, route, false);
Junxiao Shi38f4ce92016-08-04 10:01:52 +0000387 }
388
389 os << "}";
Junxiao Shi1d62e622017-03-08 22:39:28 +0000390}
391
392void
393RibModule::formatRouteText(std::ostream& os, const RibEntry& entry, const Route& route,
394 bool includePrefix)
395{
396 text::ItemAttributes ia;
397
398 if (includePrefix) {
399 os << ia("prefix") << entry.getName();
400 }
401 os << ia("nexthop") << route.getFaceId();
Davide Pesavento22db5392017-04-14 00:56:43 -0400402 os << ia("origin") << route.getOrigin();
Junxiao Shi1d62e622017-03-08 22:39:28 +0000403 os << ia("cost") << route.getCost();
404 os << ia("flags") << static_cast<ndn::nfd::RouteFlags>(route.getFlags());
Junxiao Shi1d62e622017-03-08 22:39:28 +0000405 if (route.hasExpirationPeriod()) {
Eric Newberryde332452018-01-30 11:45:32 -0700406 os << ia("expires") << text::formatDuration<time::seconds>(route.getExpirationPeriod());
Junxiao Shi1d62e622017-03-08 22:39:28 +0000407 }
408 else {
409 os << ia("expires") << "never";
410 }
Junxiao Shi38f4ce92016-08-04 10:01:52 +0000411}
412
Junxiao Shi331ade72016-08-19 14:07:19 +0000413} // namespace nfdc
Junxiao Shi38f4ce92016-08-04 10:01:52 +0000414} // namespace tools
415} // namespace nfd