NFD: Named Data Networking Forwarding Daemon 24.07-28-gdcc0e6e0
Loading...
Searching...
No Matches
face-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 "face-module.hpp"
27#include "face-helpers.hpp"
28
29#include <ndn-cxx/mgmt/nfd/status-dataset.hpp>
30
31#include <boost/lexical_cast/try_lexical_convert.hpp>
32
33namespace nfd::tools::nfdc {
34
35void
37{
38 CommandDefinition defFaceList("face", "list");
39 defFaceList
40 .setTitle("print face list")
44 parser.addCommand(defFaceList, &FaceModule::list);
45 parser.addAlias("face", "list", "");
46
47 CommandDefinition defFaceShow("face", "show");
48 defFaceShow
49 .setTitle("show face information")
51 parser.addCommand(defFaceShow, &FaceModule::show);
52
53 CommandDefinition defFaceCreate("face", "create");
54 defFaceCreate
55 .setTitle("create a face")
61 .addArg("congestion-marking-interval", ArgValueType::UNSIGNED, Required::NO, Positional::NO)
62 .addArg("default-congestion-threshold", ArgValueType::UNSIGNED, Required::NO, Positional::NO)
64 parser.addCommand(defFaceCreate, &FaceModule::create);
65
66 CommandDefinition defFaceDestroy("face", "destroy");
67 defFaceDestroy
68 .setTitle("destroy a face")
70 parser.addCommand(defFaceDestroy, &FaceModule::destroy);
71}
72
73void
75{
76 auto remoteUri = ctx.args.getOptional<FaceUri>("remote");
77 auto localUri = ctx.args.getOptional<FaceUri>("local");
78 auto uriScheme = ctx.args.getOptional<std::string>("scheme");
79
80 FaceQueryFilter filter;
81 if (remoteUri) {
82 filter.setRemoteUri(remoteUri->toString());
83 }
84 if (localUri) {
85 filter.setLocalUri(localUri->toString());
86 }
87 if (uriScheme) {
88 filter.setUriScheme(*uriScheme);
89 }
90
91 FindFace findFace(ctx);
92 FindFace::Code res = findFace.execute(filter, true);
93
94 ctx.exitCode = static_cast<int>(res);
95 switch (res) {
97 for (const FaceStatus& item : findFace.getResults()) {
98 formatItemText(ctx.out, item, false);
99 ctx.out << '\n';
100 }
101 break;
105 ctx.err << findFace.getErrorReason() << '\n';
106 break;
107 default:
108 BOOST_ASSERT_MSG(false, "unexpected FindFace result");
109 break;
110 }
111}
112
113void
115{
116 uint64_t faceId = ctx.args.get<uint64_t>("id");
117
118 FindFace findFace(ctx);
119 FindFace::Code res = findFace.execute(faceId);
120
121 ctx.exitCode = static_cast<int>(res);
122 switch (res) {
124 formatItemText(ctx.out, findFace.getFaceStatus(), true);
125 break;
128 ctx.err << findFace.getErrorReason() << '\n';
129 break;
130 default:
131 BOOST_ASSERT_MSG(false, "unexpected FindFace result");
132 break;
133 }
134}
135
138static bool
139persistencyLessThan(FacePersistency x, FacePersistency y)
140{
141 switch (x) {
142 case FacePersistency::FACE_PERSISTENCY_NONE:
143 return y != FacePersistency::FACE_PERSISTENCY_NONE;
144 case FacePersistency::FACE_PERSISTENCY_ON_DEMAND:
145 return y == FacePersistency::FACE_PERSISTENCY_PERSISTENT ||
146 y == FacePersistency::FACE_PERSISTENCY_PERMANENT;
147 case FacePersistency::FACE_PERSISTENCY_PERSISTENT:
148 return y == FacePersistency::FACE_PERSISTENCY_PERMANENT;
149 case FacePersistency::FACE_PERSISTENCY_PERMANENT:
150 return false;
151 }
152 return static_cast<int>(x) < static_cast<int>(y);
153}
154
155void
157{
158 auto remoteUri = ctx.args.get<FaceUri>("remote");
159 auto localUri = ctx.args.getOptional<FaceUri>("local");
160 auto persistency = ctx.args.get<FacePersistency>("persistency", FacePersistency::FACE_PERSISTENCY_PERSISTENT);
161 auto lpReliability = ctx.args.getTribool("reliability");
162 auto congestionMarking = ctx.args.getTribool("congestion-marking");
163 auto baseCongestionMarkingIntervalMs = ctx.args.getOptional<uint64_t>("congestion-marking-interval");
164 auto defaultCongestionThreshold = ctx.args.getOptional<uint64_t>("default-congestion-threshold");
165 auto mtuArg = ctx.args.getOptional<std::string>("mtu");
166
167 // MTU is nominally a uint64_t, but can be the string value 'auto' to unset an override MTU
168 std::optional<uint64_t> mtu;
169 if (mtuArg == "auto") {
170 mtu = std::numeric_limits<uint64_t>::max();
171 }
172 else if (mtuArg) {
173 // boost::lexical_cast<uint64_t> will accept negative numbers
174 int64_t v = -1;
175 if (!boost::conversion::try_lexical_convert<int64_t>(*mtuArg, v) || v < 0) {
176 ctx.exitCode = 2;
177 ctx.err << "MTU must either be a non-negative integer or 'auto'\n";
178 return;
179 }
180
181 mtu = static_cast<uint64_t>(v);
182 }
183
184 std::optional<FaceUri> canonicalRemote;
185 std::optional<FaceUri> canonicalLocal;
186
187 auto updateFace = [&] (ControlParameters respParams, ControlParameters resp) {
188 // faces/update response does not have FaceUris, copy from faces/create response
189 resp.setLocalUri(respParams.getLocalUri())
190 .setUri(respParams.getUri());
191 printSuccess(ctx.out, "face-updated", resp);
192 };
193
194 auto handle409 = [&] (const ControlResponse& resp) {
195 ControlParameters respParams(resp.getBody());
196 if (respParams.getUri() != canonicalRemote->toString()) {
197 // we are conflicting with a different face, which is a general error
198 return false;
199 }
200
201 bool isChangingParams = false;
202 ControlParameters params;
203 params.setFaceId(respParams.getFaceId());
204
205 if (mtu && (!respParams.hasMtu() || respParams.getMtu() != *mtu)) {
206 isChangingParams = true;
207 params.setMtu(*mtu);
208 }
209
210 if (persistencyLessThan(respParams.getFacePersistency(), persistency)) {
211 isChangingParams = true;
212 params.setFacePersistency(persistency);
213 }
214
215 if (!boost::logic::indeterminate(lpReliability) &&
216 lpReliability != respParams.getFlagBit(ndn::nfd::BIT_LP_RELIABILITY_ENABLED)) {
217 isChangingParams = true;
218 params.setFlagBit(ndn::nfd::BIT_LP_RELIABILITY_ENABLED, bool(lpReliability));
219 }
220
221 if (!boost::logic::indeterminate(congestionMarking) &&
222 congestionMarking != respParams.getFlagBit(ndn::nfd::BIT_CONGESTION_MARKING_ENABLED)) {
223 isChangingParams = true;
224 params.setFlagBit(ndn::nfd::BIT_CONGESTION_MARKING_ENABLED, bool(congestionMarking));
225 }
226
227 if (baseCongestionMarkingIntervalMs) {
228 isChangingParams = true;
229 params.setBaseCongestionMarkingInterval(time::milliseconds(*baseCongestionMarkingIntervalMs));
230 }
231
232 if (defaultCongestionThreshold) {
233 isChangingParams = true;
234 params.setDefaultCongestionThreshold(*defaultCongestionThreshold);
235 }
236
237 if (isChangingParams) {
238 ctx.controller.start<ndn::nfd::FaceUpdateCommand>(
239 params,
240 [=, &updateFace] (const auto& cp) { updateFace(respParams, cp); },
241 ctx.makeCommandFailureHandler("updating face"),
242 ctx.makeCommandOptions());
243 }
244 else {
245 // don't do anything
246 printSuccess(ctx.out, "face-exists", respParams);
247 }
248
249 return true;
250 };
251
252 auto doCreateFace = [&] {
253 ControlParameters params;
254 params.setUri(canonicalRemote->toString());
255 if (canonicalLocal) {
256 params.setLocalUri(canonicalLocal->toString());
257 }
258 params.setFacePersistency(persistency);
259 if (!boost::logic::indeterminate(lpReliability)) {
260 params.setFlagBit(ndn::nfd::BIT_LP_RELIABILITY_ENABLED, bool(lpReliability));
261 }
262 if (!boost::logic::indeterminate(congestionMarking)) {
263 params.setFlagBit(ndn::nfd::BIT_CONGESTION_MARKING_ENABLED, bool(congestionMarking));
264 }
265 if (baseCongestionMarkingIntervalMs) {
266 params.setBaseCongestionMarkingInterval(time::milliseconds(*baseCongestionMarkingIntervalMs));
267 }
268 if (defaultCongestionThreshold) {
269 params.setDefaultCongestionThreshold(*defaultCongestionThreshold);
270 }
271 if (mtu) {
272 params.setMtu(*mtu);
273 }
274
275 ctx.controller.start<ndn::nfd::FaceCreateCommand>(
276 params,
277 [&] (const ControlParameters& resp) {
278 printSuccess(ctx.out, "face-created", resp);
279 },
280 [&] (const ControlResponse& resp) {
281 if (resp.getCode() == 409 && handle409(resp)) {
282 return;
283 }
284 ctx.makeCommandFailureHandler("creating face")(resp); // invoke general error handler
285 },
286 ctx.makeCommandOptions());
287 };
288
289 std::string error;
290 std::tie(canonicalRemote, error) = canonize(ctx, remoteUri);
291 if (canonicalRemote) {
292 // RemoteUri canonization successful
293 if (localUri) {
294 std::tie(canonicalLocal, error) = canonize(ctx, *localUri);
295 if (canonicalLocal) {
296 // LocalUri canonization successful
297 doCreateFace();
298 }
299 else {
300 // LocalUri canonization failure
301 auto canonizationError = canonizeErrorHelper(*localUri, error, "local FaceUri");
302 ctx.exitCode = static_cast<int>(canonizationError.first);
303 ctx.err << canonizationError.second << '\n';
304 }
305 }
306 else {
307 doCreateFace();
308 }
309 }
310 else {
311 // RemoteUri canonization failure
312 auto canonizationError = canonizeErrorHelper(remoteUri, error, "remote FaceUri");
313 ctx.exitCode = static_cast<int>(canonizationError.first);
314 ctx.err << canonizationError.second << '\n';
315 }
316
317 ctx.face.processEvents();
318}
319
320void
322{
323 FindFace findFace(ctx);
324 FindFace::Code res = findFace.execute(ctx.args.at("face"));
325
326 ctx.exitCode = static_cast<int>(res);
327 switch (res) {
329 break;
333 ctx.err << findFace.getErrorReason() << '\n';
334 return;
336 ctx.err << "Multiple faces match specified remote FaceUri. Re-run the command with a FaceId:";
338 ctx.err << '\n';
339 return;
340 default:
341 BOOST_ASSERT_MSG(false, "unexpected FindFace result");
342 return;
343 }
344
345 const FaceStatus& face = findFace.getFaceStatus();
346
347 ctx.controller.start<ndn::nfd::FaceDestroyCommand>(
348 ControlParameters().setFaceId(face.getFaceId()),
349 [&] (const ControlParameters& resp) {
350 // We can't use printSuccess because some face attributes come from FaceStatus not ControlResponse
351 ctx.out << "face-destroyed ";
353 ctx.out << ia("id") << face.getFaceId()
354 << ia("local") << face.getLocalUri()
355 << ia("remote") << face.getRemoteUri()
356 << ia("persistency") << face.getFacePersistency();
357 printFaceParams(ctx.out, ia, resp);
358 },
359 ctx.makeCommandFailureHandler("destroying face"),
360 ctx.makeCommandOptions());
361
362 ctx.face.processEvents();
363}
364
365void
366FaceModule::fetchStatus(ndn::nfd::Controller& controller,
367 const std::function<void()>& onSuccess,
368 const ndn::nfd::DatasetFailureCallback& onFailure,
369 const CommandOptions& options)
370{
371 controller.fetch<ndn::nfd::FaceDataset>(
372 [this, onSuccess] (const auto& result) {
373 m_status = result;
374 onSuccess();
375 },
376 onFailure, options);
377}
378
379void
380FaceModule::formatStatusXml(std::ostream& os) const
381{
382 os << "<faces>";
383 for (const FaceStatus& item : m_status) {
384 this->formatItemXml(os, item);
385 }
386 os << "</faces>";
387}
388
389void
390FaceModule::formatItemXml(std::ostream& os, const FaceStatus& item) const
391{
392 os << "<face>";
393
394 os << "<faceId>" << item.getFaceId() << "</faceId>";
395 os << "<remoteUri>" << xml::Text{item.getRemoteUri()} << "</remoteUri>";
396 os << "<localUri>" << xml::Text{item.getLocalUri()} << "</localUri>";
397
398 if (item.hasExpirationPeriod()) {
399 os << "<expirationPeriod>" << xml::formatDuration(item.getExpirationPeriod())
400 << "</expirationPeriod>";
401 }
402 os << "<faceScope>" << item.getFaceScope() << "</faceScope>";
403 os << "<facePersistency>" << item.getFacePersistency() << "</facePersistency>";
404 os << "<linkType>" << item.getLinkType() << "</linkType>";
405
406 if (!item.hasBaseCongestionMarkingInterval() && !item.hasDefaultCongestionThreshold()) {
407 os << "<congestion/>";
408 }
409 else {
410 os << "<congestion>";
411 if (item.hasBaseCongestionMarkingInterval()) {
412 os << "<baseMarkingInterval>" << xml::formatDuration(item.getBaseCongestionMarkingInterval())
413 << "</baseMarkingInterval>";
414 }
415 if (item.hasDefaultCongestionThreshold()) {
416 os << "<defaultThreshold>" << item.getDefaultCongestionThreshold() << "</defaultThreshold>";
417 }
418 os << "</congestion>";
419 }
420
421 if (item.hasMtu()) {
422 os << "<mtu>" << item.getMtu() << "</mtu>";
423 }
424
425 if (item.getFlags() == 0) {
426 os << "<flags/>";
427 }
428 else {
429 os << "<flags>";
430 os << xml::Flag{"localFieldsEnabled", item.getFlagBit(ndn::nfd::BIT_LOCAL_FIELDS_ENABLED)};
431 os << xml::Flag{"lpReliabilityEnabled", item.getFlagBit(ndn::nfd::BIT_LP_RELIABILITY_ENABLED)};
432 os << xml::Flag{"congestionMarkingEnabled", item.getFlagBit(ndn::nfd::BIT_CONGESTION_MARKING_ENABLED)};
433 os << "</flags>";
434 }
435
436 os << "<packetCounters>";
437 os << "<incomingPackets>"
438 << "<nInterests>" << item.getNInInterests() << "</nInterests>"
439 << "<nData>" << item.getNInData() << "</nData>"
440 << "<nNacks>" << item.getNInNacks() << "</nNacks>"
441 << "</incomingPackets>";
442 os << "<outgoingPackets>"
443 << "<nInterests>" << item.getNOutInterests() << "</nInterests>"
444 << "<nData>" << item.getNOutData() << "</nData>"
445 << "<nNacks>" << item.getNOutNacks() << "</nNacks>"
446 << "</outgoingPackets>";
447 os << "</packetCounters>";
448
449 os << "<byteCounters>";
450 os << "<incomingBytes>" << item.getNInBytes() << "</incomingBytes>";
451 os << "<outgoingBytes>" << item.getNOutBytes() << "</outgoingBytes>";
452 os << "</byteCounters>";
453
454 os << "</face>";
455}
456
457void
458FaceModule::formatStatusText(std::ostream& os) const
459{
460 os << "Faces:\n";
461 for (const FaceStatus& item : m_status) {
462 os << " ";
463 formatItemText(os, item, false);
464 os << '\n';
465 }
466}
467
468void
469FaceModule::formatItemText(std::ostream& os, const FaceStatus& item, bool wantMultiLine)
470{
471 text::ItemAttributes ia(wantMultiLine, 10);
472
473 os << ia("faceid") << item.getFaceId();
474 os << ia("remote") << item.getRemoteUri();
475 os << ia("local") << item.getLocalUri();
476
477 if (item.hasExpirationPeriod()) {
478 os << ia("expires") << text::formatDuration<time::seconds>(item.getExpirationPeriod());
479 }
480
481 if (item.hasBaseCongestionMarkingInterval() || item.hasDefaultCongestionThreshold()) {
482 os << ia("congestion") << "{";
483 text::Separator congestionSep("", " ");
484 if (item.hasBaseCongestionMarkingInterval()) {
485 os << congestionSep << "base-marking-interval="
486 << text::formatDuration<time::milliseconds>(item.getBaseCongestionMarkingInterval());
487 }
488 if (item.hasDefaultCongestionThreshold()) {
489 os << congestionSep << "default-threshold=" << item.getDefaultCongestionThreshold() << "B";
490 }
491 os << "}";
492 }
493
494 if (item.hasMtu()) {
495 os << ia("mtu") << item.getMtu();
496 }
497
498 os << ia("counters")
499 << "{in={"
500 << item.getNInInterests() << "i "
501 << item.getNInData() << "d "
502 << item.getNInNacks() << "n "
503 << item.getNInBytes() << "B} "
504 << "out={"
505 << item.getNOutInterests() << "i "
506 << item.getNOutData() << "d "
507 << item.getNOutNacks() << "n "
508 << item.getNOutBytes() << "B}}";
509
510 os << ia("flags") << '{';
511 text::Separator flagSep("", " ");
512 os << flagSep << item.getFaceScope();
513 os << flagSep << item.getFacePersistency();
514 os << flagSep << item.getLinkType();
515 if (item.getFlagBit(ndn::nfd::BIT_LOCAL_FIELDS_ENABLED)) {
516 os << flagSep << "local-fields";
517 }
518 if (item.getFlagBit(ndn::nfd::BIT_LP_RELIABILITY_ENABLED)) {
519 os << flagSep << "lp-reliability";
520 }
521 if (item.getFlagBit(ndn::nfd::BIT_CONGESTION_MARKING_ENABLED)) {
522 os << flagSep << "congestion-marking";
523 }
524 os << '}';
525
526 os << ia.end();
527}
528
529void
531 const std::string& actionSummary,
532 const ControlParameters& resp)
533{
535 os << actionSummary << ' '
536 << ia("id") << resp.getFaceId()
537 << ia("local") << resp.getLocalUri()
538 << ia("remote") << resp.getUri()
539 << ia("persistency") << resp.getFacePersistency();
540 printFaceParams(os, ia, resp);
541}
542
543void
544FaceModule::printFaceParams(std::ostream& os, text::ItemAttributes& ia, const ControlParameters& resp)
545{
546 os << ia("reliability") << text::OnOff{resp.getFlagBit(ndn::nfd::BIT_LP_RELIABILITY_ENABLED)}
547 << ia("congestion-marking") << text::OnOff{resp.getFlagBit(ndn::nfd::BIT_CONGESTION_MARKING_ENABLED)};
548 if (resp.hasBaseCongestionMarkingInterval()) {
549 os << ia("congestion-marking-interval")
550 << text::formatDuration<time::milliseconds>(resp.getBaseCongestionMarkingInterval());
551 }
552 if (resp.hasDefaultCongestionThreshold()) {
553 os << ia("default-congestion-threshold") << resp.getDefaultCongestionThreshold() << "B";
554 }
555 if (resp.hasMtu()) {
556 os << ia("mtu") << resp.getMtu();
557 }
558 os << '\n';
559}
560
561} // namespace nfd::tools::nfdc
T get(std::string_view key, const T &defaultValue=T()) const
std::optional< T > getOptional(std::string_view key) const
boost::logic::tribool getTribool(std::string_view key) const
Get an optional boolean argument as tribool.
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::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 printFaceParams(std::ostream &os, text::ItemAttributes &ia, const ControlParameters &resp)
Print face response parameters to specified ostream.
void formatItemXml(std::ostream &os, const FaceStatus &item) const
Format a single status item as XML.
void formatStatusText(std::ostream &os) const override
Format collected status as text.
static void destroy(ExecuteContext &ctx)
The 'face destroy' command.
static void registerCommands(CommandParser &parser)
Register 'face list', 'face show', 'face create', 'face destroy' commands.
void formatStatusXml(std::ostream &os) const override
Format collected status as XML.
void fetchStatus(ndn::nfd::Controller &controller, const std::function< void()> &onSuccess, const ndn::nfd::DatasetFailureCallback &onFailure, const CommandOptions &options) override
Collect status from NFD.
static void formatItemText(std::ostream &os, const FaceStatus &item, bool wantMultiLine)
Format a single status item as text.
static void printSuccess(std::ostream &os, const std::string &actionSummary, const ControlParameters &resp)
Print face action success message to specified ostream.
static void list(ExecuteContext &ctx)
The 'face list' command.
static void show(ExecuteContext &ctx)
The 'face show' command.
static void create(ExecuteContext &ctx)
The 'face create' command.
Procedure to find a face.
const FaceStatus & getFaceStatus() const
const std::string & getErrorReason() const
const std::vector< FaceStatus > & getResults() 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.
void printDisambiguation(std::ostream &os, DisambiguationStyle style) const
Print results for disambiguation.
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.
static bool persistencyLessThan(FacePersistency x, FacePersistency y)
Order persistency in NONE < ON_DEMAND < PERSISTENCY < PERMANENT.
@ YES
argument can be specified as positional
@ NO
argument must be named
@ FACE_ID_OR_URI
FaceId or FaceUri.
@ UNSIGNED
Non-negative integer.
@ FACE_PERSISTENCY
Face persistency 'persistent' or 'permanent'.
Print boolean as 'on' or 'off'.
Print true as an empty element and false as nothing.