Compare commits

...
150 Commits
Author SHA1 Message Date
Alexander Afanasyev 84dd4ca348 Prepare release 0.6.0
Change-Id: I0a80e05ddf5e3b98544bf56575e13efae22ccb5b
Refs: #4275
2017-10-15 17:47:06 -04:00
Davide Pesavento b8d1fc7562 tests: add support for link-local addresses in getTestIp
Change-Id: If57cb93f1c25a18b680aef12895ee5924ca90cfa
2017-10-09 16:09:55 -04:00
Davide Pesavento 6d1c1c320b tests: stop using deprecated Buffer::buf()
Change-Id: I48b7672098cfe57ccb470a247eed0c80e24cfffe
2017-10-09 15:53:37 -04:00
Davide Pesavento 4b89a6e943 face: move createChannel overloads that take strings to unit tests
They are used only there.

Change-Id: I0c70221bbcad36024223cc6e3c929b9c08b683c0
2017-10-07 15:29:50 -04:00
Yanbiao Li afb2059377 use ndn::ip::address(V6)FromString for scope-id aware conversion
Change-Id: Ifdc31771b8a990867c0a79ba42b71c87a97bcd56
Refs: #1428
2017-10-06 10:15:51 -07:00
Junxiao Shi 4e837f8d12 table: limit prefix length in StrategyChoice
refs #4262

Change-Id: Iddebeb555b2f3315515a298f8cded353fb51854d
2017-10-02 22:14:43 +00:00
Davide Pesavento b5eee2066b face: delete deprecated FaceSystem constructor
Change-Id: I54d623f1432428c2c3516a00f66dfe3f847bbc2c
Refs: #3377, #4021
2017-09-22 21:20:02 -04:00
Davide Pesavento 0804f5dd1f tests: stop using deprecated Block constructor
Change-Id: If50d5f0e2a4d6cef3a5e84e0f1da4a388b216233
2017-09-22 21:19:42 -04:00
Alexander Afanasyev 847de408cb build: Add warning for non-virtual destructor and fix discovered issues
Change-Id: I1d67b9b63d84d8524580e80ac06c3efef860c56a
2017-09-22 15:00:29 -04:00
Alexander Afanasyev b5220706c6 build: Add -isystem and set specialized flags as a last configuration step
This change prevents global flags from interacting with library/feature
detection scripts.

Change-Id: Id4d1d3f6cba6672ef04309b881bcff36a106f236
Refs: #4296
2017-09-22 15:00:03 -04:00
Alexander Afanasyev 38fb251c44 Update security section in sample config for V2 certificate
Change-Id: I50225d5019040a85e6df3584fb48b6b6386ed56d
Refs: #4276
2017-09-17 18:33:44 -07:00
Chavoosh Ghasemi 60b7d51063 docs: Fix reference to ValidatorConfig documentation
Change-Id: I43d96c64d031ad318333de8613e589633e58b52e
refs: #4184
2017-09-15 13:07:27 -04:00
Davide Pesavento 085b9e3a6f ci: add clang-5.0 build job to Travis-CI
Change-Id: Iea59e4f4687485dd4f076d41a4d6d6f1c8da7797
2017-09-15 12:53:12 -04:00
Junxiao Shi cfe81d5098 face: increase tolerance in WebSocketChannel test
Face/TestWebSocketChannel/SetPingInterval now allows 80ms
absolute tolerance, so that the test case is more stable on
slower machines.

refs #4244

Change-Id: I897f3b33a0ea2eb9006f0dd8d70ef2a4624953e2
2017-09-15 03:26:08 +00:00
Junxiao Shi 042a331582 table: attach PIT entry to NameTree node within depth limit
refs #4262

Change-Id: I64d76a337c3b491efa2f93c803046cea70c87000
2017-09-15 02:51:20 +00:00
Davide Pesavento ac238f29be tests+tools: fix build with clang-5.0
Change-Id: I75eeb01b071c1702517a65a8099e52d4092a4638
Refs: #4278
2017-09-12 15:22:02 -04:00
Junxiao Shi f940ae70f2 fw: use lp::isLessSevere from ndn-cxx
refs #4228

Change-Id: Ib430810ef356c309aa1f800f9b09c51d09dbc012
2017-09-11 03:24:29 +00:00
Junxiao Shi f54d0301c6 table: advisory limit of NameTree depth
refs #4262

Change-Id: I691f9615e2b8a3b146a683919c6d5833f53f6122
2017-09-07 18:16:38 +00:00
Nick Gordon 88043fcbe8 docs: clarify adding gerrit as remote repo
Change-Id: I92498afc362d254534ca03168fb8f0dca6a37f8e
refs: #3898
2017-09-07 08:49:03 -07:00
Junxiao Shi 06a1eab063 fw: Forwarder provides CS hit/miss counters
refs #4219

Change-Id: I7058c43bac4a5508d4b9907f6e684663b8953656
2017-09-04 13:13:02 +00:00
Junxiao Shi a907980840 tests: do not encode invalid SignatureInfo
refs #3200

Change-Id: I54199080439a41cf91c190e64a68540afabc7414
2017-08-27 11:38:56 -07:00
Davide Pesavento c25737cd9a ci+docs: remove all references to cryptopp/crypto++
Change-Id: I40fc72a8fa27c1a73c349e8cbaeb402a8b6af742
Refs: #3946
2017-08-26 16:39:56 -04:00
Eric Newberry 84d3adcd6c tools: add reliability option to nfdc face create
add LpReliability flag to FaceManager face status output

refs #4004

Change-Id: Ibcdfe7ff0fc9790cbcc4f2aa5e57e27b8a76023c
2017-08-26 12:39:10 -07:00
Davide Pesavento a997d29622 Fix compilation with Boost 1.65.0
Change-Id: Ib88b7e13bddb1728dd882c086e97edffdd696f0d
Refs: #4258
2017-08-25 23:30:24 -04:00
Md Ashiqur Rahman c8f17e51ed tools: Make ndn-autoconfig register / instead of /ndn
Hosts in consumer region should use / instead of /ndn as default route.

refs #4201

Change-Id: I6dd15c6657cfe9110c7a4dc3e9046370cbaf2733
2017-08-22 15:08:58 -07:00
Davide Pesavento c0a5a39a4f Add -Wextra-semi -Wundefined-func-template to the default CXXFLAGS
And fix the resulting compilation errors.

Change-Id: I2f8a8406542d297bfeeb4c922812ff1879a4dff6
Refs: #4248
2017-08-20 14:29:18 -04:00
Alexander Afanasyev 01bbd09ed8 tools: delete nfdId from 'nfdc status' output
refs #4089

Change-Id: I95f8a4346b2c4601acdc63daac20259b076a1d7e
2017-08-14 23:56:19 +00:00
Junxiao Shi 15902efdef tools: delete legacy nfdc and nfd-status invocations
The following legacy nfdc sub-commands are deleted:
- register
- unregister
- create
- destroy
- set-strategy
- unset-strategy
- add-nexthop
- remove nexthop

nfd-status no longer accepts command line arguments.

refs #4198

Change-Id: I3ec61fd24c09599c87f07bd92d8f0b5f4e7c70de
2017-08-11 22:58:35 +00:00
Davide Pesavento 00a3c9dc86 face: enable fragmentation and reassembly on unicast Ethernet faces
Change-Id: I81a065866610ec499a88b451bfaeea8ad40c17e2
Refs: #4011
2017-08-10 22:40:16 -04:00
Davide Pesavento 8ca138de13 ci: preserve PATH when running waf under sudo
Change-Id: Ideb6f9d4ec3d92af134b923b9abafc952ee868e6
Refs: #4186
2017-08-10 21:36:30 -04:00
Junxiao Shi a5765d647b core: delete NetworkInterfaceInfo class
nfd::NetworkInterfaceInfo has been replaced by
ndn::net::NetworkInterface.

refs #4021

Change-Id: Ic023b935f47f99f3bd74bbd4dc6c0e5bb2b3bf1c
2017-08-09 04:07:46 +00:00
Eric Newberry 2642cd250b mgmt: support LpReliability flag in faces/create and faces/update
refs #4003

Change-Id: Iddf94ea55c630b038187c2503783591b118230ec
2017-08-08 18:00:28 -07:00
Davide Pesavento b15276f90e tests: improve EthernetFactory test coverage
Also simplify other factories' GetChannels test case
by using checkChannelListEqual().

Change-Id: I02ecbd06fad498ea84b11d32b61ed4d63d929f2d
Refs: #4012
2017-08-08 20:32:22 -04:00
Davide Pesavento 6126547e53 face: EthernetFactory: skip non-Ethernet interfaces
Change-Id: Ifa42755b032cf23ea688e055477820d27e7dc8df
Refs: #4021
2017-08-08 15:48:10 -04:00
Davide Pesavento b8eebcb4aa face: EthernetTransport: use MTU provided by ndn::net::NetworkInterface
Change-Id: I949f054625ae6584d99505ea3fea2c4ab65b9cfb
Refs: #4021
2017-08-08 15:03:17 -04:00
Junxiao Shi 79a92089e7 face: use NetworkMonitor in EthernetFactory
EthernetFactory now creates unicast channels and multicast faces
on eligible netifs found by NetworkMonitor. However,
EthernetFactory does not yet react to fine-grained signals from
NetworkMonitor.

refs #4021

Change-Id: I58aac67d5a1b00d3cda76e78627b887f93ffa541
2017-08-08 02:40:59 +00:00
Nick Gordon 27b3854eb7 docs: write contributing guide and code of conduct
refs: #3898
Change-Id: Ife8936030527dba9020f0b298793c1121ae1fd6c
2017-08-07 12:37:05 -07:00
Junxiao Shi bbace1de47 face: use NetworkMonitor in UdpFactory
UdpFactory now creates multicast UDP faces on eligible netifs
found by NetworkMonitor. However, UdpFactory currently does not
react to fine-grained signals from NetworkMonitor.

refs #4021

Change-Id: I7802acb6f3aaa19db8f3c3141bbcdfc338e34eba
2017-08-06 20:03:37 +00:00
Junxiao Shi 4e1b6eee32 tests: avoid misaligned memory access in CS test
refs #4191

Change-Id: Idbc59c7d64805f6af239d66635821647956daf6d
2017-08-03 02:20:50 +00:00
Junxiao Shi 849e12a068 docs: corrections in Getting Started page
* Replace nfdc command lines with new syntax
* Remove mentioning of deprecated 'ndnd' and 'ndnd-tlv'
* Remove 'start nrd' step which has been removed
* Remove mentioning of ndn-cxx 'ndncatchunks3' and
  'ndnputchunks3' which have been moved into ndn-tools

refs #4194, #3570, #3700

Change-Id: Ie52c727400f7a1b4b076b3a94e48c72724139713
2017-08-02 00:14:57 +00:00
Ashlesh Gawande 90015991bc fw: per upstream retx exponential suppression for multicast strategy
refs: #4066

Change-Id: Ic1047b871dc9dc040e95ac5edceaae9994cd2849
2017-07-31 12:06:56 -05:00
Junxiao Shi bb6146ef74 tests: use ndn::util::Sha256 instead of ndn::crypto
refs #3940

Change-Id: I87c9cbfa9ae3592a053dd7eb0cb09d857668d111
2017-07-26 23:07:52 +00:00
Junxiao Shi 6028619f71 face: remove prohibited endpoint set
Prior to this commit, TcpFactory and UdpFactory each had a "prohibited
endpoint set" which prevents creating a face that connects to NFD
itself. This feature is no longer necessary after Nack-Duplicate has
been introduced, and is now removed.

refs #4189

Change-Id: I3b80ddc76ca1afb83b6f62061ccdbb69200e1841
2017-07-26 01:07:48 +00:00
Junxiao Shi fc2e13d58d fw: replace Link with forwarding hint
Forwarding now processes forwarding hint on Interests,
and no longer considers the Link object.

refs #4055

Change-Id: I0331687ee8ec31afa2f1a105e903670689647c4d
2017-07-25 02:08:48 +00:00
Eric Newberry 944f38bb49 face: move face parameters into CreateFaceParams struct
refs #4003

Change-Id: I6e1149e7075eb055912fd452a21b22fcb610829a
2017-07-23 20:37:06 -04:00
Junxiao Shi b347b7f189 tools: switch to v2::Validator in nfdc
"nfdid" field in 'nfdc status show' command is temporarily unavailable
because NfdIdCollector relies on v1 Validator. It will be re-implemented
in a future commit.

refs #4089

Change-Id: I21feae43055a17438d8bd3c0439ae4e0a2d5d14c
2017-07-23 14:01:58 +00:00
Davide Pesavento 6837d4f732 tests: cleanup FaceManager test cases
Change-Id: I1fd379fc1e01de331523260bd98ba96d6a7de88e
Refs: #3903
2017-07-18 00:47:11 -04:00
Junxiao Shi 0ba6d64e5a face: pass addFace and NetworkMonitor to ProtocolFactory constructor
refs #4021

Change-Id: I842515eb044bb1c655b347e6069e63c55c4e2d54
2017-07-17 00:53:22 +00:00
Junxiao Shi 2d49175d6e face: give ProtocolFactory access to NetworkMonitor
Subclasses of ProtocolFactory can access NetworkMonitor and
addFace callback anytime via member fields. They are supplied
to ProtocolFactory::create from FaceSystem.

This is in preparation for letting ProtocolFactory subclasses
react to fine-grained signals from NetworkMonitor.

refs #4021

Change-Id: I8da116bffc83d1bdeed7fd3b2e12c8872f19177b
2017-07-14 21:32:05 +00:00
Junxiao Shi 84d62cbbc6 face: use NetworkMonitor::listNetworkInterfaces() in tests
refs #4021

Change-Id: I6daccc3872a0c6b2e2e49390bc5881c796503926
2017-07-12 16:15:18 +00:00
Eric Newberry 7b0071e6a9 face: Acks acknowledge TxSequence instead of Sequence
refs #3931

Change-Id: I83919fe815c2a43e47eb09d754f77166c051d013
2017-07-09 07:31:51 -07:00
Junxiao Shi a5a3a87ae0 tools: simplify autoconfig MulticastDiscovery stage
refs #4158

Change-Id: Ib44804a0ac97b59d567fe8c0493c7d9ec4915137
2017-07-08 12:42:11 +00:00
Junxiao Shi cb76686ecc tools: refactor ndn-autoconfig
Hub face creation and prefix registrations are moved into a new
Procedure class. Main function is simplified.

refs #4158

Change-Id: I15b660e3b8a1bde89498a1cb549a87788de46c7a
2017-07-07 22:21:04 +00:00
Junxiao Shi 0d82d0459c face: construct EthernetTransport with NetworkInterface
EthernetTransport and EthernetChannel constructors now accept
ndn::net::NetworkInterface instead of NetworkInterfaceInfo,
in preparation for the transition to NetworkMonitor.

refs #4021

Change-Id: I1687a13dfaafde4ab3795a6f8c76c728c12929b9
2017-07-07 06:15:27 +00:00
Junxiao Shi 2760adcb80 core: NetworkInterfacePredicate accepts ndn::net::NetworkInterface
refs #4021

Change-Id: I6612a67db03b6651e8fbccad91f776ae502283d9
2017-07-06 05:44:52 +00:00
Junxiao Shi f748a4e9e2 tools: rename autoconfig Base to Stage
Also, turn BaseDns into non-member functions.

refs #4158

Change-Id: Id3ab862497a07cc261ed34b2ab116f4014a4c6fa
2017-07-05 23:41:48 +00:00
Davide Pesavento 84c65c0546 docs: update doxygen configuration
Most notably:
 - use relative paths in file list instead of file name only
 - enable sorting of brief descriptions of class members
 - show one enum value per line

refs #4163

Change-Id: Ib08601bc90f6eeb4e8754ed4335a447d5d0cc26c
2017-07-05 18:40:34 +00:00
Davide Pesavento 9ac95d3c1c ci: expand Travis-CI build matrix and use two parallel build jobs
Change-Id: I7dbba5bcd63274daa9b0da2ee9d631a55c1eca9e
2017-07-05 00:10:25 -04:00
Davide Pesavento 68ab43d8b7 face: don't set TransportState to FAILED if connection is closed cleanly
Change-Id: I0989cdf49558574f30b55a172d467b39683f9bb6
Refs: #4099
2017-07-05 00:07:12 -04:00
Davide Pesavento 0b0a71d801 face: rename hasBeenUsedRecently to hasRecentlyReceived
The word "used" is misleading, because the flag is set only when
packets are received, not for any other use of the transport.

Change-Id: I8a940d7887955c47cef085149629591b6e80c2a1
2017-07-05 00:06:54 -04:00
Davide Pesavento 1816d4b190 face: implement close-on-idle and persistency change for Ethernet unicast
Change-Id: I255024990a72b8cea5a44fa89f515ad91aadba66
Refs: #4011
2017-07-05 00:06:39 -04:00
Junxiao Shi 3b0815486e tools: modularize ndn-autoconfig-server
refs #4158

Change-Id: I90eb570e2ca2ae611d48aeb1e456712c1d148531
2017-07-04 18:37:34 +00:00
Junxiao Shi dbb6b3e119 mgmt: use v2 ValidationPolicy in CommandAuthenticator
refs #4089

Change-Id: I9a1cc77a960abe1e58a744f9d065634ab7597d15
2017-07-03 12:42:07 +00:00
Junxiao Shi 8a1f170ea6 tests: sign command Interests with CommandInterestSigner
Previously used ndn::v2::KeyChain::sign creates signed Interests, not
command Interests.

refs #4089

Change-Id: I7b13926d676ff81041f036eaec1978d82e8e1dfd
2017-07-03 00:05:08 +00:00
Junxiao Shi 83be1dae4b use FaceUri and NetworkMonitor from ndn-cxx/net
This commit also contains a quick fix in LpReliability to avoid
ndn::lp::Packet::wireEncode(EncodingEstimator&).

refs #3940, #4156

Change-Id: Ice8e3649be21dd023ed1f6562c185f9e6721e45e
2017-06-30 13:37:37 +00:00
Junxiao Shi 08e96316be tools: simplify nfd-autoreg with range-based for
refs #4158

Change-Id: Ibe83df03a1618b987608d539173ad32f03aafc42
2017-06-29 18:07:27 +00:00
Davide Pesavento 8eb5f8f3ed build: sync default-compiler-flags.py with ndn-cxx
- explicitly require clang >= 3.4
 - move -Og to basic debug flags (supported by clang since 4.0)
 - use gold linker and enable linker optimizations

Change-Id: Ife0b645a94abfe86230bac179779fde6b529f075
2017-06-24 02:24:42 -04:00
Davide Pesavento 46afec48ec face: unicast support in EthernetFactory
Change-Id: I1886a87d79a7194b3320a5417404b17a7290fa5d
Refs: #4012
2017-06-22 14:11:29 -04:00
Davide Pesavento 43ff2a9254 face: EthernetChannel
Change-Id: I8d9be571a2b0abe0cfad09756ea6c691e0679450
Refs: #4011
2017-06-22 14:11:08 -04:00
Davide Pesavento 7744a4a298 ci: speed up Travis-CI osx jobs by upgrading only the packages we need
Change-Id: I8bb4ddb5dbb335bf4f9ea925fe87bd7739adcb9d
2017-06-20 21:29:56 -04:00
Yanbiao Li f32ca897c5 rib: make log messages of AutoPrefixPropagator more accurate
Change-Id: Ieadfb95cd97b6f4ad0397d927ab31b21da3aa431
ref: #3774
2017-06-13 11:56:08 -07:00
Davide Pesavento 221f53bb6f ci: filter websocketpp files from lcov output
Change-Id: Ibd1ce39b0bf936e280e0277eaabf98cc68a5d656
2017-06-04 19:32:53 -04:00
Eric Newberry 9f3973e7b7 tests: increase timeout in Face/TestWebSocketChannel/MultipleAccepts
prevents test case failure on Ubuntu 17.04

refs #4002

Change-Id: I379499eaa90f12137b8c31b4a4fbe16392780a7d
2017-05-31 17:13:45 -07:00
Eric Newberry 7d8695d9e7 tests: check for new FaceUri canonization result string syntax
refs #4095

Change-Id: I068d140f50375541dee159b675b2ecfe69eb56a8
2017-05-30 23:28:47 +00:00
Junxiao Shi 3f21e58a58 rib: simplify Route class
Route::expires is changed to an optional, with nullopt representing
"never expires". This avoids an integer overflow.

RouteFlags accessors are now provided by ndn::nfd::RouteFlagsTraits.

Stream insertion operator is improved.

refs #3502

Change-Id: Ia912eab771fb00020385bf84d486955feae6aafa
2017-05-29 15:26:32 +00:00
Junxiao Shi 6eb0271fae face: decode CachePolicy without requiring allowLocalFields
refs #3966

Change-Id: I9a0f3c72682a6bc66f8852f115789a450a168223
2017-05-27 22:48:02 +00:00
Junxiao Shi 33a24e8e8f ci: fix ndn-cxx-latest version detection
refs #3321

Change-Id: I25ff57b634772fae324459751d6a7375346012ff
2017-05-27 17:09:31 +00:00
Junxiao Shi 16a3adfafc switch to ndn-cxx KeyChain v2
refs #4089

Change-Id: I32bc19db156de49275b681ef67f684b76631d50b
2017-05-26 17:38:51 +00:00
Eric Newberry f4056d0242 cs+fw: omitted FreshnessPeriod implies always stale
refs #3944

Change-Id: I018242b3ba5850e8a126f956af4150b512406efd
2017-05-26 17:31:53 +00:00
Qi Zhao 9ae8857bce tools: fix the inconsistent logging among ndn-autoconfig stages
refs #4079

Change-Id: Ie1edd8d30dbe7a9f5bf7b3364a07e71910cd91eb
2017-05-23 16:19:31 -07:00
Davide Pesavento a94f3676c3 face: MulticastEthernetTransport: remove promiscuous mode fallback
Change-Id: I040704577e66951ab5d1298ac4f99d98af11f809
Refs: #4081
2017-05-18 19:52:55 -04:00
Davide Pesavento fe0580cb55 face: introduce PcapHelper class
Change-Id: I26f7d43394e1b17f314c226ca6bce723c2410ae3
Refs: #4011
2017-05-18 18:17:54 -04:00
Ashlesh Gawande 6f6ef012cb ci: check boost version change on OSX
refs #3321

Change-Id: Iedccc7588ecaa95b34e6592aa0fa4f8a593233fd
2017-05-11 16:07:15 -05:00
Junxiao Shi 5c1bb36ae9 tests: include Flags in mocked faces/create response
refs #3956

Change-Id: I2e9af94bbb525739a31623da71325178191297d3
2017-05-11 16:03:38 +00:00
Davide Pesavento 8d61c037a7 ci: add generation of HTML coverage report using lcov
Copied from ndn-cxx

Change-Id: Ie8c08a570afe67c9eb232b37ad0038566ad49572
2017-05-03 01:14:32 -04:00
Junxiao Shi 1cce2a3014 mgmt: enable LocalUri in faces/create command response
Display LocalUri in 'nfdc face create' output.

refs #3956

Change-Id: I3c9ce266f218547bd6ba9509437178d271f680f7
2017-05-02 02:39:55 +00:00
Eric Newberry 73bcad33b9 core: make SizeCounter use pointer instead of reference
Restore virtual inheritance of GenericLinkServiceCounters in
GenericLinkService

refs #3941

Change-Id: I368e4694fee7bc97173fddfc81d0c679da0d6eed
2017-04-27 18:53:34 -07:00
Davide Pesavento 22db53973d rib+tools: use ndn::nfd::RouteOrigin instead of uint64_t
Change-Id: Ic8cbe95491a41e1d34b66d406da8637aeb5fd9e3
Refs: #3903
2017-04-14 20:48:20 -04:00
Junxiao Shi 38c7d9e6d7 tests: add bad FaceUri tests in FaceManager/CreateFace
refs #3377

Change-Id: I8be096c9bd25a80f063113f3d04f544ed184cc7d
2017-04-13 14:22:50 +00:00
Junxiao Shi 8eda682a18 tools: nfdc route commands accept and print RouteOrigin strings
refs #3987

Change-Id: I47594546e3fe9279fff5ea6ade9ce38cb3b05b6c
2017-04-12 02:53:14 +00:00
Davide Pesavento a3c9ddb21c face: allow the creation of permanent TCP faces
Change-Id: I516c176b000a78012f2ef7bd7f1f3e74c1758a94
Refs: #3167
2017-04-11 02:46:47 -04:00
Davide Pesavento acca18ae12 face: use std::array in UdpChannel
Change-Id: I9dc209ef471601f73ae85a36a317d7abf7c1849d
2017-04-09 13:23:16 -04:00
Davide Pesavento 77911ccc73 face: make logging more uniform across channel types
Change-Id: I21db45f736e07464260dcc902c673d098ba3c549
Refs: #2561
2017-04-09 13:11:16 -04:00
Davide Pesavento c19408ddf2 face: move isListening()/size() to Channel as pure virtual methods
As a side effect, this ensures that Channel cannot be instantiated.

Change-Id: I875876ddf8fc0fbb04f3e88726c708ece8106702
2017-04-09 02:57:19 -04:00
Davide Pesavento e5eebad6d2 face: remove nfd::ProtocolFactory type alias
Change-Id: Iac0af82a5301245e621a39f3bfc98aaed8dbaa45
2017-04-06 20:39:41 -04:00
Davide Pesavento 8fd15e628a face: move Channel and subclasses into nfd::face namespace
Change-Id: I163ef20e11b65d19d68585ec075fd3cd224a573b
2017-04-06 20:39:05 -04:00
Junxiao Shi b283f52c78 tools: nfdc strategy set/unset commands
nfdc set-strategy and nfdc unset-strategy are deprecated.

refs #3865

Change-Id: I934cbfcd567ac7ee33381ae4baf00b668977a0aa
2017-04-06 20:46:15 +00:00
Junxiao Shi f03d4796d7 mgmt+tools: rename 'datas' to 'data'
Plural form of 'data' is 'data'.

refs #3955

Change-Id: I62f5ebe53b8e36d565e5a243288eb12ae9a08014
2017-04-06 16:41:22 +00:00
Qi Zhao 6fc57a69fa tools: ndn-autoconfig continue proceeding with existing face
refs #4008

Change-Id: Ic264d1e3ba31cf0a743d6f190272e67f9c28e595
2017-04-05 16:53:10 +00:00
Junxiao Shi 5d3e481956 tools: nfdc strategy list/show commands
refs #3865

Change-Id: I6cc115b3c3f3d0840814a329c68356bbe6ad2e3f
2017-04-05 16:52:59 +00:00
Teng Liang f995f382ec fw: forward Interest/Data to ad hoc incoming face
Change-Id: Ia2eacf2a54d65ea4bffb607a709e1d1263547fd7
Refs: #3968
2017-04-04 22:09:39 +00:00
Ashlesh Gawande cad76b6242 fw: ASF should check if FaceInfo exists before using it
refs: #3968

Change-Id: I4c20a51609dd714e6104b103a4bef63ac8761083
2017-04-04 16:14:57 -05:00
Junxiao Shi 0d97692b40 tools: nfdc face create command accepts local FaceUri
refs #4017

Change-Id: I2d13403f13c30146c53744828ba2a37a6fab7de2
2017-04-01 14:35:21 +00:00
Eric Newberry 78e32b0ee2 face+mgmt: accept LocalUri in ProtocolFactory and FaceManager
refs #4016

Change-Id: I3e192e6d3982ae8e6ced1dbfbaa62f1c993f799e
2017-04-01 14:34:44 +00:00
Davide Pesavento 6bd6d0b50a face: UnicastEthernetTransport
Change-Id: I4d19f52835d9268f2ea63fd4e0b1a0a5aed95c47
Refs: #4011
2017-03-30 18:25:57 -04:00
Teng Liang bfea575095 face: configuration option to make Ethernet face "ad hoc"
Change-Id: I55aeb2ca38ec34365b2d5d951b9ca2d7b05f46b2
Refs: #4018, #3967
2017-03-29 04:51:10 +00:00
Teng Liang fe4fce3a3f face: configuration option to make UDP multicast face "ad hoc"
Change-Id: Ifd407f42e9646826697f73ab1890c819fce16857
Refs: #4018, #3967
2017-03-29 04:49:38 +00:00
Eric Newberry 185ab29325 face: add best-effort link-layer reliability
refs #3931

Change-Id: I009fabe000f4dd4ceb62acab6b0c735c13112430
2017-03-28 06:45:39 +00:00
susmit 21f22e692f core: enable wildcard matching on interface names for whitelist/blacklist
Change-Id: Iabf5084028d7e8c4a26ec5289c331f4a779c0bf7
Refs: #4009
2017-03-26 18:35:04 -04:00
Junxiao Shi da89fa5a9f fw: silence unused variable warning in FaceTable
Change-Id: I32136aabbac6c25f3b1e4ea17e2eeaddafd5c8c0
2017-03-22 21:34:56 +00:00
Davide Pesavento d2ea2350ad tests: no need to wrap ndn::nfd::RibEntry anymore
ndn-cxx provides the required operators now.

This commit also contains minor cleanups to RibManager test suite.

Change-Id: I2c7e4b5978319d654b69346935de37088bd6e877
Refs: #3903
2017-03-18 12:39:18 -04:00
Teng Liang 4f5cdcf421 fw: avoid breaking logic after LINK_TYPE_AD_HOC is introduced
Change-Id: I4f17a3a92c6ec44f1cb3ee619600c07e2ec39a16
Refs: #3967
2017-03-16 15:54:03 -07:00
Davide Pesavento d396b61ba1 rib: a few cleanups and simplifications in RibManager
Change-Id: I77699525ab372c4f4f3bbcae74e65bec24a86b4c
Refs: #3903
2017-03-12 19:53:54 -04:00
Davide Pesavento 1586aff97b mgmt+tests: no need to wrap ndn::nfd::FibEntry anymore
ndn-cxx provides the required operators now.

Change-Id: I7d7f371b89f8231853cb75682066ac09a858916d
Refs: #3903
2017-03-12 19:53:35 -04:00
Nick Gordon 9fcf123b19 rib: Enable Readvertise-to-NLSR
Change-Id: If50ffe740e8137ca2aeed67be219f6487153af9f
refs: #3818
2017-03-10 22:30:20 +00:00
Alexander Afanasyev 635bf20ef5 Remove use of deprecated code
Notably, faces/enable-local-control and faces/disable-local-control
management commands are removed in this commit.

Change-Id: I9ba45c9697eca418315c1c749a3c0c602a36f34e
Refs: #3988
2017-03-09 21:57:34 +00:00
Junxiao Shi f6af4f8913 tools: deprecate nfdc register/unregister/add-nexthop/remove-nexthop
They are replaced by 'nfdc route add' and 'nfdc route remove'.

refs #3866

Change-Id: I8040bf39ce9bd4b68409896c355beb12b0da5692
2017-03-09 16:20:14 +00:00
Junxiao Shi 1d62e62bc0 tools: nfdc route list and nfd route show commands
refs #3866

Change-Id: Ic8feab0ce9e5707c1cf382cdea7264e28e3edb30
2017-03-08 22:39:28 +00:00
Junxiao Shi 89c0ea0621 rib: introduce Readvertise
refs #3818

Change-Id: I4635fecfcb8be5d9bb3086dea3bf7bbd90538832
2017-03-06 19:52:05 +00:00
Nick Gordon 2fb3e8e3fe rib: introduce ReadvertisePolicy
refs #3818

Change-Id: Id40a7dfeeb1c46d6bff0750b15f3288a10897288
2017-03-06 19:50:59 +00:00
Junxiao Shi 63b67e2b23 rib: simplify ReadvertisedRoute
refs #3818

Change-Id: Ied644d825458f387b6f886bfcd425cffd2c2c7aa
2017-03-06 19:46:02 +00:00
Junxiao Shi 1a25a6edd0 tests: MockNfdMgmtFixture requires specific command Interest
MockNfdMgmtFixture previously allows a test case to respond to
the "last" command Interest, which causes difficulty when the
management client sends multiple command Interests together. It
now requires a specific command Interest for sending a response.

refs #3866

Change-Id: I80eeed2cdced618725a73f21bfaa387d831ca11a
2017-03-06 03:09:47 +00:00
Junxiao Shi 2fe3af0577 fw: duplicate Nonce from same face is not loop
refs #3888

Change-Id: I973f58281dca32b5f807d7be82a49d04314cbfb1
2017-03-04 17:24:19 +00:00
Ashlesh Gawande ecdbe5f2a6 fw: consumer retansmission in multicast startegy
refs: #2062

Change-Id: I2b58a289fffe262c69a797f339e40ad9037acf8f
2017-03-04 03:08:24 +00:00
Ashlesh Gawande f7bf4093be tools: fix compilation error due to ndn-cxx RouteFlags change
refs #3903

Change-Id: I799291d51d2b96ea1f7f8d07d000ae2c12300cf1
2017-03-04 03:06:26 +00:00
Junxiao Shi 084b79553f tools: nfdc route remove command
refs #3866

Change-Id: Iffbcf3face8758a52d35a854408f9910f4ba6c0b
2017-02-26 22:00:53 +00:00
Ashlesh Gawande e38e261ae6 fw: Nack in multicast strategy
refs: #3176

Change-Id: I1ef43c277a6a251170b652bf77d5516912a5d630
2017-02-25 07:23:41 +00:00
Junxiao Shi 918e5d451e tools: nfdc route add command
This commit also simplifies 'nfdc face destroy' and its test case.

'nfdc register' is deprecated in favor of 'nfdc route add'.

refs #3866

Change-Id: I1de5cc0bc956d57b0793da920c1e87b3580a3297
2017-02-25 03:58:21 +00:00
Davide Pesavento 7a294d49ab tests+tools: add missing includes
Change-Id: I29d9d265cb72c43edc4dbfd44261da823090bc10
2017-02-21 21:46:44 -05:00
Junxiao Shi d695801754 tools: deprecate nfdc create and nfdc destroy
They are deprecated in favor of nfdc face create and nfdc face destroy.

There is also a minor correction to nfdc-face man page.

refs #3864

Change-Id: I038bc604304ac5650101ee2afe30753e59061632
2017-02-20 03:34:48 +00:00
Davide Pesavento 26cbdbd904 tools: follow API change in ndn::nfd::Route
Change-Id: I67b770b4083afbea1fcf59fdcd183e971b1f6a6d
Refs: #3903
2017-02-19 21:48:21 -05:00
Davide Pesavento 6fd1e7e5ef fw: in FaceTable::add(), move the shared_ptr instead of copying it
Change-Id: Iddbdc35452f9512a75850b7a9cd3734d4cf89326
2017-02-17 18:45:27 +00:00
Junxiao Shi 36e5429622 tools: nfdc face list command with filters
refs #3864

Change-Id: Ie22e506bb2aeae8abf7c94b394e7aa56c840266f
2017-02-17 18:43:16 +00:00
Yanbiao Li 58ba3f9429 mgmt+tools: rework FacePersistency handling in faces/create and faces/update
1. enable changing persistency in faces/update command
2. reject faces/create request if the face already exists
3. nfdc changes to support the above

Change-Id: I659906da846608a42a768f08fb110ceee1a947a7
refs: #3232
2017-02-17 20:05:06 +08:00
Junxiao Shi 970f648843 tools: nfdc face create output fix
NFD faces/create command response does not contain a Uri field,
so nfdc face create output should not try to obtain canonical
remote FaceUri from the command response.

refs #3864

Change-Id: Idae4552e0e197257ea1277e12e20fe565a8562c6
2017-02-10 20:50:14 +00:00
Junxiao Shi 8f803f292e tools: use FindFace in nfdc face show command
This commit also uses FaceQueryFilter operator== in test cases.

refs #3864

Change-Id: I5096ab07ce02535364c5ac8f9e0f0ad27e55b96e
2017-02-10 03:04:28 +00:00
Junxiao Shi 05dd4441b0 tools: nfdc face destroy command
refs #3864

Change-Id: I1d070570c14364529c566273eba44b87413942b1
2017-02-06 22:50:07 +00:00
Junxiao Shi 1fbdb54f85 tests: improve Table/TestCs test suite
Adding MustBeFresh test case.
Updating DigestOrder test case.

refs #2411

Change-Id: Iae38e62e460251f3c74b67d0fcafae6007d8ddc5
2017-02-03 00:05:53 +00:00
Junxiao Shi f9858fdd1a fw: strip Link object when Interest reaches producer region
refs #3893

Change-Id: I7cb0a58f75eb4275bbf0fe0c7dcdb583c8519644
2017-02-02 06:07:52 +00:00
Junxiao Shi fab9e0de46 tests: forwarding with Link objects test suite
Fw/TestLinkForwarding is an end-to-end test suite that verifies
the proper forwarding rules for Interest with Link objects.
It replaces Fw/TestStrategy/LookupFib test suite.

refs #3893

Change-Id: I6dd3fe7988f8be6d55dd5dc589f7953c4d18f2ee
2017-02-02 06:04:59 +00:00
Junxiao Shi 1d7fef5810 tools: nfdc face create command
refs #3864

Change-Id: Icca589eceae0b78f68cda61e761dd4721ce54f9c
2017-02-02 05:33:14 +00:00
Junxiao Shi f0dfb33292 docs: write arguments as upper case in manpages
refs #3780, #3864

Change-Id: Iff0534dbd0119734fdb567500fe2e1842c7805f5
2017-02-02 05:32:25 +00:00
Nick Gordon 04262d9c84 rib: ReadvertiseDestination availability
refs: #3818

Change-Id: Ifc2875ba38025595f3d6a8f8207f5ec126f93c90
2017-02-01 12:14:21 -06:00
Junxiao Shi 056815ea8b tools: fix nfdc::text::ItemAttributes evaluation order bug
When multiple ItemAttributes::operator() invocations appear on the same
expression, they are not always evaluated left-to-right, causing incorrect
text output.

refs #3864

Change-Id: I4c99c5bc4a76b4becf83f368c9f253c0b254fc05
2017-01-29 16:39:19 +00:00
Junxiao Shi 215fd0b6d9 tools: import FacePersistency and FaceUri to nfdc namespace
refs #3864

Change-Id: I999fa2419f4d23368d924b536aee6f225017b570
2017-01-28 19:01:04 +00:00
Junxiao Shi 9954007722 fw: abstract Nack processing out of BestRouteStrategy2
refs #3176

Change-Id: Ib220269ff52acc47c65f854a610941afd862ad41
2017-01-27 19:57:33 +00:00
Junxiao Shi ea47bde02e mgmt: FaceManager no longer owns FaceSystem
refs #3904

Change-Id: I2ef6fd36a51493eea27d8eb0e1c7dc022766f948
2017-01-26 17:49:16 +00:00
Junxiao Shi c344304026 face: make ProtocolFactory::processConfig pure virtual
refs #3904

Change-Id: I1f2cb0a85b75f9adbabd4c9e5afb7d81567cfbd7
2017-01-26 17:21:35 +00:00
Junxiao Shi 1f481fa1a3 tools: nfdc face show command
refs #3864

Change-Id: Ibee7b082681edc35e8c272b2363436dbc7eaf9c0
2017-01-26 15:14:43 +00:00
Junxiao Shi 88a062de78 tools: add exit code and streams to nfdc::ExecuteContext
Moving exit code to the context allows error handling routines to
rely only on the context.

Moving stdout and stderr streams to the context allows mocking in
unit tests, and allows sending output to a buffer for pre-processing
when we implement interactive mode.

refs #3864

Change-Id: Ibf59c12405d0eaca0597835cb2e30125b7f70adb
2017-01-26 03:10:05 +00:00
335 changed files with 16770 additions and 9124 deletions

No files matched your search

+18 -5
View File
@@ -7,15 +7,28 @@ source "$JDIR"/util.sh
set -x
if has OSX $NODE_LABELS; then
FORMULAE=(boost openssl pkg-config)
brew update
brew upgrade
brew install boost pkg-config cryptopp openssl
if [[ -n $TRAVIS ]]; then
# travis images come with a large number of brew packages
# pre-installed, don't waste time upgrading all of them
for FORMULA in "${FORMULAE[@]}"; do
brew outdated $FORMULA || brew upgrade $FORMULA
done
else
brew upgrade
fi
brew install "${FORMULAE[@]}"
brew cleanup
fi
if has Ubuntu $NODE_LABELS; then
sudo apt-get -qq update
sudo apt-get -qq install build-essential pkg-config libboost-all-dev \
libcrypto++-dev libsqlite3-dev libssl-dev \
libpcap-dev
sudo apt-get -qy install build-essential pkg-config libboost-all-dev \
libsqlite3-dev libssl-dev libpcap-dev
if [[ $JOB_NAME == *"code-coverage" ]]; then
sudo apt-get -qy install lcov libgd-perl python-setuptools
sudo easy_install --upgrade gcovr
fi
fi
+22 -9
View File
@@ -8,13 +8,25 @@ set -x
pushd "${CACHE_DIR:-/tmp}" >/dev/null
INSTALLED_VERSION=$((cd ndn-cxx && git rev-parse HEAD) 2>/dev/null || echo NONE)
INSTALLED_VERSION=
if has OSX $NODE_LABELS; then
BOOST=$(brew ls --versions boost)
OLD_BOOST=$(cat boost.txt || :)
if [[ $OLD_BOOST != $BOOST ]]; then
echo "$BOOST" > boost.txt
INSTALLED_VERSION=NONE
fi
fi
if [[ -z $INSTALLED_VERSION ]]; then
INSTALLED_VERSION=$(git -C ndn-cxx rev-parse HEAD 2>/dev/null || echo NONE)
fi
sudo rm -Rf ndn-cxx-latest
git clone --depth 1 git://github.com/named-data/ndn-cxx ndn-cxx-latest
LATEST_VERSION=$((cd ndn-cxx-latest && git rev-parse HEAD) 2>/dev/null || echo UNKNOWN)
LATEST_VERSION=$(git -C ndn-cxx-latest rev-parse HEAD 2>/dev/null || echo UNKNOWN)
if [[ $INSTALLED_VERSION != $LATEST_VERSION ]]; then
sudo rm -Rf ndn-cxx
@@ -23,21 +35,22 @@ else
sudo rm -Rf ndn-cxx-latest
fi
sudo rm -Rf /usr/local/include/ndn-cxx
sudo rm -f /usr/local/bin/ndnsec*
sudo rm -fr /usr/local/include/ndn-cxx
sudo rm -f /usr/local/lib/libndn-cxx*
sudo rm -f /usr/local/lib/pkgconfig/libndn-cxx*
sudo rm -f /usr/local/lib/pkgconfig/libndn-cxx.pc
pushd ndn-cxx >/dev/null
./waf -j1 --color=yes configure --enable-shared --disable-static --without-osx-keychain
./waf -j1 --color=yes build
sudo ./waf -j1 --color=yes install
./waf configure --color=yes --enable-shared --disable-static --without-osx-keychain
./waf build --color=yes -j${WAF_JOBS:-1}
sudo env "PATH=$PATH" ./waf install --color=yes
popd >/dev/null
popd >/dev/null
if has Linux $NODE_LABELS; then
sudo ldconfig
elif has FreeBSD $NODE_LABELS; then
sudo ldconfig -a
elif has FreeBSD10 $NODE_LABELS; then
sudo ldconfig -m
fi
+11 -11
View File
@@ -11,34 +11,34 @@ git submodule sync
git submodule update
# Cleanup
sudo ./waf -j1 --color=yes distclean
sudo env "PATH=$PATH" ./waf --color=yes distclean
if [[ $JOB_NAME != *"code-coverage" && $JOB_NAME != *"limited-build" ]]; then
# Configure/build in optimized mode with tests
./waf -j1 --color=yes configure --with-tests
./waf -j1 --color=yes build
./waf --color=yes configure --with-tests
./waf --color=yes build -j${WAF_JOBS:-1}
# Cleanup
sudo ./waf -j1 --color=yes distclean
sudo env "PATH=$PATH" ./waf --color=yes distclean
# Configure/build in optimized mode without tests
./waf -j1 --color=yes configure
./waf -j1 --color=yes build
./waf --color=yes configure
./waf --color=yes build -j${WAF_JOBS:-1}
# Cleanup
sudo ./waf -j1 --color=yes distclean
sudo env "PATH=$PATH" ./waf --color=yes distclean
fi
# Configure/build in debug mode with tests and without precompiled headers
if [[ $JOB_NAME == *"code-coverage" ]]; then
COVERAGE="--with-coverage"
elif ! has OSX-10.9 $NODE_LABELS && ! has OSX-10.11 $NODE_LABELS; then
elif [[ -n $BUILD_WITH_ASAN || -z $TRAVIS ]]; then
ASAN="--with-sanitizer=address"
fi
./waf -j1 --color=yes configure --debug --with-tests --without-pch $COVERAGE $ASAN
./waf -j1 --color=yes build
./waf --color=yes configure --debug --with-tests --without-pch $COVERAGE $ASAN
./waf --color=yes build -j${WAF_JOBS:-1}
# (tests will be run against debug version)
# Install
sudo ./waf -j1 --color=yes install
sudo env "PATH=$PATH" ./waf --color=yes install
+1 -1
View File
@@ -49,6 +49,6 @@ export ASAN_OPTIONS
# Then use sudo to run those tests that need superuser powers
sudo -E ./build/unit-tests-core -t TestPrivilegeHelper $(ut_log_args core-privilege)
sudo -E ./build/unit-tests-daemon -t Face/TestEthernetTransport $(ut_log_args daemon-ethernet)
sudo -E ./build/unit-tests-daemon -t Face/*Ethernet* $(ut_log_args daemon-ethernet)
sudo -E ./build/unit-tests-daemon -t Face/*Factory/ProcessConfig $(ut_log_args daemon-face-config)
sudo -E ./build/unit-tests-daemon -t Mgmt/TestGeneralConfigSection/UserAndGroupConfig,NoUserConfig $(ut_log_args daemon-user-config)
+26
View File
@@ -12,4 +12,30 @@ if [[ $JOB_NAME == *"code-coverage" ]]; then
--exclude="$PWD/(tests|websocketpp)" \
--root=. \
--xml
# Generate also a detailed HTML output, but using lcov (better results)
lcov --quiet \
--capture \
--directory . \
--no-external \
--rc lcov_branch_coverage=1 \
--output-file build/coverage-with-tests-and-websocketpp.info
lcov --quiet \
--remove build/coverage-with-tests-and-websocketpp.info "$PWD/websocketpp/*" \
--rc lcov_branch_coverage=1 \
--output-file build/coverage-with-tests.info
lcov --quiet \
--remove build/coverage-with-tests.info "$PWD/tests/*" \
--rc lcov_branch_coverage=1 \
--output-file build/coverage.info
genhtml --branch-coverage \
--demangle-cpp \
--frames \
--legend \
--output-directory build/coverage \
--title "NFD unit tests" \
build/coverage.info
fi
+2
View File
@@ -32,3 +32,5 @@ Environment Variables Used in Build Scripts
- `CACHE_DIR`: the variable defines a path to folder containing cached files from previous builds,
e.g., a compiled version of ndn-cxx library. If not set, `/tmp` is used.
- `WAF_JOBS`: number of parallel build jobs used by waf, defaults to 1.
+134 -8
View File
@@ -3,26 +3,152 @@ language: generic
env:
global:
- JOB_NAME=limited-build
- WAF_JOBS=2
matrix:
include:
# Linux/gcc
- os: linux
dist: trusty
env:
- CXX=g++
- CXX=g++-4.8
- NODE_LABELS="Linux Ubuntu Ubuntu-14.04"
- BUILD_WITH_ASAN=yes
- os: linux
dist: trusty
addons:
apt:
sources: ubuntu-toolchain-r-test
packages: g++-4.9
env:
- CXX=g++-4.9
- NODE_LABELS="Linux Ubuntu Ubuntu-14.04"
- BUILD_WITH_ASAN=yes
- os: linux
dist: trusty
addons:
apt:
sources: ubuntu-toolchain-r-test
packages: g++-5
env:
- CXX=g++-5
- NODE_LABELS="Linux Ubuntu Ubuntu-14.04"
- BUILD_WITH_ASAN=yes
- os: linux
dist: trusty
addons:
apt:
sources: ubuntu-toolchain-r-test
packages: g++-6
env:
- CXX=g++-6
- NODE_LABELS="Linux Ubuntu Ubuntu-14.04"
- BUILD_WITH_ASAN=yes
- os: linux
dist: trusty
addons:
apt:
sources: ubuntu-toolchain-r-test
packages: g++-7
env:
- CXX=g++-7
- NODE_LABELS="Linux Ubuntu Ubuntu-14.04"
- BUILD_WITH_ASAN=yes
# Linux/clang
- os: linux
dist: trusty
addons:
apt:
packages: clang-3.5
env:
- CXX=clang++-3.5
- NODE_LABELS="Linux Ubuntu Ubuntu-14.04"
- BUILD_WITH_ASAN=yes
- os: linux
dist: trusty
addons:
apt:
packages: clang-3.6
env:
- CXX=clang++-3.6
- NODE_LABELS="Linux Ubuntu Ubuntu-14.04"
- BUILD_WITH_ASAN=yes
- os: linux
dist: trusty
addons:
apt:
sources:
- llvm-toolchain-precise-3.7
- ubuntu-toolchain-r-test
packages: clang-3.7
env:
- CXX=clang++-3.7
- NODE_LABELS="Linux Ubuntu Ubuntu-14.04"
- os: linux
dist: trusty
addons:
apt:
sources:
- llvm-toolchain-precise-3.8
- ubuntu-toolchain-r-test
packages: clang-3.8
env:
- CXX=clang++
- CXX=clang++-3.8
- NODE_LABELS="Linux Ubuntu Ubuntu-14.04"
- BUILD_WITH_ASAN=yes
- os: linux
dist: trusty
addons:
apt:
sources:
- llvm-toolchain-trusty-3.9
- ubuntu-toolchain-r-test
packages: clang-3.9
env:
- CXX=clang++-3.9
- NODE_LABELS="Linux Ubuntu Ubuntu-14.04"
- os: linux
dist: trusty
addons:
apt:
sources:
- llvm-toolchain-trusty-4.0
- ubuntu-toolchain-r-test
packages: clang-4.0
env:
- CXX=clang++-4.0
- NODE_LABELS="Linux Ubuntu Ubuntu-14.04"
- BUILD_WITH_ASAN=yes
- os: linux
dist: trusty
addons:
apt:
sources:
- llvm-toolchain-trusty-5.0
- ubuntu-toolchain-r-test
packages: clang-5.0
env:
- CXX=clang++-5.0
- NODE_LABELS="Linux Ubuntu Ubuntu-14.04"
- BUILD_WITH_ASAN=yes
# macOS/clang
# https://docs.travis-ci.com/user/osx-ci-environment/#OS-X-Version
- os: osx
osx_image: xcode8.2
osx_image: xcode7.3
env:
- NODE_LABELS="OSX OSX-10.11"
- BUILD_WITH_ASAN=yes
- os: osx
osx_image: xcode8.3
env:
- CXX=clang++
- NODE_LABELS="OSX OSX-10.12"
notifications:
email:
on_success: always
on_failure: always
- BUILD_WITH_ASAN=yes
before_script:
- ${CXX:-c++} --version
- python --version
script:
- ./.jenkins
+75 -44
View File
@@ -4,41 +4,63 @@ from waflib import Logs, Configure, Utils
def options(opt):
opt.add_option('--debug', '--with-debug', action='store_true', default=False, dest='debug',
help='''Compile in debugging mode without optimizations (-O0 or -Og)''')
help='''Compile in debugging mode with minimal optimizations (-O0 or -Og)''')
def configure(conf):
cxx = conf.env['CXX_NAME'] # CXX_NAME represents generic name of the compiler
if cxx == 'gcc':
flags = GccFlags()
elif cxx == 'clang':
flags = ClangFlags()
else:
flags = CompilerFlags()
Logs.warn('The code has not yet been tested with %s compiler' % cxx)
conf.start_msg('Checking C++ compiler version')
areCustomCxxflagsPresent = (len(conf.env.CXXFLAGS) > 0)
cxx = conf.env['CXX_NAME'] # CXX_NAME is the generic name of the compiler
ccver = tuple(int(i) for i in conf.env['CC_VERSION'])
errmsg = ''
warnmsg = ''
if cxx == 'gcc':
if ccver < (4, 8, 2):
errmsg = ('The version of gcc you are using is too old.\n'
'The minimum supported gcc version is 4.8.2.')
conf.flags = GccFlags()
elif cxx == 'clang':
if ccver < (3, 4, 0):
errmsg = ('The version of clang you are using is too old.\n'
'The minimum supported clang version is 3.4.0.')
conf.flags = ClangFlags()
else:
warnmsg = 'Note: %s compiler is unsupported' % cxx
conf.flags = CompilerFlags()
if errmsg:
conf.end_msg('.'.join(conf.env['CC_VERSION']), color='RED')
conf.fatal(errmsg)
elif warnmsg:
conf.end_msg('.'.join(conf.env['CC_VERSION']), color='YELLOW')
Logs.warn(warnmsg)
else:
conf.end_msg('.'.join(conf.env['CC_VERSION']))
conf.areCustomCxxflagsPresent = (len(conf.env.CXXFLAGS) > 0)
# General flags are always applied (e.g., selecting C++11 mode)
generalFlags = flags.getGeneralFlags(conf)
generalFlags = conf.flags.getGeneralFlags(conf)
conf.add_supported_cxxflags(generalFlags['CXXFLAGS'])
conf.add_supported_linkflags(generalFlags['LINKFLAGS'])
conf.env.DEFINES += generalFlags['DEFINES']
@Configure.conf
def check_compiler_flags(conf):
# Debug or optimized CXXFLAGS and LINKFLAGS are applied only if the
# corresponding environment variables are not set.
# DEFINES are always applied.
if conf.options.debug:
extraFlags = flags.getDebugFlags(conf)
if areCustomCxxflagsPresent:
extraFlags = conf.flags.getDebugFlags(conf)
if conf.areCustomCxxflagsPresent:
missingFlags = [x for x in extraFlags['CXXFLAGS'] if x not in conf.env.CXXFLAGS]
if len(missingFlags) > 0:
Logs.warn("Selected debug mode, but CXXFLAGS is set to a custom value '%s'"
% " ".join(conf.env.CXXFLAGS))
Logs.warn("Default flags '%s' are not activated" % " ".join(missingFlags))
else:
extraFlags = flags.getOptimizedFlags(conf)
extraFlags = conf.flags.getOptimizedFlags(conf)
if not areCustomCxxflagsPresent:
if not conf.areCustomCxxflagsPresent:
conf.add_supported_cxxflags(extraFlags['CXXFLAGS'])
conf.add_supported_linkflags(extraFlags['LINKFLAGS'])
@@ -55,9 +77,10 @@ def add_supported_cxxflags(self, cxxflags):
self.start_msg('Checking supported CXXFLAGS')
supportedFlags = []
for flag in cxxflags:
if self.check_cxx(cxxflags=['-Werror', flag], mandatory=False):
supportedFlags += [flag]
for flags in cxxflags:
flags = Utils.to_list(flags)
if self.check_cxx(cxxflags=['-Werror'] + flags, mandatory=False):
supportedFlags += flags
self.end_msg(' '.join(supportedFlags))
self.env.prepend_value('CXXFLAGS', supportedFlags)
@@ -73,9 +96,10 @@ def add_supported_linkflags(self, linkflags):
self.start_msg('Checking supported LINKFLAGS')
supportedFlags = []
for flag in linkflags:
if self.check_cxx(linkflags=['-Werror', flag], mandatory=False):
supportedFlags += [flag]
for flags in linkflags:
flags = Utils.to_list(flags)
if self.check_cxx(linkflags=['-Werror'] + flags, mandatory=False):
supportedFlags += flags
self.end_msg(' '.join(supportedFlags))
self.env.prepend_value('LINKFLAGS', supportedFlags)
@@ -98,18 +122,26 @@ class GccBasicFlags(CompilerFlags):
"""
This class defines basic flags that work for both gcc and clang compilers
"""
def getGeneralFlags(self, conf):
flags = super(GccBasicFlags, self).getGeneralFlags(conf)
flags['CXXFLAGS'] += ['-std=c++11']
return flags
def getDebugFlags(self, conf):
flags = super(GccBasicFlags, self).getDebugFlags(conf)
flags['CXXFLAGS'] += ['-O0',
'-Og', # gcc >= 4.8, clang >= 4.0
'-g3',
'-pedantic',
'-Wall',
'-Wextra',
'-Werror',
'-Wno-unused-parameter',
'-Wno-error=maybe-uninitialized', # Bug #1615
'-Wnon-virtual-dtor',
'-Wno-error=deprecated-declarations', # Bug #3795
'-Wno-error=maybe-uninitialized', # Bug #1615
'-Wno-unused-parameter',
]
flags['LINKFLAGS'] += ['-fuse-ld=gold', '-Wl,-O1']
return flags
def getOptimizedFlags(self, conf):
@@ -119,30 +151,19 @@ class GccBasicFlags(CompilerFlags):
'-pedantic',
'-Wall',
'-Wextra',
'-Wnon-virtual-dtor',
'-Wno-unused-parameter',
]
flags['LINKFLAGS'] += ['-fuse-ld=gold', '-Wl,-O1']
return flags
class GccFlags(GccBasicFlags):
def getGeneralFlags(self, conf):
flags = super(GccFlags, self).getGeneralFlags(conf)
version = tuple(int(i) for i in conf.env['CC_VERSION'])
if version < (4, 8, 2):
conf.fatal('The version of gcc you are using (%s) is too old.\n' %
'.'.join(conf.env['CC_VERSION']) +
'The minimum supported gcc version is 4.8.2.')
else:
flags['CXXFLAGS'] += ['-std=c++11']
return flags
def getDebugFlags(self, conf):
flags = super(GccFlags, self).getDebugFlags(conf)
version = tuple(int(i) for i in conf.env['CC_VERSION'])
if version < (5, 1, 0):
flags['CXXFLAGS'] += ['-Wno-missing-field-initializers']
flags['CXXFLAGS'] += ['-Og', # gcc >= 4.8
'-fdiagnostics-color', # gcc >= 4.9
]
flags['CXXFLAGS'] += ['-fdiagnostics-color'] # gcc >= 4.9
return flags
def getOptimizedFlags(self, conf):
@@ -156,26 +177,36 @@ class GccFlags(GccBasicFlags):
class ClangFlags(GccBasicFlags):
def getGeneralFlags(self, conf):
flags = super(ClangFlags, self).getGeneralFlags(conf)
flags['CXXFLAGS'] += ['-std=c++11']
if Utils.unversioned_sys_platform() == 'darwin':
flags['CXXFLAGS'] += ['-stdlib=libc++']
flags['LINKFLAGS'] += ['-stdlib=libc++']
version = tuple(int(i) for i in conf.env['CC_VERSION'])
if Utils.unversioned_sys_platform() == 'darwin' and version >= (9, 0, 0): # Bug #4296
flags['CXXFLAGS'] += [['-isystem', '/usr/local/include'], # for Homebrew
['-isystem', '/opt/local/include']] # for MacPorts
return flags
def getDebugFlags(self, conf):
flags = super(ClangFlags, self).getDebugFlags(conf)
flags['CXXFLAGS'] += ['-fcolor-diagnostics',
'-Wno-unused-local-typedef', # Bugs #2657 and #3209
'-Wno-error=unneeded-internal-declaration', # Bug #1588
'-Wextra-semi',
'-Wundefined-func-template',
'-Wno-error=deprecated-register',
'-Wno-error=keyword-macro', # Bug #3235
'-Wno-error=infinite-recursion', # Bug #3358
'-Wno-error=keyword-macro', # Bug #3235
'-Wno-error=unneeded-internal-declaration', # Bug #1588
'-Wno-unused-local-typedef', # Bugs #2657 and #3209
]
version = tuple(int(i) for i in conf.env['CC_VERSION'])
if version < (3, 9, 0) or (Utils.unversioned_sys_platform() == 'darwin' and version < (8, 1, 0)):
flags['CXXFLAGS'] += ['-Wno-unknown-pragmas']
return flags
def getOptimizedFlags(self, conf):
flags = super(ClangFlags, self).getOptimizedFlags(conf)
flags['CXXFLAGS'] += ['-fcolor-diagnostics',
'-Wextra-semi',
'-Wundefined-func-template',
'-Wno-unused-local-typedef', # Bugs #2657 and #3209
]
version = tuple(int(i) for i in conf.env['CC_VERSION'])
if version < (3, 9, 0) or (Utils.unversioned_sys_platform() == 'darwin' and version < (8, 1, 0)):
flags['CXXFLAGS'] += ['-Wno-unknown-pragmas']
return flags
+81
View File
@@ -0,0 +1,81 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our
project and our community a harassment-free experience for everyone,
regardless of age, body size, disability, ethnicity, gender identity
and expression, level of experience, nationality, personal appearance,
race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive
environment include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual
attention or advances
* Trolling, insulting/derogatory comments, and personal or political
attacks
* Public or private harassment
* Publishing others' private information, such as a physical or
electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in
a professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of
acceptable behavior and are expected to take appropriate and fair
corrective action in response to any instances of unacceptable
behavior.
Project maintainers have the right and responsibility to remove, edit,
or reject comments, commits, code, wiki edits, issues, and other
contributions that are not aligned to this Code of Conduct, or to ban
temporarily or permanently any contributor for other behaviors that
they deem inappropriate, threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public
spaces when an individual is representing the project or its
community. Examples of representing a project or community include
using an official project e-mail address, posting via an official
social media account, or acting as an appointed representative at an
online or offline event. Representation of a project may be further
defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior
may be reported by contacting the project leaders Lixia Zhang,
Beichuan Zhang, or Alex Afanasyev
at
[lixia@cs.ucla.edu](mailto:lixia@cs.ucla.edu),
[bzhang@cs.arizona.edu](mailto:bzhang@cs.arizona.edu),
and [aa@cs.ucla.edu](mailto:aa@cs.ucla.edu). All complaints will be
reviewed and investigated and will result in a response that is deemed
necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of
an incident. Further details of specific enforcement policies may be
posted separately.
Project maintainers who do not follow or enforce the Code of Conduct
in good faith may face temporary or permanent repercussions as
determined by other members of the project's leadership.
## Attribution
This Code of Conduct is adapted from
the [Contributor Covenant](http://contributor-covenant.org), version 1.4, available
at [http://contributor-covenant.org/version/1/4](http://contributor-covenant.org/version/1/4/)
+485
View File
@@ -0,0 +1,485 @@
# Contributing to the NDN Codebase
## Getting Started
The NDN team is very glad you are interested in contributing! The NDN
(Named Data Networking) codebase is composed of many projects, with
multiple universities across the world contributing. This
documentation will help you understand how we work and what we expect
from contributors.
### Code of Conduct
NDN codebase development adheres to the Contributor Covenant, located
in `CODE_OF_CONDUCT.md`. By contributing, you are expected to also
adhere to this code. If you feel someone has breached this code of
conduct,
please
[email us](mailto:lixia@cs.ucla.edu,bzhang@cs.arizona.edu,aa@cs.ucla.edu).
### What can I do?
The NDN codebase continually needs help with, among other
things:
1. Implementing new features
2. Writing fixes for bugs
3. Writing documentation for features that have recently changed or
that change quickly. Sometimes people forget to update
documentation!
4. Code review
## Finding Documentation
The NDN team maintains a community
([Redmine site](https://redmine.named-data.net/projects/nfd)) for
issue tracking and documentation hosting. Redmine is the hub for
design activity. In particular, all design discussion and decisions
will either occur or be copied there.
**Why did they do it *this* way?** When writing code, if you ever have
questions about why some design decision was made, the best approach
is to use `git blame` on a file to inspect the last commit for some
line of code. In this view there will be a commit hash in hex
(e.g. `0ab12e3a`) in the first column. Using our example, `git show
0ab12e3a` would show the commit made for that line. In the commit view
there should be a reference number (e.g. `refs: #1234`). Using this
number, you can find a pertaining issue on the Redmine
(e.g., `https://redmine.named-data.net/issues/1234`).
Additionally, in some cases there are published papers you can read in
order to gain a better understanding of the project. Searching for
these papers is not difficult; in many cases you can find a pertaining
paper or technical report listed on
[the main NDN website](https://named-data.net/publications).
Usually the title of a project will be a good keyword.
If you cannot find an answer to your question, the best place to go is
the
[mailing lists](https://named-data.net/codebase/platform/support/mailing-lists/).
There are multiple different lists for different interests, so be sure you
are mailing the right list for a quick response.
## Tracking work on Redmine ##
As mentioned above, Redmine is the organizational hub of the NDN
projects. As such, extensive use of it is made, and learning how it
works will help you. In particular, there is a workflow associated
with Redmine. Generally it is:
1. An issue is reported by someone. At this point they should
describe the issue, including relationships to other
issues. Unless they know what they're doing, they should leave
particulars blank. Such particulars are priority, target version,
assignees, and categories.
2. Design discussion about the issue occurs, and the issue is either
accepted or rejected.
3. Someone either is assigned to, or assigns themselves to work on the
issue, setting the status to "In progress."
**Note:** You must be a "Developer" of the project before you are
able to toggle the Status field. A user should contact a project
manager to be granted Developer privilege. Project managers can be
found on "Overview" page of the project.
4. After every useful chunk of progress, notes are made on the issue
and the progress percentage is updated to reflect this. Remember
that notes are actually Markdown, so it is advised to use the
'preview' button to ensure your note looks right.
5. The changes are uploaded to Gerrit for review. Once all changes
pertaining to an issue are on Gerrit, an issues status should
change to "Code review."
6. Once all changes are cleared for merging, they either can be
merged, or held for feedback, at which point the issue status
should become "Feedback."
7. After all changes have been merged, the issue should be set to
"Closed."
Redmine provides other facilities for managing your work on NDN
projects, too, such as time logging and time estimation.
## Contributing Guidelines
### Repositories ###
All NDN projects are hosted on GitHub, at various organizations, using git
for version control:
- [Main NDN codebase](https://github.com/named-data)
- [NDN codebase for mobile platforms](https://github.com/named-data-mobile)
- [NDN codebase for IoT](https://github.com/named-data-iot)
- [ndnSIM codebase](https://github.com/named-data-ndnSIM)
- [Other NDN codebase (REMAP)](https://github.com/remap)
If you are unfamiliar with git, some kind
of [tutorial on git](https://git-scm.com/book/en/v1/Getting-Started)
should be your first step.
There is also a [Git game](http://learngitbranching.js.org/index.html)
you can play. Finally, there is a
[wiki page](https://redmine.named-data.net/projects/nfd/wiki#Development-process)
on NFD's Redmine that has more useful links.
There are a lot of different projects, so take some time to look
through them for ones that pertain to your interests. NDN projects
use [Gerrit](https://gerrit.named-data.net/) for code review purposes.
Occasionally there will be other repositories used privately to test
changes, but this is uncommon. In general, you will not need to refer
to these repositories.
### Style ###
Most NDN projects are written in C++, and there is a
[style guide](https://named-data.net/doc/ndn-cxx/current/code-style.html).
In general the NDN style is similar to the GNU style, but there are
some significant changes. This style guide is not exhaustive, and in
all cases not covered by the guide you should emulate the current
style of the project.
There is a
[partial styleguide](https://redmine.named-data.net/projects/nfd/wiki/CodeStyle)
for Python. It applies many of the provisions from the C++ guide, in
addition to some other Python-specific things.
Some NDN projects are writtin in JavaScript, and there is a [style guide](https://named-data.net/codebase/platform/documentation/ndn-platform-development-guidelines/cpp-code-guidelines/) for that language, too.
#### Commit Messages ####
Commit messages are very important, as they are usually the first
thing (besides the changelog file) a developer looks at when trying to
understand the timeline of a project. Commit messages should:
- Have a short, descriptive first line, starting with the module
they change. A good rule of thumb is a maximum length of 65
characters.
- If including a body, leave a blank line between the first line and
the rest, and ensure that it's no longer than 72 characters.
- Include Redmine issue numbers. The exact syntax is given below.
- Be written in an imperative mood. E.g. `"Make foo a bar"` and not
`"Foo is now a bar"`
- Use the present tense. E.g. `"Make foo a bar"` and not `"Made foo a
bar"`
To explain, the anatomy of a typical commit message is like this:
docs: write contributing guide and code of conduct
refs: #3898
Change-Id: Ife89360305027dba9020f0b298793c1121ae1fd6c
Explaining this:
- `docs` is the module that the commit affects. We want this because
it lets someone know at a glance what part of the project it
changes. For some projects, there will be only one module or only
very small other modules. This practice should be observed in
those cases, too.
- `#3898` is the Redmine issue number. Gerrit transforms these into
clickable links, and it is useful to reviewers to gain background
understanding of the issue. You can have multiple by separating
them with commas.
- `Change-Id` should be filled automatically. It is used by Gerrit
to track changes.
### Unit Tests ###
With a few exceptions, every patch needs to have unit tests that
accompany them. For C++, we use
the [Boost unit test framework](http://www.boost.org/libs/test) to
help us out. Note that this link points to the newest version of the
Boost Test library documentation, and you may need to refer to older
documentation if you are using an older version of Boost. More
information on this is available in
the
[NFD Dev Guide](https://named-data.net/publications/techreports/ndn-0021-7-nfd-developer-guide/)
When designing and writing tests, a few things need to be kept in mind:
1. Unit tests are **design** tools, *not* debug tools. Just because
your code passes some unit tests does not mean it is
bug-free. Unit tests are tools to convince you that your code does
what you think it does.
2. It can be difficult to know when you should test something. If
you find that you are having a hard time designing a test for
something, ask yourself whether it is because it doesn't make
sense to test what you've just written, or if it's because the way
you designed it makes it difficult to test. Consider a second look
at your design if you think it's the second one.
3. The method that gave the world unit tests says that unit tests
should be written *before* the code they test. This is
a contentious issue, so writing them either before or after is
acceptable. If writing them later, keep the tests in mind when
writing the code, and it will help design good interfaces.
4. At the very least, write the test for a completed module/unit of
code before moving on. Doing this will ensure that the test gets
written, and will help you think about the interface that you need
to examine. If you don't, you may forget exactly what each piece
is responsible for when you're looking at the whole system
afterward.
5. Separate your I/O, as it is very hard to test, so I/O should be
isolated if possible. Consider something like separating a
function that invokes I/O into two functions: one that does the
I/O and calls the other one, which takes that I/O result. This
will make it easier to test that second function than if they were
combined.
Writing unit tests using the Boost framework is quite simple, and you
can refer to existing unit tests for examples.
([This is a good example.](https://github.com/named-data/NFD/blob/master/tests/rib/rib.t.cpp))
#### Building and running unit tests ####
This is mentioned in greater depth in
the [developer's readme](README-dev.md), but the basic procedure is:
1. In the base directory of the project, run `./waf distclean`. This
removes all prior build files.
2. Run `./waf configure --with-tests`. This configures the next build
to also build the tests.
3. Run `./waf`. This will build the tests.
4. Then, the unit tests will be in the `build` directory, and will be
named `unit-tests-<module name`, e.g. `unit-tests-nlsr` or
`unit-tests-rib`. To run just one test suite, run
`./unit-tests-exampleModule -t ExampleTestSuite`. To run a
specific test in a suite, use `./unit-tests-exampleModule -t
ExampleTestSuite/ExampleTestCase`.
## Gerrit: Uploading Patches and Code Review ##
As mentioned above, NDN projects
use [Gerrit](https://gerrit.named-data.net/) for code review. This is
a web-hosted, open code review platform that allows for interactive
code review, rebasing, and cross-linking with the Redmine, and a
developer interacts with it using the familiar git. For issues that
require a tracker reference, a particularly useful feature is that
the `refs: #...` becomes a clickable link to the issue for design
discussion.
### First-time Setup ###
The first-time Gerrit setup goes like this:
1. Log in to Gerrit. You can authenticate using many different
methods, including GitHub OAuth. You need to ensure that you have a Gerrit username:
1. Log in.
2. Click on your name in the top right corner and click "Settings" in the pop-up box.
3. In the `Username` box, check for a name. If there's already one there, great.
4. If not, type one in, and this will be used in later steps.
2. Set up your Gerrit credentials. This will depend on how you
configured your Gerrit remote in step 1. Among other things, you
need to set up identities so that the email on your Gerrit profile
matches whatever email you will be committing with on your git
repo.
**Note:** We only support using
[SSH access to Gerrit](https://gerrit.named-data.net/Documentation/user-upload.html#ssh).
This shows what the identities panel looks like. If you do
not see the email here that you have configured git to use, you
cannot upload to Gerrit.
![Web page showing what the "identities" panel looks like](./docs/images/gerrit-credentials-blurred.png)
In that case, add it under contact information panel.
![Web page showing what the "contact information" panel looks like](./docs/images/gerrit-email.png).
Gerrit itself has
[extensive documentation](https://gerrit.named-data.net/Documentation/error-messages.html)
regarding error messages, and this identity-based one is by far the most
common. This is the
[documentation for an identity error.](https://gerrit.named-data.net/Documentation/error-invalid-author.html)
3. Clone the source for a project from Gerrit.
1. While logged in to Gerrit, there will be a link in the top-left
area, `Projects`.
2. From this project view, click on the `Clone with commit-msg
hook` tab.
3. Click on the `ssh` tab to select cloning over SSH. **Note:**
This will require setting up SSH access to Gerrit first.
4. Copy-and-paste the `git clone ...` command into your terminal
to fetch the project source.
After this, your remotes will be set up correctly, and pushing to
Gerrit will only require `git push origin HEAD:refs/for/master` after
committing work. This section has some
[git tips and tricks to type less][type-less].
### Uploading patches ###
Most patches should have a corresponding Redmine issue that they can
reference. If you search the Redmine and notice there is no relevant
issue for a patch you are writing, please create an issue first. You
will need a Redmine account, which can be created there.
After writing some changes, commit them locally as normal. After
saving and exiting your editor, the commit hook will insert a unique
`Change-Id` to the message.
Once you have a commit message you are happy with, simply run `git
push HEAD:refs/for/master`.
**Note:** Gerrit separates commits into patch sets by the unique
`Change-Id`s. As a result, it is important that you either:
* Squash your various commits into one with `git rebase -i
<initial commit>`, ensuring that the ultimate `Change-Id` in the
commit is the one on the patch set on Gerrit. This workflow is
generally preferred.
* Amend your commit with any new changes using `git commit --amend`.
If you do not do this, what will happen is that each commit will be
interpreted by Gerrit as a separate patch set. This is probably not
what you want.
### Code Review ###
#### Dealing with Code Review ####
It is important to remember that code review is about improving
the quality of code contributed, and nothing else. Further, code
review is highly important, as every line of code that's
committed comes with a burden of maintenance. Code review helps
minimize the burden of that maintenance. Consider that when you
are receiving comments, those comments are influenced by two
things:
- How the reviewer personally feels about the change ("Would I
be happy to see this code in a year?" or "Would I be happy if
I wrote this?") and
- How the reviewer feels the change abides by style and usage
requirements ("I may not personally mind, but we have to do it
this way.")
Remembering these things when reading comments helps to separate
what can feel like needless negativity.
#### Doing Code Review and Writing Comments ####
**Code review is *extremely* important!** We need every bit of code
review you can give. In many cases this is the bottleneck for
new contributors who do not fully understand how certain
language constructs should be used, what best practices are,
etc. In these cases it is important to give constructive
feedback.
Writing comments is somewhat counter-intuitive on Gerrit. If you are
signed in through a modern browser, you can leave a comment in a file
either by clicking on the line number, by selecting some text you want
to comment and pressing 'c', or by clicking on someone else's
comment. After typing, click the `Save` button there. After navigating
through the patch set with the arrows at the top-right of the Gerrit
UI, you then must click the up-arrow to get back to the Change
screen. **At this point your comments have *not* been made yet!** You
must then click the `Reply...` button, and assign a score. If you
correctly saved your comments, they will be shown at the bottom of
that box. Once you click post, the comments will be made public to
others.
Minimally, a review *must* include:
- A score (usually -1, 0, or +1)
- An "itemized" commentary on each objection you have, or a
justification for a whole-change objection.
Optimally, a review should include:
- A useful explanation of *why* you object to some item. This is
more important than it first appears. Consider that although
you may have been writing code since before you could count,
not everyone has. Some things may not be obvious to others,
and sharing your knowledge is key to making an open-source
project work.
- Comments about good design decisions. These help motivate
developers when otherwise a review is entirely negative
comments.
#### Expediting the Code Review Process ####
If you observe that code review takes a long time, there are a
few things that you can do to to expedite the process:
1. **Think about language best practices.**
2. **Ask yourself how you would feel about this code, if you saw it "in the wild."**
3. **When making code decisions, ask yourself "why *shouldn't* I do it this way?"**
#### Responding to Code Review ####
There are a few things to remember when responding to code review, including:
- **Don't** click "Done" on a comment if you agree with a comment and
are updating the code accordingly. These comments are essentially
useless and only clutter the discussion. Gerrit has a convenient
mechanism to check the differences between two patch sets, so
reviewers are expected to check that their comments have been
addressed.
- **Don't** blindly follow what is suggested. Review is just that: a
review. Sometimes what is suggested is functionally equivalent to
what you have implemented. Other times, the reviewer has not
considered something when writing their review, and their
suggestion will not work. Lastly, if you disagree with a suggestion
for some other reason, feel free to object, but you may be asked to
provide an explanation.
- **How** to reply. To reply to a comment, click on the comment in
the Gerrit interface, type your reply, and then press save. After
replying, you **must remember** to go to the main patch set change
screen, and press `Reply...`. Your comments will be shown as
drafts, and you must click `Post` to make them visible to others.
### Jenkins ###
As a supplement to the code review process, every patch set is automatically
compiled and tested on multiple platforms
using [our instance of Jenkins](http://jenkins.named-data.net/), the
continuous integration system. Interacting with Jenkins is not usually
necessary, as Jenkins automatically picks up new patch sets and posts
the results. Typically the only interaction needed with Jenkins is
when some kind of glitch occurs and a build needs to be retriggered.
It is expected that code is checked locally. Reviewers may wait until
Jenkins checks the code before doing a more functional review of the
code. Since Jenkins checks can take a while, you can save some time by
checking yourself first.
### Gerrit Change-Id Commit Hook ###
If you encounter an error like this when trying to push work:
remote: ERROR: [4311462] missing Change-Id in commit message footer
remote:
remote: Hint: To automatically insert Change-Id, install the hook:
remote: gitdir=$(git rev-parse --git-dir); scp -p -P 29418 someuser@gerrit.named-data.net:hooks/commit-msg ${gitdir}/hooks/
remote: And then amend the commit:
remote: git commit --amend
Then your commit hook has not been set up correctly, somehow. Follow
the instructions in the terminal to resolve this.
### Code Style Robot ###
As part of CI, there is a script that checks every patch uploaded to
Gerrit for code style consistency. Checking these kinds of things is
notoriously difficult, and while the robot is very good in general, it
will occasionally report false positives and miss things. So all
issues it raises should be inspected manually to ensure they really
are style problems. Further, since this automated check is not
perfect, you should take steps (e.g., configure your editor or run a
linter before you commit) to ensure you adhere to code style rules.
### <a name="type-less"></a> Typing Less to Upload Patches to Gerrit ###
If typing `HEAD:refs/for/master` feels
repetitive, you have a few options:
* You can configure git to do the work for you, as described
in [this blog post](https://yoursunny.com/t/2017/NFD-devbox/#Uploading-to-Gerrit)
* Git has an alias system that you can use to specify certain
commands. For example, you can use `git config --global alias.pg
"push origin HEAD:refs/for/master"` to make `git pg` push to Gerrit. You
can [learn about git aliases.](https://githowto.com/aliases)
* If using Linux or macOS, it is relatively simple to create a shell
alias: `alias gs="git status"` for example, or `alias gitpush="git
push origin HEAD:refs/for/master`. The alias itself must be one
word, but it can represent multiple words. To persist your
aliases, they need to go in your environment file, which is
generally at `~/.bashrc` or `~/.zshrc` or `~/.profile`. The exact
syntax of aliases and the environment variables file will vary by
shell. You should be careful not to overuse aliases, however.
[type-less]: #type-less
+2 -5
View File
@@ -8,19 +8,16 @@ NFD relies on third-party software and libraries, licensed under the following l
- Boost libraries licensed under conditions of
[Boost Software License 1.0](http://www.boost.org/users/license.html)
- CryptoPP library is licensed under conditions of
[Boost Software License 1.0](http://www.boost.org/users/license.html)
- SQLite is in [public domain](http://www.sqlite.org/copyright.html)
- WebSocket++ library licensed under conditions of
[BSD license](https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/COPYING)
[BSD license](https://github.com/zaphoyd/websocketpp/blob/0.7.0/COPYING)
- Libpcap library licensed under conditions of
[a 3-clause BSD license](http://www.tcpdump.org/license.html).
- waf build system is licensed under conditions of
[BSD license](https://github.com/named-data/ndn-cxx/blob/master/waf)
[BSD license](https://github.com/named-data/NFD/blob/master/waf)
The GPL license is provided below in this file. For more information about these licenses, see
http://www.gnu.org/licenses/
+1 -1
View File
@@ -11,7 +11,7 @@ files:
Include the following license boilerplate into all `.hpp` and `.cpp` files:
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
/*
* Copyright (c) [Year(s)], [Copyright Holder(s)].
*
* This file is part of NFD (Named Data Networking Forwarding Daemon).
+13 -9
View File
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2014-2016, Regents of the University of California,
/*
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -56,13 +56,16 @@
#include <utility>
#include <vector>
#include <ndn-cxx/interest.hpp>
#include <ndn-cxx/data.hpp>
#include <ndn-cxx/delegation.hpp>
#include <ndn-cxx/delegation-list.hpp>
#include <ndn-cxx/exclude.hpp>
#include <ndn-cxx/interest.hpp>
#include <ndn-cxx/name.hpp>
#include <ndn-cxx/encoding/block.hpp>
#include <ndn-cxx/lp/nack.hpp>
#include <ndn-cxx/net/face-uri.hpp>
#include <ndn-cxx/util/backports.hpp>
#include <ndn-cxx/util/face-uri.hpp>
#include <ndn-cxx/util/signal.hpp>
#include <boost/asio.hpp>
@@ -96,14 +99,15 @@ using std::cref;
using ndn::to_string;
using ndn::Interest;
using ndn::Block;
using ndn::Data;
using ndn::Delegation;
using ndn::DelegationList;
using ndn::Exclude;
using ndn::FaceUri;
using ndn::Interest;
using ndn::Name;
using ndn::PartialName;
using ndn::Exclude;
using ndn::Link;
using ndn::Block;
using ndn::util::FaceUri;
namespace tlv {
// Don't write "namespace tlv = ndn::tlv", because NFD can add other members into this namespace.
+9 -18
View File
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2014-2016, Regents of the University of California,
/*
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -24,10 +24,10 @@
*/
#include "config-file.hpp"
#include "logger.hpp"
#include <boost/property_tree/info_parser.hpp>
#include <fstream>
#include <sstream>
namespace nfd {
@@ -42,11 +42,8 @@ ConfigFile::throwErrorOnUnknownSection(const std::string& filename,
const ConfigSection& section,
bool isDryRun)
{
std::string msg = "Error processing configuration file ";
msg += filename;
msg += ": no module subscribed for section \"" + sectionName + "\"";
BOOST_THROW_EXCEPTION(ConfigFile::Error(msg));
BOOST_THROW_EXCEPTION(Error("Error processing configuration file " + filename +
": no module subscribed for section \"" + sectionName + "\""));
}
void
@@ -87,7 +84,7 @@ ConfigFile::parse(const std::string& filename, bool isDryRun)
{
std::ifstream inputFile(filename);
if (!inputFile.good() || !inputFile.is_open()) {
BOOST_THROW_EXCEPTION(Error("Failed to read configuration file: " + filename));
BOOST_THROW_EXCEPTION(Error("Failed to read configuration file " + filename));
}
parse(inputFile, isDryRun, filename);
inputFile.close();
@@ -107,11 +104,8 @@ ConfigFile::parse(std::istream& input, bool isDryRun, const std::string& filenam
boost::property_tree::read_info(input, m_global);
}
catch (const boost::property_tree::info_parser_error& error) {
std::stringstream msg;
msg << "Failed to parse configuration file";
msg << " " << filename;
msg << " " << error.message() << " line " << error.line();
BOOST_THROW_EXCEPTION(Error(msg.str()));
BOOST_THROW_EXCEPTION(Error("Failed to parse configuration file " + filename +
": " + error.message() + " on line " + to_string(error.line())));
}
process(isDryRun, filename);
@@ -130,10 +124,7 @@ ConfigFile::process(bool isDryRun, const std::string& filename) const
BOOST_ASSERT(!filename.empty());
if (m_global.begin() == m_global.end()) {
std::string msg = "Error processing configuration file: ";
msg += filename;
msg += " no data";
BOOST_THROW_EXCEPTION(Error(msg));
BOOST_THROW_EXCEPTION(Error("Error processing configuration file " + filename + ": no data"));
}
for (const auto& i : m_global) {
+16 -7
View File
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2014-2015, Regents of the University of California,
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -107,15 +107,17 @@ public:
/** \brief provides a counter that observes the size of a table
* \tparam T a type that provides a size() const member function
*
* if table not specified in constructor, it can be added later by invoking observe()
*/
template<typename T>
class SizeCounter
{
public:
typedef size_t rep;
typedef size_t Rep;
constexpr
SizeCounter(const T& table)
explicit constexpr
SizeCounter(const T* table = nullptr)
: m_table(table)
{
}
@@ -125,15 +127,22 @@ public:
SizeCounter&
operator=(const SizeCounter&) = delete;
void
observe(const T* table)
{
m_table = table;
}
/** \brief observe the counter
*/
operator rep() const
operator Rep() const
{
return m_table.size();
BOOST_ASSERT(m_table != nullptr);
return m_table->size();
}
private:
const T& m_table;
const T* m_table;
};
} // namespace nfd
+4 -2
View File
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2014-2016, Regents of the University of California,
/*
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -30,6 +30,8 @@
#include <boost/algorithm/string/case_conv.hpp>
#include <boost/range/adaptor/map.hpp>
#include <iostream>
#ifdef HAVE_CUSTOM_LOGGER
#error "This file should not be compiled when custom logger is used"
#endif
+4 -3
View File
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2014-2016, Regents of the University of California,
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -30,13 +30,14 @@ namespace nfd {
using ndn::mgmt::ValidateParameters;
using ndn::mgmt::Authorization;
ManagerBase::ManagerBase(Dispatcher& dispatcher,
const std::string& module)
ManagerBase::ManagerBase(Dispatcher& dispatcher, const std::string& module)
: m_dispatcher(dispatcher)
, m_module(module)
{
}
ManagerBase::~ManagerBase() = default;
void
ManagerBase::registerStatusDatasetHandler(const std::string& verb,
const ndn::mgmt::StatusDatasetHandler& handler)
+5 -4
View File
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2014-2016, Regents of the University of California,
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -59,8 +59,10 @@ public:
};
public:
ManagerBase(Dispatcher& dispatcher,
const std::string& module);
ManagerBase(Dispatcher& dispatcher, const std::string& module);
virtual
~ManagerBase();
const std::string&
getModule() const
@@ -69,7 +71,6 @@ public:
}
PUBLIC_WITH_TESTS_ELSE_PROTECTED: // registrations to the dispatcher
// difference from mgmt::ControlCommand: accepts nfd::ControlParameters
typedef function<void(const ControlCommand& command,
const Name& prefix, const Interest& interest,
+20 -11
View File
@@ -1,5 +1,5 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
/*
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
@@ -26,9 +26,10 @@
#include "network-interface-predicate.hpp"
#include "config-file.hpp"
#include "network-interface.hpp"
#include "network.hpp"
#include <fnmatch.h>
namespace nfd {
NetworkInterfacePredicate::NetworkInterfacePredicate()
@@ -64,7 +65,7 @@ parseList(std::set<std::string>& set, const boost::property_tree::ptree& list, c
else if (item.first == "ether") {
// validate ethernet address
auto addr = item.second.get_value<std::string>();
if (ethernet::Address::fromString(addr).isNull()) {
if (ndn::ethernet::Address::fromString(addr).isNull()) {
BOOST_THROW_EXCEPTION(ConfigFile::Error("Malformed ether address \"" + addr +
"\" in \"" + section + "\" section"));
}
@@ -95,28 +96,36 @@ NetworkInterfacePredicate::parseBlacklist(const boost::property_tree::ptree& lis
}
static bool
doesMatchRule(const NetworkInterfaceInfo& netif, const std::string& rule)
doesMatchPattern(const std::string& ifname, const std::string& pattern)
{
// use fnmatch(3) to provide unix glob-style matching for interface names
// fnmatch returns 0 if there is a match
return ::fnmatch(pattern.data(), ifname.data(), 0) == 0;
}
static bool
doesMatchRule(const ndn::net::NetworkInterface& netif, const std::string& rule)
{
// if '/' is in rule, this is a subnet, check if IP in subnet
if (rule.find('/') != std::string::npos) {
Network n = boost::lexical_cast<Network>(rule);
for (const auto& addr : netif.ipv4Addresses) {
if (n.doesContain(addr)) {
for (const auto& addr : netif.getNetworkAddresses()) {
if (n.doesContain(addr.getIp())) {
return true;
}
}
}
return rule == "*" ||
netif.name == rule ||
netif.etherAddress.toString() == rule;
doesMatchPattern(netif.getName(), rule) ||
netif.getEthernetAddress().toString() == rule;
}
bool
NetworkInterfacePredicate::operator()(const NetworkInterfaceInfo& netif) const
NetworkInterfacePredicate::operator()(const ndn::net::NetworkInterface& netif) const
{
return std::any_of(m_whitelist.begin(), m_whitelist.end(), bind(&doesMatchRule, netif, _1)) &&
std::none_of(m_blacklist.begin(), m_blacklist.end(), bind(&doesMatchRule, netif, _1));
return std::any_of(m_whitelist.begin(), m_whitelist.end(), bind(&doesMatchRule, cref(netif), _1)) &&
std::none_of(m_blacklist.begin(), m_blacklist.end(), bind(&doesMatchRule, cref(netif), _1));
}
bool
+3 -4
View File
@@ -1,5 +1,5 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
/*
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
@@ -27,11 +27,10 @@
#define NFD_CORE_NETWORK_INTERFACE_PREDICATE_HPP
#include "common.hpp"
#include <ndn-cxx/net/network-interface.hpp>
namespace nfd {
class NetworkInterfaceInfo;
/**
* \brief Represents a predicate to accept or reject a NetworkInterfaceInfo.
*
@@ -59,7 +58,7 @@ public:
parseBlacklist(const boost::property_tree::ptree& list);
bool
operator()(const NetworkInterfaceInfo& netif) const;
operator()(const ndn::net::NetworkInterface& netif) const;
bool
operator==(const NetworkInterfacePredicate& other) const;
-185
View File
@@ -1,185 +0,0 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2014-2015, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
* Washington University in St. Louis,
* Beijing Institute of Technology,
* The University of Memphis.
*
* This file is part of NFD (Named Data Networking Forwarding Daemon).
* See AUTHORS.md for complete list of NFD authors and contributors.
*
* NFD is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Foundation,
* either version 3 of the License, or (at your option) any later version.
*
* NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
*/
#include "network-interface.hpp"
#include "core/logger.hpp"
#include <cerrno>
#include <cstring>
#include <type_traits>
#include <unordered_map>
#ifdef HAVE_IFADDRS_H
#include <ifaddrs.h> // for getifaddrs()
#include <arpa/inet.h> // for inet_ntop()
#include <netinet/in.h> // for struct sockaddr_in{,6}
#if defined(__linux__)
#include <net/if_arp.h> // for ARPHRD_* constants
#include <netpacket/packet.h> // for struct sockaddr_ll
#elif defined(__APPLE__) || defined(__FreeBSD__)
#include <net/if_dl.h> // for struct sockaddr_dl
#include <net/if_types.h> // for IFT_* constants
#endif
#endif // HAVE_IFADDRS_H
NFD_LOG_INIT("NetworkInterfaceInfo");
namespace nfd {
static_assert(std::is_standard_layout<NetworkInterfaceInfo>::value,
"NetworkInterfaceInfo must be a standard layout type");
#ifdef HAVE_IS_DEFAULT_CONSTRUCTIBLE
static_assert(std::is_default_constructible<NetworkInterfaceInfo>::value,
"NetworkInterfaceInfo must provide a default constructor");
#endif
#ifdef WITH_TESTS
static shared_ptr<std::vector<NetworkInterfaceInfo>> s_debugNetworkInterfaces = nullptr;
void
setDebugNetworkInterfaces(shared_ptr<std::vector<NetworkInterfaceInfo>> interfaces)
{
s_debugNetworkInterfaces = interfaces;
}
#endif
std::vector<NetworkInterfaceInfo>
listNetworkInterfaces()
{
#ifdef WITH_TESTS
if (s_debugNetworkInterfaces != nullptr) {
return *s_debugNetworkInterfaces;
}
#endif
#ifdef HAVE_IFADDRS_H
using namespace boost::asio::ip;
using std::strerror;
std::unordered_map<std::string, NetworkInterfaceInfo> ifmap;
ifaddrs* ifa_list = nullptr;
if (::getifaddrs(&ifa_list) < 0)
BOOST_THROW_EXCEPTION(std::runtime_error(std::string("getifaddrs() failed: ") +
strerror(errno)));
for (ifaddrs* ifa = ifa_list; ifa != nullptr; ifa = ifa->ifa_next) {
std::string ifname(ifa->ifa_name);
NetworkInterfaceInfo& netif = ifmap[ifname];
netif.name = ifa->ifa_name;
netif.flags = ifa->ifa_flags;
if (ifa->ifa_addr == nullptr)
continue;
switch (ifa->ifa_addr->sa_family) {
case AF_INET: {
const sockaddr_in* sin = reinterpret_cast<sockaddr_in*>(ifa->ifa_addr);
char address[INET_ADDRSTRLEN];
if (::inet_ntop(AF_INET, &sin->sin_addr, address, sizeof(address))) {
netif.ipv4Addresses.push_back(address_v4::from_string(address));
NFD_LOG_TRACE(ifname << ": added IPv4 address " << address);
}
else
NFD_LOG_WARN(ifname << ": inet_ntop(AF_INET) failed: " << strerror(errno));
break;
}
case AF_INET6: {
const sockaddr_in6* sin6 = reinterpret_cast<sockaddr_in6*>(ifa->ifa_addr);
char address[INET6_ADDRSTRLEN];
if (::inet_ntop(AF_INET6, &sin6->sin6_addr, address, sizeof(address))) {
netif.ipv6Addresses.push_back(address_v6::from_string(address));
NFD_LOG_TRACE(ifname << ": added IPv6 address " << address);
}
else
NFD_LOG_WARN(ifname << ": inet_ntop(AF_INET6) failed: " << strerror(errno));
break;
}
#if defined(__linux__)
case AF_PACKET: {
const sockaddr_ll* sll = reinterpret_cast<sockaddr_ll*>(ifa->ifa_addr);
netif.index = sll->sll_ifindex;
if (sll->sll_hatype == ARPHRD_ETHER && sll->sll_halen == ethernet::ADDR_LEN) {
netif.etherAddress = ethernet::Address(sll->sll_addr);
NFD_LOG_TRACE(ifname << ": added Ethernet address " << netif.etherAddress);
}
else if (sll->sll_hatype != ARPHRD_LOOPBACK) {
NFD_LOG_DEBUG(ifname << ": ignoring link-layer address for unhandled hardware type "
<< sll->sll_hatype);
}
break;
}
#elif defined(__APPLE__) || defined(__FreeBSD__)
case AF_LINK: {
const sockaddr_dl* sdl = reinterpret_cast<sockaddr_dl*>(ifa->ifa_addr);
netif.index = sdl->sdl_index;
if (sdl->sdl_type == IFT_ETHER && sdl->sdl_alen == ethernet::ADDR_LEN) {
netif.etherAddress = ethernet::Address(reinterpret_cast<uint8_t*>(LLADDR(sdl)));
NFD_LOG_TRACE(ifname << ": added Ethernet address " << netif.etherAddress);
}
else if (sdl->sdl_type != IFT_LOOP) {
NFD_LOG_DEBUG(ifname << ": ignoring link-layer address for unhandled interface type "
<< sdl->sdl_type);
}
break;
}
#endif
}
if (netif.isBroadcastCapable() && ifa->ifa_broadaddr != nullptr) {
const sockaddr_in* sin = reinterpret_cast<sockaddr_in*>(ifa->ifa_broadaddr);
char address[INET_ADDRSTRLEN];
if (::inet_ntop(AF_INET, &sin->sin_addr, address, sizeof(address))) {
netif.broadcastAddress = address_v4::from_string(address);
NFD_LOG_TRACE(ifname << ": added IPv4 broadcast address " << address);
}
else
NFD_LOG_WARN(ifname << ": inet_ntop(AF_INET) for broadaddr failed: " << strerror(errno));
}
}
::freeifaddrs(ifa_list);
std::vector<NetworkInterfaceInfo> v;
v.reserve(ifmap.size());
for (auto&& element : ifmap) {
v.push_back(element.second);
}
return v;
#else
return {};
#endif // HAVE_IFADDRS_H
}
} // namespace nfd
-107
View File
@@ -1,107 +0,0 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2014, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
* Washington University in St. Louis,
* Beijing Institute of Technology,
* The University of Memphis
*
* This file is part of NFD (Named Data Networking Forwarding Daemon).
* See AUTHORS.md for complete list of NFD authors and contributors.
*
* NFD is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Foundation,
* either version 3 of the License, or (at your option) any later version.
*
* NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef NFD_CORE_NETWORK_INTERFACE_HPP
#define NFD_CORE_NETWORK_INTERFACE_HPP
#include "common.hpp"
#include <ndn-cxx/util/ethernet.hpp>
#include <net/if.h>
namespace nfd {
namespace ethernet = ndn::util::ethernet;
/** \brief contains information about a network interface
*/
class NetworkInterfaceInfo
{
public:
int index;
std::string name;
ethernet::Address etherAddress;
std::vector<boost::asio::ip::address_v4> ipv4Addresses;
std::vector<boost::asio::ip::address_v6> ipv6Addresses;
boost::asio::ip::address_v4 broadcastAddress;
unsigned int flags;
bool
isLoopback() const;
bool
isMulticastCapable() const;
bool
isBroadcastCapable() const;
bool
isUp() const;
};
inline bool
NetworkInterfaceInfo::isLoopback() const
{
return (flags & IFF_LOOPBACK) != 0;
}
inline bool
NetworkInterfaceInfo::isMulticastCapable() const
{
return (flags & IFF_MULTICAST) != 0;
}
inline bool
NetworkInterfaceInfo::isBroadcastCapable() const
{
return (flags & IFF_BROADCAST) != 0;
}
inline bool
NetworkInterfaceInfo::isUp() const
{
return (flags & IFF_UP) != 0;
}
/** \brief List configured network interfaces on the system and their info
* \warning invalid IP addresses (e.g., 0.0.0.0) may be returned in some environments
*/
std::vector<NetworkInterfaceInfo>
listNetworkInterfaces();
#ifdef WITH_TESTS
/** \brief Set a list of network interfaces to be returned by subsequent listNetworkInterfaces call
* \note To reset to normal behavior, use `setDebugNetworkInterfaces(nullptr);`
*/
void
setDebugNetworkInterfaces(shared_ptr<std::vector<NetworkInterfaceInfo>> interfaces);
#endif
} // namespace nfd
#endif // NFD_CORE_NETWORK_INTERFACE_HPP
+7 -6
View File
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2014-2016, Regents of the University of California,
/*
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -21,10 +21,11 @@
*
* You should have received a copy of the GNU General Public License along with
* NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
**/
*/
#include "network.hpp"
#include <ndn-cxx/net/address-converter.hpp>
#include <boost/utility/value_init.hpp>
#include <algorithm>
@@ -105,11 +106,11 @@ operator>>(std::istream& is, Network& network)
size_t position = networkStr.find('/');
if (position == std::string::npos) {
network.m_minAddress = ip::address::from_string(networkStr);
network.m_maxAddress = ip::address::from_string(networkStr);
network.m_minAddress = ndn::ip::addressFromString(networkStr);
network.m_maxAddress = ndn::ip::addressFromString(networkStr);
}
else {
ip::address address = ip::address::from_string(networkStr.substr(0, position));
ip::address address = ndn::ip::addressFromString(networkStr.substr(0, position));
size_t mask = boost::lexical_cast<size_t>(networkStr.substr(position+1));
if (address.is_v4()) {
+4 -2
View File
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2014-2015, Regents of the University of California,
/*
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -29,6 +29,8 @@
#include <pwd.h>
#include <grp.h>
#include <sstream>
namespace nfd {
NFD_LOG_INIT("PrivilegeHelper");
+7 -6
View File
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2014-2016, Regents of the University of California,
/*
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -24,14 +24,15 @@
*/
/**
* This file declares the global pseudorandom number generator (PRNG).
* \file
* \brief Declares the global pseudorandom number generator (PRNG) for NFD
*
* All random numbers generated by NFD should use this global generator,
* so that it can be properly seeded when necessary.
*
* This PRNG is not suitable for security purposes; security-critical
* code must use a cryptographically secure PRNG, such as those provided
* by OpenSSL and Crypto++.
* \warning This PRNG is not suitable for security purposes; security-critical
* code must use a cryptographically secure PRNG, such as that provided
* by OpenSSL.
*/
#ifndef NFD_CORE_RANDOM_HPP
@@ -1,12 +1,12 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2014 Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
* Washington University in St. Louis,
* Beijing Institute of Technology,
* The University of Memphis
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
* Washington University in St. Louis,
* Beijing Institute of Technology,
* The University of Memphis
*
* This file is part of NFD (Named Data Networking Forwarding Daemon).
* See AUTHORS.md for complete list of NFD authors and contributors.
@@ -48,7 +48,8 @@ RttEstimator::addMeasurement(Duration measure)
m_rtt += gErr;
double difference = std::abs(err) - m_variance;
m_variance += difference * m_gain;
} else {
}
else {
m_rtt = m;
m_variance = m;
}
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2014-2016, Regents of the University of California,
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -23,8 +23,8 @@
* NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef NFD_DAEMON_FW_RTT_ESTIMATOR_HPP
#define NFD_DAEMON_FW_RTT_ESTIMATOR_HPP
#ifndef NFD_CORE_RTT_ESTIMATOR_HPP
#define NFD_CORE_RTT_ESTIMATOR_HPP
#include "core/common.hpp"
@@ -80,4 +80,4 @@ private:
} // namespace nfd
#endif // NFD_DAEMON_FW_RTT_ESTIMATOR_HPP
#endif // NFD_CORE_RTT_ESTIMATOR_HPP
+2 -2
View File
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2014-2015, Regents of the University of California,
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -44,7 +44,7 @@ getGlobalScheduler()
}
EventId
schedule(const time::nanoseconds& after, const Scheduler::Event& event)
schedule(time::nanoseconds after, const EventCallback& event)
{
return getGlobalScheduler().scheduleEvent(after, event);
}
+4 -2
View File
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2014-2015, Regents of the University of California,
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -39,10 +39,12 @@ using ndn::Scheduler;
*/
using ndn::EventId;
using ndn::util::scheduler::EventCallback;
/** \brief schedule an event
*/
EventId
schedule(const time::nanoseconds& after, const Scheduler::Event& event);
schedule(time::nanoseconds after, const EventCallback& event);
/** \brief cancel a scheduled event
*/
+62
View File
@@ -0,0 +1,62 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
* Washington University in St. Louis,
* Beijing Institute of Technology,
* The University of Memphis.
*
* This file is part of NFD (Named Data Networking Forwarding Daemon).
* See AUTHORS.md for complete list of NFD authors and contributors.
*
* NFD is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Foundation,
* either version 3 of the License, or (at your option) any later version.
*
* NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef NFD_DAEMON_FACE_CHANNEL_LOG_HPP
#define NFD_DAEMON_FACE_CHANNEL_LOG_HPP
#include "core/logger.hpp"
/** \defgroup ChannelLogging Channel logging macros
*
* These macros augment the log message with some channel-specific information,
* such as the local URI, that are useful to distinguish which channel produced
* the message. It is strongly recommended to use these macros instead of the
* generic ones for all logging inside Channel subclasses.
* @{
*/
/** \cond */
#define NFD_LOG_CHAN(level, msg) NFD_LOG_##level( \
"[" << this->getUri() << "] " << msg)
/** \endcond */
/** \brief Log a message at TRACE level */
#define NFD_LOG_CHAN_TRACE(msg) NFD_LOG_CHAN(TRACE, msg)
/** \brief Log a message at DEBUG level */
#define NFD_LOG_CHAN_DEBUG(msg) NFD_LOG_CHAN(DEBUG, msg)
/** \brief Log a message at INFO level */
#define NFD_LOG_CHAN_INFO(msg) NFD_LOG_CHAN(INFO, msg)
/** \brief Log a message at WARN level */
#define NFD_LOG_CHAN_WARN(msg) NFD_LOG_CHAN(WARN, msg)
/** \brief Log a message at ERROR level */
#define NFD_LOG_CHAN_ERROR(msg) NFD_LOG_CHAN(ERROR, msg)
/** @} */
#endif // NFD_DAEMON_FACE_CHANNEL_LOG_HPP
+4 -6
View File
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2014-2015, Regents of the University of California,
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -26,10 +26,9 @@
#include "channel.hpp"
namespace nfd {
namespace face {
Channel::~Channel()
{
}
Channel::~Channel() = default;
void
Channel::setUri(const FaceUri& uri)
@@ -40,8 +39,6 @@ Channel::setUri(const FaceUri& uri)
void
connectFaceClosedSignal(Face& face, const std::function<void()>& f)
{
using face::FaceState;
face.afterStateChange.connect(
[f] (FaceState oldState, FaceState newState) {
if (newState == FaceState::CLOSED) {
@@ -50,4 +47,5 @@ connectFaceClosedSignal(Face& face, const std::function<void()>& f)
});
}
} // namespace face
} // namespace nfd
+22 -14
View File
@@ -26,20 +26,20 @@
#ifndef NFD_DAEMON_FACE_CHANNEL_HPP
#define NFD_DAEMON_FACE_CHANNEL_HPP
#include "channel-log.hpp"
#include "face.hpp"
namespace nfd {
namespace face {
/**
* \brief Prototype for the callback that is invoked when the face
* is created (as a response to incoming connection or after
* connection is established)
class Face;
/** \brief Prototype for the callback that is invoked when a face is created
* (in response to an incoming connection or after a connection is established)
*/
typedef function<void(const shared_ptr<Face>& newFace)> FaceCreatedCallback;
/**
* \brief Prototype for the callback that is invoked when the face
* fails to be created
/** \brief Prototype for the callback that is invoked when a face fails to be created
*/
typedef function<void(uint32_t status, const std::string& reason)> FaceCreationFailedCallback;
@@ -56,7 +56,20 @@ public:
~Channel();
const FaceUri&
getUri() const;
getUri() const
{
return m_uri;
}
/** \brief Returns whether the channel is listening
*/
virtual bool
isListening() const = 0;
/** \brief Returns the number of faces in the channel
*/
virtual size_t
size() const = 0;
protected:
void
@@ -66,12 +79,6 @@ private:
FaceUri m_uri;
};
inline const FaceUri&
Channel::getUri() const
{
return m_uri;
}
/** \brief invokes a callback when the face is closed
* \param face the face
* \param f the callback to be invoked when the face enters CLOSED state
@@ -82,6 +89,7 @@ Channel::getUri() const
void
connectFaceClosedSignal(Face& face, const std::function<void()>& f);
} // namespace face
} // namespace nfd
#endif // NFD_DAEMON_FACE_CHANNEL_HPP
+21 -22
View File
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2014-2016, Regents of the University of California,
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -61,10 +61,10 @@ public:
const boost::system::error_code& error);
protected:
virtual void
void
doClose() override;
virtual void
void
doSend(Transport::Packet&& packet) override;
void
@@ -79,10 +79,10 @@ protected:
processErrorCode(const boost::system::error_code& error);
bool
hasBeenUsedRecently() const;
hasRecentlyReceived() const;
void
resetRecentUsage();
resetRecentlyReceived();
static EndpointId
makeEndpointId(const typename protocol::endpoint& ep);
@@ -95,14 +95,14 @@ protected:
private:
std::array<uint8_t, ndn::MAX_NDN_PACKET_SIZE> m_receiveBuffer;
bool m_hasBeenUsedRecently;
bool m_hasRecentlyReceived;
};
template<class T, class U>
DatagramTransport<T, U>::DatagramTransport(typename DatagramTransport::protocol::socket&& socket)
: m_socket(std::move(socket))
, m_hasBeenUsedRecently(false)
, m_hasRecentlyReceived(false)
{
m_socket.async_receive_from(boost::asio::buffer(m_receiveBuffer), m_sender,
bind(&DatagramTransport<T, U>::handleReceive, this,
@@ -152,13 +152,13 @@ DatagramTransport<T, U>::receiveDatagram(const uint8_t* buffer, size_t nBytesRec
if (error)
return processErrorCode(error);
NFD_LOG_FACE_TRACE("Received: " << nBytesReceived << " bytes");
NFD_LOG_FACE_TRACE("Received: " << nBytesReceived << " bytes from " << m_sender);
bool isOk = false;
Block element;
std::tie(isOk, element) = Block::fromBuffer(buffer, nBytesReceived);
if (!isOk) {
NFD_LOG_FACE_WARN("Failed to parse incoming packet");
NFD_LOG_FACE_WARN("Failed to parse incoming packet from " << m_sender);
// This packet won't extend the face lifetime
return;
}
@@ -167,7 +167,7 @@ DatagramTransport<T, U>::receiveDatagram(const uint8_t* buffer, size_t nBytesRec
// This packet won't extend the face lifetime
return;
}
m_hasBeenUsedRecently = true;
m_hasRecentlyReceived = true;
Transport::Packet tp(std::move(element));
tp.remoteEndpoint = makeEndpointId(m_sender);
@@ -209,38 +209,37 @@ DatagramTransport<T, U>::processErrorCode(const boost::system::error_code& error
if (getState() == TransportState::CLOSING ||
getState() == TransportState::FAILED ||
getState() == TransportState::CLOSED ||
error == boost::asio::error::operation_aborted)
error == boost::asio::error::operation_aborted) {
// transport is shutting down, ignore any errors
return;
}
if (getPersistency() == ndn::nfd::FacePersistency::FACE_PERSISTENCY_PERMANENT) {
if (getPersistency() == ndn::nfd::FACE_PERSISTENCY_PERMANENT) {
NFD_LOG_FACE_DEBUG("Permanent face ignores error: " << error.message());
return;
}
if (error != boost::asio::error::eof)
NFD_LOG_FACE_WARN("Send or receive operation failed: " << error.message());
NFD_LOG_FACE_ERROR("Send or receive operation failed: " << error.message());
this->setState(TransportState::FAILED);
doClose();
}
template<class T, class U>
inline bool
DatagramTransport<T, U>::hasBeenUsedRecently() const
bool
DatagramTransport<T, U>::hasRecentlyReceived() const
{
return m_hasBeenUsedRecently;
return m_hasRecentlyReceived;
}
template<class T, class U>
inline void
DatagramTransport<T, U>::resetRecentUsage()
void
DatagramTransport<T, U>::resetRecentlyReceived()
{
m_hasBeenUsedRecently = false;
m_hasRecentlyReceived = false;
}
template<class T, class U>
inline Transport::EndpointId
Transport::EndpointId
DatagramTransport<T, U>::makeEndpointId(const typename protocol::endpoint& ep)
{
return 0;
+241
View File
@@ -0,0 +1,241 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
* Washington University in St. Louis,
* Beijing Institute of Technology,
* The University of Memphis.
*
* This file is part of NFD (Named Data Networking Forwarding Daemon).
* See AUTHORS.md for complete list of NFD authors and contributors.
*
* NFD is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Foundation,
* either version 3 of the License, or (at your option) any later version.
*
* NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ethernet-channel.hpp"
#include "ethernet-protocol.hpp"
#include "generic-link-service.hpp"
#include "unicast-ethernet-transport.hpp"
#include "core/global-io.hpp"
#include <boost/range/adaptor/map.hpp>
#include <pcap/pcap.h>
namespace nfd {
namespace face {
NFD_LOG_INIT("EthernetChannel");
EthernetChannel::EthernetChannel(shared_ptr<const ndn::net::NetworkInterface> localEndpoint,
time::nanoseconds idleTimeout)
: m_localEndpoint(std::move(localEndpoint))
, m_isListening(false)
, m_socket(getGlobalIoService())
, m_pcap(m_localEndpoint->getName())
, m_idleFaceTimeout(idleTimeout)
#ifdef _DEBUG
, m_nDropped(0)
#endif
{
setUri(FaceUri::fromDev(m_localEndpoint->getName()));
NFD_LOG_CHAN_INFO("Creating channel");
}
void
EthernetChannel::connect(const ethernet::Address& remoteEndpoint,
ndn::nfd::FacePersistency persistency,
bool wantLpReliability,
const FaceCreatedCallback& onFaceCreated,
const FaceCreationFailedCallback& onConnectFailed)
{
shared_ptr<Face> face;
try {
face = createFace(remoteEndpoint, persistency, wantLpReliability).second;
}
catch (const boost::system::system_error& e) {
NFD_LOG_CHAN_DEBUG("Face creation for " << remoteEndpoint << " failed: " << e.what());
if (onConnectFailed)
onConnectFailed(504, std::string("Face creation failed: ") + e.what());
return;
}
// Need to invoke the callback regardless of whether or not we had already
// created the face so that control responses and such can be sent
onFaceCreated(face);
}
void
EthernetChannel::listen(const FaceCreatedCallback& onFaceCreated,
const FaceCreationFailedCallback& onFaceCreationFailed)
{
if (isListening()) {
NFD_LOG_CHAN_WARN("Already listening");
return;
}
m_isListening = true;
try {
m_pcap.activate(DLT_EN10MB);
m_socket.assign(m_pcap.getFd());
}
catch (const PcapHelper::Error& e) {
BOOST_THROW_EXCEPTION(Error(e.what()));
}
updateFilter();
asyncRead(onFaceCreated, onFaceCreationFailed);
NFD_LOG_CHAN_DEBUG("Started listening");
}
void
EthernetChannel::asyncRead(const FaceCreatedCallback& onFaceCreated,
const FaceCreationFailedCallback& onReceiveFailed)
{
m_socket.async_read_some(boost::asio::null_buffers(),
bind(&EthernetChannel::handleRead, this,
boost::asio::placeholders::error,
onFaceCreated, onReceiveFailed));
}
void
EthernetChannel::handleRead(const boost::system::error_code& error,
const FaceCreatedCallback& onFaceCreated,
const FaceCreationFailedCallback& onReceiveFailed)
{
if (error) {
if (error != boost::asio::error::operation_aborted) {
NFD_LOG_CHAN_DEBUG("Receive failed: " << error.message());
if (onReceiveFailed)
onReceiveFailed(500, "Receive failed: " + error.message());
}
return;
}
const uint8_t* pkt;
size_t len;
std::string err;
std::tie(pkt, len, err) = m_pcap.readNextPacket();
if (pkt == nullptr) {
NFD_LOG_CHAN_WARN("Read error: " << err);
}
else {
const ether_header* eh;
std::tie(eh, err) = ethernet::checkFrameHeader(pkt, len, m_localEndpoint->getEthernetAddress(),
m_localEndpoint->getEthernetAddress());
if (eh == nullptr) {
NFD_LOG_CHAN_DEBUG(err);
}
else {
ethernet::Address sender(eh->ether_shost);
pkt += ethernet::HDR_LEN;
len -= ethernet::HDR_LEN;
processIncomingPacket(pkt, len, sender, onFaceCreated, onReceiveFailed);
}
}
#ifdef _DEBUG
size_t nDropped = m_pcap.getNDropped();
if (nDropped - m_nDropped > 0)
NFD_LOG_CHAN_DEBUG("Detected " << nDropped - m_nDropped << " dropped frame(s)");
m_nDropped = nDropped;
#endif
asyncRead(onFaceCreated, onReceiveFailed);
}
void
EthernetChannel::processIncomingPacket(const uint8_t* packet, size_t length,
const ethernet::Address& sender,
const FaceCreatedCallback& onFaceCreated,
const FaceCreationFailedCallback& onReceiveFailed)
{
NFD_LOG_CHAN_TRACE("New peer " << sender);
bool isCreated = false;
shared_ptr<Face> face;
try {
std::tie(isCreated, face) = createFace(sender, ndn::nfd::FACE_PERSISTENCY_ON_DEMAND, false);
}
catch (const EthernetTransport::Error& e) {
NFD_LOG_CHAN_DEBUG("Face creation for " << sender << " failed: " << e.what());
if (onReceiveFailed)
onReceiveFailed(504, std::string("Face creation failed: ") + e.what());
return;
}
if (isCreated)
onFaceCreated(face);
else
NFD_LOG_CHAN_DEBUG("Received frame for existing face");
// dispatch the packet to the face for processing
auto* transport = static_cast<UnicastEthernetTransport*>(face->getTransport());
transport->receivePayload(packet, length, sender);
}
std::pair<bool, shared_ptr<Face>>
EthernetChannel::createFace(const ethernet::Address& remoteEndpoint,
ndn::nfd::FacePersistency persistency,
bool wantLpReliability)
{
auto it = m_channelFaces.find(remoteEndpoint);
if (it != m_channelFaces.end()) {
// we already have a face for this endpoint, so reuse it
NFD_LOG_CHAN_TRACE("Reusing existing face for " << remoteEndpoint);
return {false, it->second};
}
// else, create a new face
GenericLinkService::Options options;
options.allowFragmentation = true;
options.allowReassembly = true;
options.reliabilityOptions.isEnabled = wantLpReliability;
auto linkService = make_unique<GenericLinkService>(options);
auto transport = make_unique<UnicastEthernetTransport>(*m_localEndpoint, remoteEndpoint,
persistency, m_idleFaceTimeout);
auto face = make_shared<Face>(std::move(linkService), std::move(transport));
m_channelFaces[remoteEndpoint] = face;
connectFaceClosedSignal(*face, [this, remoteEndpoint] {
m_channelFaces.erase(remoteEndpoint);
updateFilter();
});
updateFilter();
return {true, face};
}
void
EthernetChannel::updateFilter()
{
if (!isListening())
return;
std::string filter = "(ether proto " + to_string(ethernet::ETHERTYPE_NDN) +
") && (ether dst " + m_localEndpoint->getEthernetAddress().toString() + ")";
for (const auto& addr : m_channelFaces | boost::adaptors::map_keys) {
filter += " && (not ether src " + addr.toString() + ")";
}
// "not vlan" must appear last in the filter expression, or the
// rest of the filter won't work as intended, see pcap-filter(7)
filter += " && (not vlan)";
NFD_LOG_CHAN_TRACE("Updating filter: " << filter);
m_pcap.setPacketFilter(filter.data());
}
} // namespace face
} // namespace nfd
+150
View File
@@ -0,0 +1,150 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
* Washington University in St. Louis,
* Beijing Institute of Technology,
* The University of Memphis.
*
* This file is part of NFD (Named Data Networking Forwarding Daemon).
* See AUTHORS.md for complete list of NFD authors and contributors.
*
* NFD is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Foundation,
* either version 3 of the License, or (at your option) any later version.
*
* NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef NFD_DAEMON_FACE_ETHERNET_CHANNEL_HPP
#define NFD_DAEMON_FACE_ETHERNET_CHANNEL_HPP
#include "channel.hpp"
#include "ethernet-protocol.hpp"
#include "pcap-helper.hpp"
#include <ndn-cxx/net/network-interface.hpp>
namespace nfd {
namespace face {
/**
* \brief Class implementing Ethernet-based channel to create faces
*/
class EthernetChannel : public Channel
{
public:
/**
* \brief EthernetChannel-related error
*/
class Error : public std::runtime_error
{
public:
explicit
Error(const std::string& what)
: std::runtime_error(what)
{
}
};
/**
* \brief Create an Ethernet channel on the given \p localEndpoint (network interface)
*
* To enable creation of faces upon incoming connections,
* one needs to explicitly call EthernetChannel::listen method.
*/
EthernetChannel(shared_ptr<const ndn::net::NetworkInterface> localEndpoint,
time::nanoseconds idleTimeout);
bool
isListening() const override
{
return m_isListening;
}
size_t
size() const override
{
return m_channelFaces.size();
}
/**
* \brief Create a unicast Ethernet face toward \p remoteEndpoint
*
* \param remoteEndpoint The remote Ethernet endpoint
* \param persistency Persistency of the newly created face
* \param wantLpReliability whether LpReliability should be enabled
* \param onFaceCreated Callback to notify successful creation of the face
* \param onConnectFailed Callback to notify errors
*/
void
connect(const ethernet::Address& remoteEndpoint,
ndn::nfd::FacePersistency persistency,
bool wantLpReliability,
const FaceCreatedCallback& onFaceCreated,
const FaceCreationFailedCallback& onConnectFailed);
/**
* \brief Start listening
*
* Enable listening on the local endpoint, waiting for incoming frames,
* and creating a face when a frame is received from a new remote host.
*
* Faces created in this way will have on-demand persistency.
*
* \param onFaceCreated Callback to notify successful creation of a face
* \param onFaceCreationFailed Callback to notify errors
* \throw Error
*/
void
listen(const FaceCreatedCallback& onFaceCreated,
const FaceCreationFailedCallback& onFaceCreationFailed);
private:
void
asyncRead(const FaceCreatedCallback& onFaceCreated,
const FaceCreationFailedCallback& onReceiveFailed);
void
handleRead(const boost::system::error_code& error,
const FaceCreatedCallback& onFaceCreated,
const FaceCreationFailedCallback& onReceiveFailed);
void
processIncomingPacket(const uint8_t* packet, size_t length,
const ethernet::Address& sender,
const FaceCreatedCallback& onFaceCreated,
const FaceCreationFailedCallback& onReceiveFailed);
std::pair<bool, shared_ptr<Face>>
createFace(const ethernet::Address& remoteEndpoint,
ndn::nfd::FacePersistency persistency,
bool wantLpReliability);
void
updateFilter();
private:
shared_ptr<const ndn::net::NetworkInterface> m_localEndpoint;
bool m_isListening;
boost::asio::posix::stream_descriptor m_socket;
PcapHelper m_pcap;
std::map<ethernet::Address, shared_ptr<Face>> m_channelFaces;
const time::nanoseconds m_idleFaceTimeout; ///< Timeout for automatic closure of idle on-demand faces
#ifdef _DEBUG
/// number of frames dropped by the kernel, as reported by libpcap
size_t m_nDropped;
#endif
};
} // namespace face
} // namespace nfd
#endif // NFD_DAEMON_FACE_ETHERNET_CHANNEL_HPP
+226 -65
View File
@@ -1,5 +1,5 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
/*
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
@@ -24,9 +24,8 @@
*/
#include "ethernet-factory.hpp"
#include "ethernet-transport.hpp"
#include "generic-link-service.hpp"
#include "core/logger.hpp"
#include "multicast-ethernet-transport.hpp"
#include <boost/range/adaptors.hpp>
#include <boost/range/algorithm/copy.hpp>
@@ -43,6 +42,15 @@ EthernetFactory::getId()
return id;
}
EthernetFactory::EthernetFactory(const CtorParams& params)
: ProtocolFactory(params)
{
m_netifAddConn = netmon->onInterfaceAdded.connect(
[this] (const shared_ptr<const ndn::net::NetworkInterface>& netif) {
this->applyUnicastConfigToNetif(netif);
this->applyMcastConfigToNetif(*netif);
});
}
void
EthernetFactory::processConfig(OptionalConfigSection configSection,
@@ -50,8 +58,11 @@ EthernetFactory::processConfig(OptionalConfigSection configSection,
{
// ether
// {
// listen yes
// idle_timeout 600
// mcast yes
// mcast_group 01:00:5E:00:17:AA
// mcast_ad_hoc no
// whitelist
// {
// *
@@ -61,18 +72,25 @@ EthernetFactory::processConfig(OptionalConfigSection configSection,
// }
// }
UnicastConfig unicastConfig;
MulticastConfig mcastConfig;
if (configSection) {
// face_system.ether.mcast defaults to 'yes' but only if face_system.ether section is present
mcastConfig.isEnabled = true;
// listen and mcast default to 'yes' but only if face_system.ether section is present
unicastConfig.isEnabled = unicastConfig.wantListen = mcastConfig.isEnabled = true;
for (const auto& pair : *configSection) {
const std::string& key = pair.first;
const ConfigSection& value = pair.second;
if (key == "mcast") {
mcastConfig.isEnabled = ConfigFile::parseYesNo(pair, "ether");
if (key == "listen") {
unicastConfig.wantListen = ConfigFile::parseYesNo(pair, "face_system.ether");
}
else if (key == "idle_timeout") {
unicastConfig.idleTimeout = time::seconds(ConfigFile::parseNumber<uint32_t>(pair, "face_system.ether"));
}
else if (key == "mcast") {
mcastConfig.isEnabled = ConfigFile::parseYesNo(pair, "face_system.ether");
}
else if (key == "mcast_group") {
const std::string& valueStr = value.get_value<std::string>();
@@ -86,6 +104,10 @@ EthernetFactory::processConfig(OptionalConfigSection configSection,
valueStr + "' is not a multicast address"));
}
}
else if (key == "mcast_ad_hoc") {
bool wantAdHoc = ConfigFile::parseYesNo(pair, "face_system.ether");
mcastConfig.linkType = wantAdHoc ? ndn::nfd::LINK_TYPE_AD_HOC : ndn::nfd::LINK_TYPE_MULTI_ACCESS;
}
else if (key == "whitelist") {
mcastConfig.netifPredicate.parseWhitelist(value);
}
@@ -98,66 +120,136 @@ EthernetFactory::processConfig(OptionalConfigSection configSection,
}
}
if (!context.isDryRun) {
if (m_mcastConfig.isEnabled != mcastConfig.isEnabled) {
if (mcastConfig.isEnabled) {
NFD_LOG_INFO("enabling multicast on " << mcastConfig.group);
}
else {
NFD_LOG_INFO("disabling multicast");
}
if (context.isDryRun) {
return;
}
if (unicastConfig.isEnabled) {
if (m_unicastConfig.wantListen && !unicastConfig.wantListen && !m_channels.empty()) {
NFD_LOG_WARN("Cannot stop listening on Ethernet channels");
}
else if (m_mcastConfig.group != mcastConfig.group) {
if (m_unicastConfig.idleTimeout != unicastConfig.idleTimeout && !m_channels.empty()) {
NFD_LOG_WARN("Idle timeout setting applies to new Ethernet channels only");
}
}
else if (m_unicastConfig.isEnabled && !m_channels.empty()) {
NFD_LOG_WARN("Cannot disable Ethernet channels after initialization");
}
if (m_mcastConfig.isEnabled != mcastConfig.isEnabled) {
if (mcastConfig.isEnabled) {
NFD_LOG_INFO("enabling multicast on " << mcastConfig.group);
}
else {
NFD_LOG_INFO("disabling multicast");
}
}
else if (mcastConfig.isEnabled) {
if (m_mcastConfig.linkType != mcastConfig.linkType && !m_mcastFaces.empty()) {
NFD_LOG_WARN("Cannot change ad hoc setting on existing faces");
}
if (m_mcastConfig.group != mcastConfig.group) {
NFD_LOG_INFO("changing multicast group from " << m_mcastConfig.group <<
" to " << mcastConfig.group);
}
else if (m_mcastConfig.netifPredicate != mcastConfig.netifPredicate) {
if (m_mcastConfig.netifPredicate != mcastConfig.netifPredicate) {
NFD_LOG_INFO("changing whitelist/blacklist");
}
else {
// There's no configuration change, but we still need to re-apply configuration because
// netifs may have changed.
}
m_mcastConfig = mcastConfig;
this->applyConfig(context);
}
// Even if there's no configuration change, we still need to re-apply configuration because
// netifs may have changed.
m_unicastConfig = unicastConfig;
m_mcastConfig = mcastConfig;
this->applyConfig(context);
}
void
EthernetFactory::createFace(const FaceUri& uri,
ndn::nfd::FacePersistency persistency,
bool wantLocalFieldsEnabled,
EthernetFactory::createFace(const CreateFaceParams& params,
const FaceCreatedCallback& onCreated,
const FaceCreationFailedCallback& onFailure)
{
onFailure(406, "Unsupported protocol");
BOOST_ASSERT(params.remoteUri.isCanonical());
if (!params.localUri || params.localUri->getScheme() != "dev") {
NFD_LOG_TRACE("Cannot create unicast Ethernet face without dev:// LocalUri");
onFailure(406, "Creation of unicast Ethernet faces requires a LocalUri with dev:// scheme");
return;
}
BOOST_ASSERT(params.localUri->isCanonical());
if (params.persistency == ndn::nfd::FACE_PERSISTENCY_ON_DEMAND) {
NFD_LOG_TRACE("createFace does not support FACE_PERSISTENCY_ON_DEMAND");
onFailure(406, "Outgoing Ethernet faces do not support on-demand persistency");
return;
}
ethernet::Address remoteEndpoint(ethernet::Address::fromString(params.remoteUri.getHost()));
std::string localEndpoint(params.localUri->getHost());
if (remoteEndpoint.isMulticast()) {
NFD_LOG_TRACE("createFace does not support multicast faces");
onFailure(406, "Cannot create multicast Ethernet faces");
return;
}
if (params.wantLocalFields) {
// Ethernet faces are never local
NFD_LOG_TRACE("createFace cannot create non-local face with local fields enabled");
onFailure(406, "Local fields can only be enabled on faces with local scope");
return;
}
for (const auto& i : m_channels) {
if (i.first == localEndpoint) {
i.second->connect(remoteEndpoint, params.persistency, params.wantLpReliability,
onCreated, onFailure);
return;
}
}
NFD_LOG_TRACE("No channels available to connect to " << remoteEndpoint);
onFailure(504, "No channels available to connect");
}
shared_ptr<EthernetChannel>
EthernetFactory::createChannel(const shared_ptr<const ndn::net::NetworkInterface>& localEndpoint,
time::nanoseconds idleTimeout)
{
auto it = m_channels.find(localEndpoint->getName());
if (it != m_channels.end())
return it->second;
auto channel = std::make_shared<EthernetChannel>(localEndpoint, idleTimeout);
m_channels[localEndpoint->getName()] = channel;
return channel;
}
std::vector<shared_ptr<const Channel>>
EthernetFactory::getChannels() const
{
return {};
return getChannelsFromMap(m_channels);
}
shared_ptr<Face>
EthernetFactory::createMulticastFace(const NetworkInterfaceInfo& netif,
EthernetFactory::createMulticastFace(const ndn::net::NetworkInterface& netif,
const ethernet::Address& address)
{
BOOST_ASSERT(address.isMulticast());
auto key = std::make_pair(netif.name, address);
auto key = std::make_pair(netif.getName(), address);
auto found = m_mcastFaces.find(key);
if (found != m_mcastFaces.end()) {
return found->second;
}
face::GenericLinkService::Options opts;
GenericLinkService::Options opts;
opts.allowFragmentation = true;
opts.allowReassembly = true;
auto linkService = make_unique<face::GenericLinkService>(opts);
auto transport = make_unique<face::EthernetTransport>(netif, address);
auto linkService = make_unique<GenericLinkService>(opts);
auto transport = make_unique<MulticastEthernetTransport>(netif, address, m_mcastConfig.linkType);
auto face = make_shared<Face>(std::move(linkService), std::move(transport));
m_mcastFaces[key] = face;
@@ -166,46 +258,115 @@ EthernetFactory::createMulticastFace(const NetworkInterfaceInfo& netif,
return face;
}
shared_ptr<EthernetChannel>
EthernetFactory::applyUnicastConfigToNetif(const shared_ptr<const ndn::net::NetworkInterface>& netif)
{
if (!m_unicastConfig.isEnabled) {
return nullptr;
}
if (netif->getType() != ndn::net::InterfaceType::ETHERNET) {
NFD_LOG_DEBUG("Not creating channel on " << netif->getName() << ": incompatible netif type");
return nullptr;
}
if (!netif->isUp()) {
NFD_LOG_DEBUG("Not creating channel on " << netif->getName() << ": netif is down");
return nullptr;
}
if (netif->getEthernetAddress().isNull()) {
NFD_LOG_DEBUG("Not creating channel on " << netif->getName() << ": invalid Ethernet address");
return nullptr;
}
auto channel = this->createChannel(netif, m_unicastConfig.idleTimeout);
if (m_unicastConfig.wantListen && !channel->isListening()) {
try {
channel->listen(this->addFace, nullptr);
}
catch (const EthernetChannel::Error& e) {
NFD_LOG_WARN("Cannot listen on " << netif->getName() << ": " << e.what());
}
}
return channel;
}
shared_ptr<Face>
EthernetFactory::applyMcastConfigToNetif(const ndn::net::NetworkInterface& netif)
{
if (!m_mcastConfig.isEnabled) {
return nullptr;
}
if (netif.getType() != ndn::net::InterfaceType::ETHERNET) {
NFD_LOG_DEBUG("Not creating multicast face on " << netif.getName() << ": incompatible netif type");
return nullptr;
}
if (!netif.isUp()) {
NFD_LOG_DEBUG("Not creating multicast face on " << netif.getName() << ": netif is down");
return nullptr;
}
if (!netif.canMulticast()) {
NFD_LOG_DEBUG("Not creating multicast face on " << netif.getName() << ": netif cannot multicast");
return nullptr;
}
if (netif.getEthernetAddress().isNull()) {
NFD_LOG_DEBUG("Not creating multicast face on " << netif.getName() << ": invalid Ethernet address");
return nullptr;
}
if (!m_mcastConfig.netifPredicate(netif)) {
NFD_LOG_DEBUG("Not creating multicast face on " << netif.getName() << ": rejected by whitelist/blacklist");
return nullptr;
}
NFD_LOG_DEBUG("Creating multicast face on " << netif.getName());
shared_ptr<Face> face;
try {
face = this->createMulticastFace(netif, m_mcastConfig.group);
}
catch (const EthernetTransport::Error& e) {
NFD_LOG_WARN("Cannot create multicast face on " << netif.getName() << ": " << e.what());
return nullptr;
}
if (face->getId() == face::INVALID_FACEID) {
// new face: register with forwarding
this->addFace(face);
}
return face;
}
void
EthernetFactory::applyConfig(const FaceSystem::ConfigContext& context)
{
// collect old faces
if (m_unicastConfig.isEnabled) {
providedSchemes.insert("ether");
}
else {
providedSchemes.erase("ether");
}
// collect old multicast faces
std::set<shared_ptr<Face>> oldFaces;
boost::copy(m_mcastFaces | boost::adaptors::map_values,
std::inserter(oldFaces, oldFaces.end()));
boost::copy(m_mcastFaces | boost::adaptors::map_values, std::inserter(oldFaces, oldFaces.end()));
if (m_mcastConfig.isEnabled) {
// determine interfaces on which faces should be created or retained
auto capableNetifs = context.listNetifs() |
boost::adaptors::filtered([this] (const NetworkInterfaceInfo& netif) {
return netif.isUp() && netif.isMulticastCapable() &&
m_mcastConfig.netifPredicate(netif);
});
// create channels and multicast faces if requested by config
for (const auto& netif : netmon->listNetworkInterfaces()) {
this->applyUnicastConfigToNetif(netif);
// create faces
for (const auto& netif : capableNetifs) {
shared_ptr<Face> face;
try {
face = this->createMulticastFace(netif, m_mcastConfig.group);
}
catch (const EthernetTransport::Error& e) {
NFD_LOG_ERROR("Cannot create Ethernet multicast face on " << netif.name << ": " <<
e.what() << ", continuing");
continue;
}
if (face->getId() == face::INVALID_FACEID) {
// new face: register with forwarding
context.addFace(face);
}
else {
// existing face: don't destroy
oldFaces.erase(face);
}
auto face = this->applyMcastConfigToNetif(*netif);
if (face != nullptr) {
// don't destroy face
oldFaces.erase(face);
}
}
// destroy old faces that are not needed in new configuration
// destroy old multicast faces that are not needed in new configuration
for (const auto& face : oldFaces) {
face->close();
}
+57 -18
View File
@@ -1,5 +1,5 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
/*
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
@@ -27,13 +27,12 @@
#define NFD_DAEMON_FACE_ETHERNET_FACTORY_HPP
#include "protocol-factory.hpp"
#include "ethernet-channel.hpp"
namespace nfd {
namespace face {
/** \brief protocol factory for Ethernet
*
* Currently, only Ethernet multicast is supported.
*/
class EthernetFactory : public ProtocolFactory
{
@@ -41,52 +40,92 @@ public:
static const std::string&
getId();
explicit
EthernetFactory(const CtorParams& params);
/** \brief process face_system.ether config section
*/
void
processConfig(OptionalConfigSection configSection,
FaceSystem::ConfigContext& context) override;
/** \brief unicast face creation is not supported and will always fail
*/
void
createFace(const FaceUri& uri,
ndn::nfd::FacePersistency persistency,
bool wantLocalFieldsEnabled,
createFace(const CreateFaceParams& params,
const FaceCreatedCallback& onCreated,
const FaceCreationFailedCallback& onFailure) override;
/** \return empty container, because Ethernet unicast is not supported
/**
* \brief Create Ethernet-based channel on the specified network interface
*
* If this method is called twice with the same endpoint, only one channel
* will be created. The second call will just return the existing channel.
*
* \return always a valid pointer to a EthernetChannel object, an exception
* is thrown if it cannot be created.
* \throw PcapHelper::Error channel creation failed
*/
shared_ptr<EthernetChannel>
createChannel(const shared_ptr<const ndn::net::NetworkInterface>& localEndpoint,
time::nanoseconds idleTimeout);
std::vector<shared_ptr<const Channel>>
getChannels() const override;
private:
/** \brief Create a face to communicate on the given Ethernet multicast group
* \param netif local network interface
* \param group multicast group address
* \note Calling this method again with same arguments returns the existing face on the given
* interface and multicast group rather than creating a new one.
* \throw EthernetTransport::Error transport creation fails
/**
* \brief Create a face to communicate on the given Ethernet multicast group
*
* If this method is called twice with the same arguments, only one face
* will be created. The second call will just return the existing face.
*
* \param localEndpoint local network interface
* \param group multicast group address
*
* \throw EthernetTransport::Error transport creation fails
*/
shared_ptr<Face>
createMulticastFace(const NetworkInterfaceInfo& netif, const ethernet::Address& group);
createMulticastFace(const ndn::net::NetworkInterface& localEndpoint,
const ethernet::Address& group);
private:
/** \brief Create EthernetChannel on \p netif if requested by \p m_unicastConfig.
* \return new or existing channel, or nullptr if no channel should be created
*/
shared_ptr<EthernetChannel>
applyUnicastConfigToNetif(const shared_ptr<const ndn::net::NetworkInterface>& netif);
/** \brief Create Ethernet multicast face on \p netif if requested by \p m_mcastConfig.
* \return new or existing face, or nullptr if no face should be created
*/
shared_ptr<Face>
applyMcastConfigToNetif(const ndn::net::NetworkInterface& netif);
void
applyConfig(const FaceSystem::ConfigContext& context);
private:
std::map<std::string, shared_ptr<EthernetChannel>> m_channels; ///< ifname => channel
struct UnicastConfig
{
bool isEnabled = false;
bool wantListen = false;
time::nanoseconds idleTimeout = time::seconds(600);
};
UnicastConfig m_unicastConfig;
struct MulticastConfig
{
bool isEnabled = false;
ethernet::Address group = ethernet::getDefaultMulticastAddress();
ndn::nfd::LinkType linkType = ndn::nfd::LINK_TYPE_MULTI_ACCESS;
NetworkInterfacePredicate netifPredicate;
};
MulticastConfig m_mcastConfig;
/// (ifname, group) => face
std::map<std::pair<std::string, ethernet::Address>, shared_ptr<Face>> m_mcastFaces;
signal::ScopedConnection m_netifAddConn;
};
} // namespace face
+61
View File
@@ -0,0 +1,61 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
* Washington University in St. Louis,
* Beijing Institute of Technology,
* The University of Memphis.
*
* This file is part of NFD (Named Data Networking Forwarding Daemon).
* See AUTHORS.md for complete list of NFD authors and contributors.
*
* NFD is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Foundation,
* either version 3 of the License, or (at your option) any later version.
*
* NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ethernet-protocol.hpp"
#include <arpa/inet.h> // for ntohs()
namespace nfd {
namespace ethernet {
std::pair<const ether_header*, std::string>
checkFrameHeader(const uint8_t* packet, size_t length,
const Address& localAddr, const Address& destAddr)
{
if (length < HDR_LEN + MIN_DATA_LEN)
return {nullptr, "Received frame too short: " + to_string(length) + " bytes"};
const ether_header* eh = reinterpret_cast<const ether_header*>(packet);
// in some cases VLAN-tagged frames may survive the BPF filter,
// make sure we do not process those frames (see #3348)
if (ntohs(eh->ether_type) != ETHERTYPE_NDN)
return {nullptr, "Received frame with wrong ethertype: " + to_string(ntohs(eh->ether_type))};
#ifdef _DEBUG
Address shost(eh->ether_shost);
if (shost == localAddr)
return {nullptr, "Received frame sent by this host"};
Address dhost(eh->ether_dhost);
if (dhost != destAddr)
return {nullptr, "Received frame addressed to another host or multicast group: " + dhost.toString()};
#endif
return {eh, ""};
}
} // namespace ethernet
} // namespace nfd
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2014-2016, Regents of the University of California,
/*
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -23,22 +23,23 @@
* NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef NFD_TOOLS_NFDC_LEGACY_STATUS_HPP
#define NFD_TOOLS_NFDC_LEGACY_STATUS_HPP
#ifndef NFD_DAEMON_FACE_ETHERNET_PROTOCOL_HPP
#define NFD_DAEMON_FACE_ETHERNET_PROTOCOL_HPP
#include "status.hpp"
#include "core/common.hpp"
#include <ndn-cxx/net/ethernet.hpp>
#include <net/ethernet.h>
namespace nfd {
namespace tools {
namespace nfdc {
namespace ethernet {
/** \brief registers 'legacy-nfd-status' command
*/
void
registerLegacyStatusCommand(CommandParser& parser);
using namespace ndn::ethernet;
} // namespace nfdc
} // namespace tools
std::pair<const ether_header*, std::string>
checkFrameHeader(const uint8_t* packet, size_t length,
const Address& localAddr, const Address& destAddr);
} // namespace ethernet
} // namespace nfd
#endif // NFD_TOOLS_NFDC_LEGACY_STATUS_HPP
#endif // NFD_DAEMON_FACE_ETHERNET_PROTOCOL_HPP
+90 -285
View File
@@ -1,5 +1,5 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
/*
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
@@ -24,110 +24,44 @@
*/
#include "ethernet-transport.hpp"
#include "ethernet-protocol.hpp"
#include "core/global-io.hpp"
#include <pcap/pcap.h>
#include <cerrno> // for errno
#include <cstring> // for memcpy(), strerror(), strncpy()
#include <arpa/inet.h> // for htons() and ntohs()
#include <net/ethernet.h> // for struct ether_header
#include <net/if.h> // for struct ifreq
#include <stdio.h> // for snprintf()
#include <sys/ioctl.h> // for ioctl()
#include <unistd.h> // for dup()
#if defined(__linux__)
#include <netpacket/packet.h> // for struct packet_mreq
#include <sys/socket.h> // for setsockopt()
#endif
#ifdef SIOCADDMULTI
#if defined(__APPLE__) || defined(__FreeBSD__)
#include <net/if_dl.h> // for struct sockaddr_dl
#endif
#endif
#if !defined(PCAP_NETMASK_UNKNOWN)
/*
* Value to pass to pcap_compile() as the netmask if you don't know what
* the netmask is.
*/
#define PCAP_NETMASK_UNKNOWN 0xffffffff
#endif
#include <arpa/inet.h> // for htons()
#include <cstring> // for memcpy()
namespace nfd {
namespace face {
NFD_LOG_INIT("EthernetTransport");
EthernetTransport::EthernetTransport(const NetworkInterfaceInfo& interface,
const ethernet::Address& mcastAddress)
: m_pcap(nullptr, pcap_close)
, m_socket(getGlobalIoService())
, m_srcAddress(interface.etherAddress)
, m_destAddress(mcastAddress)
, m_interfaceName(interface.name)
#if defined(__linux__)
, m_interfaceIndex(interface.index)
#endif
EthernetTransport::EthernetTransport(const ndn::net::NetworkInterface& localEndpoint,
const ethernet::Address& remoteEndpoint)
: m_socket(getGlobalIoService())
, m_pcap(localEndpoint.getName())
, m_srcAddress(localEndpoint.getEthernetAddress())
, m_destAddress(remoteEndpoint)
, m_interfaceName(localEndpoint.getName())
, m_hasRecentlyReceived(false)
#ifdef _DEBUG
, m_nDropped(0)
#endif
{
this->setLocalUri(FaceUri::fromDev(interface.name));
this->setRemoteUri(FaceUri(mcastAddress));
this->setScope(ndn::nfd::FACE_SCOPE_NON_LOCAL);
this->setPersistency(ndn::nfd::FACE_PERSISTENCY_PERMANENT);
this->setLinkType(ndn::nfd::LINK_TYPE_MULTI_ACCESS);
NFD_LOG_FACE_INFO("Creating transport");
pcapInit();
int fd = pcap_get_selectable_fd(m_pcap.get());
if (fd < 0)
BOOST_THROW_EXCEPTION(Error("pcap_get_selectable_fd failed"));
// need to duplicate the fd, otherwise both pcap_close()
// and stream_descriptor::close() will try to close the
// same fd and one of them will fail
m_socket.assign(::dup(fd));
// do this after assigning m_socket because getInterfaceMtu uses it
this->setMtu(getInterfaceMtu());
char filter[110];
// note #1: we cannot use std::snprintf because it's not available
// on some platforms (see #2299)
// note #2: "not vlan" must appear last in the filter expression, or the
// rest of the filter won't work as intended (see pcap-filter(7))
snprintf(filter, sizeof(filter),
"(ether proto 0x%x) && (ether dst %s) && (not ether src %s) && (not vlan)",
ethernet::ETHERTYPE_NDN,
m_destAddress.toString().c_str(),
m_srcAddress.toString().c_str());
setPacketFilter(filter);
if (!m_destAddress.isBroadcast() && !joinMulticastGroup()) {
NFD_LOG_FACE_WARN("Falling back to promiscuous mode");
pcap_set_promisc(m_pcap.get(), 1);
try {
m_pcap.activate(DLT_EN10MB);
m_socket.assign(m_pcap.getFd());
}
catch (const PcapHelper::Error& e) {
BOOST_THROW_EXCEPTION(Error(e.what()));
}
m_socket.async_read_some(boost::asio::null_buffers(),
bind(&EthernetTransport::handleRead, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
asyncRead();
}
void EthernetTransport::doSend(Transport::Packet&& packet)
{
NFD_LOG_FACE_TRACE(__func__);
sendPacket(packet.packet);
}
void EthernetTransport::doClose()
void
EthernetTransport::doClose()
{
NFD_LOG_FACE_TRACE(__func__);
@@ -138,7 +72,7 @@ void EthernetTransport::doClose()
m_socket.cancel(error);
m_socket.close(error);
}
m_pcap.reset();
m_pcap.close();
// Ensure that the Transport stays alive at least
// until all pending handlers are dispatched
@@ -148,116 +82,16 @@ void EthernetTransport::doClose()
}
void
EthernetTransport::pcapInit()
EthernetTransport::doSend(Transport::Packet&& packet)
{
char errbuf[PCAP_ERRBUF_SIZE] = {};
m_pcap.reset(pcap_create(m_interfaceName.c_str(), errbuf));
if (!m_pcap)
BOOST_THROW_EXCEPTION(Error("pcap_create: " + std::string(errbuf)));
NFD_LOG_FACE_TRACE(__func__);
#ifdef HAVE_PCAP_SET_IMMEDIATE_MODE
// Enable "immediate mode", effectively disabling any read buffering in the kernel.
// This corresponds to the BIOCIMMEDIATE ioctl on BSD-like systems (including OS X)
// where libpcap uses a BPF device. On Linux this forces libpcap not to use TPACKET_V3,
// even if the kernel supports it, thus preventing bug #1511.
pcap_set_immediate_mode(m_pcap.get(), 1);
#endif
if (pcap_activate(m_pcap.get()) < 0)
BOOST_THROW_EXCEPTION(Error("pcap_activate failed"));
if (pcap_set_datalink(m_pcap.get(), DLT_EN10MB) < 0)
BOOST_THROW_EXCEPTION(Error("pcap_set_datalink: " + std::string(pcap_geterr(m_pcap.get()))));
if (pcap_setdirection(m_pcap.get(), PCAP_D_IN) < 0)
// no need to throw on failure, BPF will filter unwanted packets anyway
NFD_LOG_FACE_WARN("pcap_setdirection failed: " << pcap_geterr(m_pcap.get()));
}
void
EthernetTransport::setPacketFilter(const char* filterString)
{
bpf_program filter;
if (pcap_compile(m_pcap.get(), &filter, filterString, 1, PCAP_NETMASK_UNKNOWN) < 0)
BOOST_THROW_EXCEPTION(Error("pcap_compile: " + std::string(pcap_geterr(m_pcap.get()))));
int ret = pcap_setfilter(m_pcap.get(), &filter);
pcap_freecode(&filter);
if (ret < 0)
BOOST_THROW_EXCEPTION(Error("pcap_setfilter: " + std::string(pcap_geterr(m_pcap.get()))));
}
bool
EthernetTransport::joinMulticastGroup()
{
#if defined(__linux__)
packet_mreq mr{};
mr.mr_ifindex = m_interfaceIndex;
mr.mr_type = PACKET_MR_MULTICAST;
mr.mr_alen = m_destAddress.size();
std::memcpy(mr.mr_address, m_destAddress.data(), m_destAddress.size());
if (::setsockopt(m_socket.native_handle(), SOL_PACKET,
PACKET_ADD_MEMBERSHIP, &mr, sizeof(mr)) == 0)
return true; // success
NFD_LOG_FACE_WARN("setsockopt(PACKET_ADD_MEMBERSHIP) failed: " << std::strerror(errno));
#endif
#if defined(SIOCADDMULTI)
ifreq ifr{};
std::strncpy(ifr.ifr_name, m_interfaceName.c_str(), sizeof(ifr.ifr_name) - 1);
#if defined(__APPLE__) || defined(__FreeBSD__)
// see bug #2327
using boost::asio::ip::udp;
udp::socket sock(getGlobalIoService(), udp::v4());
int fd = sock.native_handle();
/*
* Differences between Linux and the BSDs (including OS X):
* o BSD does not have ifr_hwaddr; use ifr_addr instead.
* o While OS X seems to accept both AF_LINK and AF_UNSPEC as the address
* family, FreeBSD explicitly requires AF_LINK, so we have to use AF_LINK
* and sockaddr_dl instead of the generic sockaddr structure.
* o BSD's sockaddr (and sockaddr_dl in particular) contains an additional
* field, sa_len (sdl_len), which must be set to the total length of the
* structure, including the length field itself.
* o We do not specify the interface name, thus sdl_nlen is left at 0 and
* LLADDR is effectively the same as sdl_data.
*/
sockaddr_dl* sdl = reinterpret_cast<sockaddr_dl*>(&ifr.ifr_addr);
sdl->sdl_len = sizeof(ifr.ifr_addr);
sdl->sdl_family = AF_LINK;
sdl->sdl_alen = m_destAddress.size();
std::memcpy(LLADDR(sdl), m_destAddress.data(), m_destAddress.size());
static_assert(sizeof(ifr.ifr_addr) >= offsetof(sockaddr_dl, sdl_data) + ethernet::ADDR_LEN,
"ifr_addr in struct ifreq is too small on this platform");
#else
int fd = m_socket.native_handle();
ifr.ifr_hwaddr.sa_family = AF_UNSPEC;
std::memcpy(ifr.ifr_hwaddr.sa_data, m_destAddress.data(), m_destAddress.size());
static_assert(sizeof(ifr.ifr_hwaddr.sa_data) >= ethernet::ADDR_LEN,
"ifr_hwaddr in struct ifreq is too small on this platform");
#endif
if (::ioctl(fd, SIOCADDMULTI, &ifr) == 0)
return true; // success
NFD_LOG_FACE_WARN("ioctl(SIOCADDMULTI) failed: " << std::strerror(errno));
#endif
return false;
sendPacket(packet.packet);
}
void
EthernetTransport::sendPacket(const ndn::Block& block)
{
/// \todo Right now there is no reserve when packet is received, but
/// we should reserve some space at the beginning and at the end
ndn::EncodingBuffer buffer(block);
// pad with zeroes if the payload is too short
@@ -272,139 +106,110 @@ EthernetTransport::sendPacket(const ndn::Block& block)
buffer.prependByteArray(m_srcAddress.data(), m_srcAddress.size());
buffer.prependByteArray(m_destAddress.data(), m_destAddress.size());
// send the packet
int sent = pcap_inject(m_pcap.get(), buffer.buf(), buffer.size());
// send the frame
int sent = pcap_inject(m_pcap, buffer.buf(), buffer.size());
if (sent < 0)
NFD_LOG_FACE_ERROR("pcap_inject failed: " << pcap_geterr(m_pcap.get()));
handleError("Send operation failed: " + m_pcap.getLastError());
else if (static_cast<size_t>(sent) < buffer.size())
NFD_LOG_FACE_ERROR("Failed to send the full frame: bufsize=" << buffer.size() << " sent=" << sent);
handleError("Failed to send the full frame: size=" + to_string(buffer.size()) +
" sent=" + to_string(sent));
else
// print block size because we don't want to count the padding in buffer
NFD_LOG_FACE_TRACE("Successfully sent: " << block.size() << " bytes");
}
void
EthernetTransport::handleRead(const boost::system::error_code& error, size_t)
EthernetTransport::asyncRead()
{
if (error)
return processErrorCode(error);
pcap_pkthdr* header;
const uint8_t* packet;
// read the pcap header and packet data
int ret = pcap_next_ex(m_pcap.get(), &header, &packet);
if (ret < 0)
NFD_LOG_FACE_ERROR("pcap_next_ex failed: " << pcap_geterr(m_pcap.get()));
else if (ret == 0)
NFD_LOG_FACE_WARN("Read timeout");
else
processIncomingPacket(header, packet);
#ifdef _DEBUG
pcap_stat ps{};
ret = pcap_stats(m_pcap.get(), &ps);
if (ret < 0) {
NFD_LOG_FACE_DEBUG("pcap_stats failed: " << pcap_geterr(m_pcap.get()));
}
else if (ret == 0) {
if (ps.ps_drop - m_nDropped > 0)
NFD_LOG_FACE_DEBUG("Detected " << ps.ps_drop - m_nDropped << " dropped packet(s)");
m_nDropped = ps.ps_drop;
}
#endif
m_socket.async_read_some(boost::asio::null_buffers(),
bind(&EthernetTransport::handleRead, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
boost::asio::placeholders::error));
}
void
EthernetTransport::processIncomingPacket(const pcap_pkthdr* header, const uint8_t* packet)
EthernetTransport::handleRead(const boost::system::error_code& error)
{
size_t length = header->caplen;
if (length < ethernet::HDR_LEN + ethernet::MIN_DATA_LEN) {
NFD_LOG_FACE_WARN("Received frame is too short (" << length << " bytes)");
if (error) {
// boost::asio::error::operation_aborted must be checked first: in that case, the Transport
// may already have been destructed, therefore it's unsafe to call getState() or do logging.
if (error != boost::asio::error::operation_aborted &&
getState() != TransportState::CLOSING &&
getState() != TransportState::FAILED &&
getState() != TransportState::CLOSED) {
handleError("Receive operation failed: " + error.message());
}
return;
}
const ether_header* eh = reinterpret_cast<const ether_header*>(packet);
const ethernet::Address sourceAddress(eh->ether_shost);
const uint8_t* pkt;
size_t len;
std::string err;
std::tie(pkt, len, err) = m_pcap.readNextPacket();
// in some cases VLAN-tagged frames may survive the BPF filter,
// make sure we do not process those frames (see #3348)
if (ntohs(eh->ether_type) != ethernet::ETHERTYPE_NDN)
return;
if (pkt == nullptr) {
NFD_LOG_FACE_WARN("Read error: " << err);
}
else {
const ether_header* eh;
std::tie(eh, err) = ethernet::checkFrameHeader(pkt, len, m_srcAddress,
m_destAddress.isMulticast() ? m_destAddress : m_srcAddress);
if (eh == nullptr) {
NFD_LOG_FACE_WARN(err);
}
else {
ethernet::Address sender(eh->ether_shost);
pkt += ethernet::HDR_LEN;
len -= ethernet::HDR_LEN;
receivePayload(pkt, len, sender);
}
}
// check that our BPF filter is working correctly
BOOST_ASSERT_MSG(ethernet::Address(eh->ether_dhost) == m_destAddress,
"Received frame addressed to a different multicast group");
BOOST_ASSERT_MSG(sourceAddress != m_srcAddress,
"Received frame sent by this host");
#ifdef _DEBUG
size_t nDropped = m_pcap.getNDropped();
if (nDropped - m_nDropped > 0)
NFD_LOG_FACE_DEBUG("Detected " << nDropped - m_nDropped << " dropped frame(s)");
m_nDropped = nDropped;
#endif
packet += ethernet::HDR_LEN;
length -= ethernet::HDR_LEN;
asyncRead();
}
void
EthernetTransport::receivePayload(const uint8_t* payload, size_t length,
const ethernet::Address& sender)
{
NFD_LOG_FACE_TRACE("Received: " << length << " bytes from " << sender);
bool isOk = false;
Block element;
std::tie(isOk, element) = Block::fromBuffer(packet, length);
std::tie(isOk, element) = Block::fromBuffer(payload, length);
if (!isOk) {
NFD_LOG_FACE_WARN("Received invalid packet from " << sourceAddress.toString());
NFD_LOG_FACE_WARN("Failed to parse incoming packet from " << sender);
// This packet won't extend the face lifetime
return;
}
NFD_LOG_FACE_TRACE("Received: " << element.size() << " bytes from " << sourceAddress.toString());
m_hasRecentlyReceived = true;
Transport::Packet tp(std::move(element));
static_assert(sizeof(tp.remoteEndpoint) >= ethernet::ADDR_LEN,
"Transport::Packet::remoteEndpoint is too small");
std::memcpy(&tp.remoteEndpoint, sourceAddress.data(), sourceAddress.size());
if (m_destAddress.isMulticast()) {
std::memcpy(&tp.remoteEndpoint, sender.data(), sender.size());
}
this->receive(std::move(tp));
}
void
EthernetTransport::processErrorCode(const boost::system::error_code& error)
EthernetTransport::handleError(const std::string& errorMessage)
{
// boost::asio::error::operation_aborted must be checked first. In that situation, the Transport
// may already have been destructed, and it's unsafe to call getState() or do logging.
if (error == boost::asio::error::operation_aborted ||
getState() == TransportState::CLOSING ||
getState() == TransportState::FAILED ||
getState() == TransportState::CLOSED) {
// transport is shutting down, ignore any errors
if (getPersistency() == ndn::nfd::FACE_PERSISTENCY_PERMANENT) {
NFD_LOG_FACE_DEBUG("Permanent face ignores error: " << errorMessage);
return;
}
NFD_LOG_FACE_WARN("Receive operation failed: " << error.message());
}
size_t
EthernetTransport::getInterfaceMtu()
{
#ifdef SIOCGIFMTU
#if defined(__APPLE__) || defined(__FreeBSD__)
// see bug #2328
using boost::asio::ip::udp;
udp::socket sock(getGlobalIoService(), udp::v4());
int fd = sock.native_handle();
#else
int fd = m_socket.native_handle();
#endif
ifreq ifr{};
std::strncpy(ifr.ifr_name, m_interfaceName.c_str(), sizeof(ifr.ifr_name) - 1);
if (::ioctl(fd, SIOCGIFMTU, &ifr) == 0) {
NFD_LOG_FACE_DEBUG("Interface MTU is " << ifr.ifr_mtu);
return static_cast<size_t>(ifr.ifr_mtu);
}
NFD_LOG_FACE_WARN("Failed to get interface MTU: " << std::strerror(errno));
#endif
NFD_LOG_FACE_DEBUG("Assuming default MTU of " << ethernet::MAX_DATA_LEN);
return ethernet::MAX_DATA_LEN;
NFD_LOG_FACE_ERROR(errorMessage);
this->setState(TransportState::FAILED);
doClose();
}
} // namespace face
+37 -70
View File
@@ -1,5 +1,5 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
/*
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
@@ -26,26 +26,18 @@
#ifndef NFD_DAEMON_FACE_ETHERNET_TRANSPORT_HPP
#define NFD_DAEMON_FACE_ETHERNET_TRANSPORT_HPP
#include "core/common.hpp"
#include "ethernet-protocol.hpp"
#include "pcap-helper.hpp"
#include "transport.hpp"
#include "core/network-interface.hpp"
#ifndef HAVE_LIBPCAP
#error "Cannot include this file when libpcap is not available"
#endif
// forward declarations
struct pcap;
typedef pcap pcap_t;
struct pcap_pkthdr;
#include <ndn-cxx/net/network-interface.hpp>
namespace nfd {
namespace face {
/**
* \brief A multicast Transport that uses raw Ethernet II frames
* @brief Base class for Ethernet-based Transports
*/
class EthernetTransport final : public Transport
class EthernetTransport : public Transport
{
public:
class Error : public std::runtime_error
@@ -59,90 +51,65 @@ public:
};
/**
* @brief Creates an Ethernet-based transport for multicast communication
* @brief Processes the payload of an incoming frame
* @param payload Pointer to the first byte of data after the Ethernet header
* @param length Payload length
* @param sender Sender address
*/
EthernetTransport(const NetworkInterfaceInfo& interface,
const ethernet::Address& mcastAddress);
void
receivePayload(const uint8_t* payload, size_t length,
const ethernet::Address& sender);
protected:
EthernetTransport(const ndn::net::NetworkInterface& localEndpoint,
const ethernet::Address& remoteEndpoint);
void
doClose() final;
bool
hasRecentlyReceived() const
{
return m_hasRecentlyReceived;
}
void
resetRecentlyReceived()
{
m_hasRecentlyReceived = false;
}
private:
void
doSend(Transport::Packet&& packet) final;
/**
* @brief Allocates and initializes a libpcap context for live capture
*/
void
pcapInit();
/**
* @brief Installs a BPF filter on the receiving socket
*
* @param filterString string containing the source BPF program
*/
void
setPacketFilter(const char* filterString);
/**
* @brief Enables receiving frames addressed to our MAC multicast group
*
* @return true if successful, false otherwise
*/
bool
joinMulticastGroup();
/**
* @brief Sends the specified TLV block on the network wrapped in an Ethernet frame
*/
void
sendPacket(const ndn::Block& block);
/**
* @brief Receive callback
*/
void
handleRead(const boost::system::error_code& error, size_t nBytesRead);
asyncRead();
PUBLIC_WITH_TESTS_ELSE_PRIVATE:
/**
* @brief Processes an incoming frame as captured by libpcap
*
* @param header pointer to capture metadata
* @param packet pointer to the received frame, including the link-layer header
*/
void
processIncomingPacket(const pcap_pkthdr* header, const uint8_t* packet);
handleRead(const boost::system::error_code& error);
private:
/**
* @brief Handles errors encountered by Boost.Asio on the receive path
*/
void
processErrorCode(const boost::system::error_code& error);
handleError(const std::string& errorMessage);
/**
* @brief Returns the MTU of the underlying network interface
*/
size_t
getInterfaceMtu();
private:
unique_ptr<pcap_t, void(*)(pcap_t*)> m_pcap;
protected:
boost::asio::posix::stream_descriptor m_socket;
PcapHelper m_pcap;
ethernet::Address m_srcAddress;
ethernet::Address m_destAddress;
std::string m_interfaceName;
#if defined(__linux__)
int m_interfaceIndex;
#endif
private:
bool m_hasRecentlyReceived;
#ifdef _DEBUG
/// number of packets dropped by the kernel, as reported by libpcap
unsigned int m_nDropped;
/// number of frames dropped by the kernel, as reported by libpcap
size_t m_nDropped;
#endif
};
+7 -5
View File
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2014-2015, Regents of the University of California,
/*
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -53,6 +53,9 @@ public:
const T& obj;
};
} // namespace face
} // namespace nfd
/** \defgroup FaceLogging Face logging macros
*
* These macros augment the log message with some face-specific information,
@@ -62,6 +65,7 @@ public:
* @{
*/
/** \cond */
#define NFD_LOG_FACE(level, msg) NFD_LOG_##level( \
::nfd::face::FaceLogHelper< \
typename std::remove_cv< \
@@ -69,6 +73,7 @@ public:
>::type \
>(*this) \
<< msg)
/** \endcond */
/** \brief Log a message at TRACE level */
#define NFD_LOG_FACE_TRACE(msg) NFD_LOG_FACE(TRACE, msg)
@@ -87,7 +92,4 @@ public:
/** @} */
} // namespace face
} // namespace nfd
#endif // NFD_DAEMON_FACE_FACE_LOG_HPP
+13 -6
View File
@@ -1,5 +1,5 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
/*
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
@@ -25,7 +25,7 @@
#include "face-system.hpp"
#include "protocol-factory.hpp"
#include "core/logger.hpp"
#include "core/global-io.hpp"
#include "fw/face-table.hpp"
namespace nfd {
@@ -33,15 +33,24 @@ namespace face {
NFD_LOG_INIT("FaceSystem");
FaceSystem::FaceSystem(FaceTable& faceTable)
FaceSystem::FaceSystem(FaceTable& faceTable, shared_ptr<ndn::net::NetworkMonitor> netmon)
: m_faceTable(faceTable)
, m_netmon(std::move(netmon))
{
auto pfCtorParams = this->makePFCtorParams();
for (const std::string& id : ProtocolFactory::listRegistered()) {
NFD_LOG_TRACE("creating factory " << id);
m_factories[id] = ProtocolFactory::create(id);
m_factories[id] = ProtocolFactory::create(id, pfCtorParams);
}
}
ProtocolFactoryCtorParams
FaceSystem::makePFCtorParams()
{
auto addFace = bind(&FaceTable::add, &m_faceTable, _1);
return {addFace, m_netmon};
}
FaceSystem::~FaceSystem() = default;
std::set<const ProtocolFactory*>
@@ -79,8 +88,6 @@ FaceSystem::processConfig(const ConfigSection& configSection, bool isDryRun, con
{
ConfigContext context;
context.isDryRun = isDryRun;
context.addFace = bind(&FaceTable::add, &m_faceTable, _1);
context.m_netifs = listNetworkInterfaces();
// process sections in protocol factories
for (const auto& pair : m_factories) {
+18 -19
View File
@@ -1,5 +1,5 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
/*
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
@@ -28,8 +28,10 @@
#include "channel.hpp"
#include "core/config-file.hpp"
#include "core/network-interface.hpp"
#include "core/network-interface-predicate.hpp"
#include <ndn-cxx/net/network-address.hpp>
#include <ndn-cxx/net/network-interface.hpp>
#include <ndn-cxx/net/network-monitor.hpp>
namespace nfd {
@@ -38,6 +40,7 @@ class FaceTable;
namespace face {
class ProtocolFactory;
struct ProtocolFactoryCtorParams;
/** \brief entry point of the face system
*
@@ -47,8 +50,7 @@ class ProtocolFactory;
class FaceSystem : noncopyable
{
public:
explicit
FaceSystem(FaceTable& faceTable);
FaceSystem(FaceTable& faceTable, shared_ptr<ndn::net::NetworkMonitor> netmon);
~FaceSystem();
@@ -67,6 +69,12 @@ public:
ProtocolFactory*
getFactoryByScheme(const std::string& scheme);
FaceTable&
getFaceTable()
{
return m_faceTable;
}
/** \brief register handler for face_system section of NFD configuration file
*/
void
@@ -76,25 +84,14 @@ public:
*/
class ConfigContext : noncopyable
{
public:
const std::vector<NetworkInterfaceInfo>&
listNetifs() const
{
///\todo get netifs from NetworkMonitor
return m_netifs;
}
public:
bool isDryRun;
FaceCreatedCallback addFace;
///\todo add NetworkMonitor
private:
std::vector<NetworkInterfaceInfo> m_netifs;
friend class FaceSystem;
};
PUBLIC_WITH_TESTS_ELSE_PRIVATE:
ProtocolFactoryCtorParams
makePFCtorParams();
private:
void
processConfig(const ConfigSection& configSection, bool isDryRun,
@@ -113,6 +110,8 @@ private:
std::map<std::string, ProtocolFactory*> m_factoryByScheme;
FaceTable& m_faceTable;
shared_ptr<ndn::net::NetworkMonitor> m_netmon;
};
} // namespace face
+1 -8
View File
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2014-2016, Regents of the University of California,
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -293,13 +293,6 @@ Face::getCounters() const
std::ostream&
operator<<(std::ostream& os, const FaceLogHelper<Face>& flh);
template<typename T>
typename std::enable_if<std::is_base_of<Face, T>::value, std::ostream&>::type
operator<<(std::ostream& os, const FaceLogHelper<T>& flh)
{
return os << FaceLogHelper<Face>(flh.obj);
}
} // namespace face
using face::FaceId;
+69 -33
View File
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2014-2016, Regents of the University of California,
/*
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -31,11 +31,6 @@ namespace face {
NFD_LOG_INIT("GenericLinkService");
GenericLinkServiceCounters::GenericLinkServiceCounters(const LpReassembler& reassembler)
: nReassembling(reassembler)
{
}
GenericLinkService::Options::Options()
: allowLocalFields(false)
, allowFragmentation(false)
@@ -44,13 +39,48 @@ GenericLinkService::Options::Options()
}
GenericLinkService::GenericLinkService(const GenericLinkService::Options& options)
: GenericLinkServiceCounters(m_reassembler)
, m_options(options)
: m_options(options)
, m_fragmenter(m_options.fragmenterOptions, this)
, m_reassembler(m_options.reassemblerOptions, this)
, m_reliability(m_options.reliabilityOptions, this)
, m_lastSeqNo(-2)
{
m_reassembler.beforeTimeout.connect(bind([this] { ++this->nReassemblyTimeouts; }));
nReassembling.observe(&m_reassembler);
}
void
GenericLinkService::setOptions(const GenericLinkService::Options& options)
{
m_options = options;
m_fragmenter.setOptions(m_options.fragmenterOptions);
m_reassembler.setOptions(m_options.reassemblerOptions);
m_reliability.setOptions(m_options.reliabilityOptions);
}
void
GenericLinkService::requestIdlePacket()
{
// No need to request Acks to attach to this packet from LpReliability, as they are already
// attached in sendLpPacket
this->sendLpPacket({});
}
void
GenericLinkService::sendLpPacket(lp::Packet&& pkt)
{
const ssize_t mtu = this->getTransport()->getMtu();
if (m_options.reliabilityOptions.isEnabled) {
m_reliability.piggyback(pkt, mtu);
}
Transport::Packet tp(pkt.wireEncode());
if (mtu != MTU_UNLIMITED && tp.packet.size() > static_cast<size_t>(mtu)) {
++this->nOutOverMtu;
NFD_LOG_FACE_WARN("attempted to send packet over MTU limit");
return;
}
this->sendPacket(std::move(tp));
}
void
@@ -104,7 +134,14 @@ void
GenericLinkService::sendNetPacket(lp::Packet&& pkt)
{
std::vector<lp::Packet> frags;
const ssize_t mtu = this->getTransport()->getMtu();
ssize_t mtu = this->getTransport()->getMtu();
// Make space for feature fields in fragments
if (m_options.reliabilityOptions.isEnabled && mtu != MTU_UNLIMITED) {
mtu -= LpReliability::RESERVED_HEADER_SPACE;
BOOST_ASSERT(mtu > 0);
}
if (m_options.allowFragmentation && mtu != MTU_UNLIMITED) {
bool isOk = false;
std::tie(isOk, frags) = m_fragmenter.fragmentPacket(pkt, mtu);
@@ -115,29 +152,28 @@ GenericLinkService::sendNetPacket(lp::Packet&& pkt)
}
}
else {
frags.push_back(pkt);
frags.push_back(std::move(pkt));
}
if (frags.size() > 1) {
// sequence is needed only if packet is fragmented
this->assignSequences(frags);
}
else {
if (frags.size() == 1) {
// even if indexed fragmentation is enabled, the fragmenter should not
// fragment the packet if it can fit in MTU
BOOST_ASSERT(frags.size() > 0);
BOOST_ASSERT(!frags.front().has<lp::FragIndexField>());
BOOST_ASSERT(!frags.front().has<lp::FragCountField>());
}
for (const lp::Packet& frag : frags) {
Transport::Packet tp(frag.wireEncode());
if (mtu != MTU_UNLIMITED && tp.packet.size() > static_cast<size_t>(mtu)) {
++this->nOutOverMtu;
NFD_LOG_FACE_WARN("attempt to send packet over MTU limit");
continue;
}
this->sendPacket(std::move(tp));
// Only assign sequences to fragments if packet contains more than 1 fragment
if (frags.size() > 1) {
// Assign sequences to all fragments
this->assignSequences(frags);
}
if (m_options.reliabilityOptions.isEnabled && frags.front().has<lp::FragmentField>()) {
m_reliability.handleOutgoing(frags);
}
for (lp::Packet& frag : frags) {
this->sendLpPacket(std::move(frag));
}
}
@@ -159,6 +195,10 @@ GenericLinkService::doReceivePacket(Transport::Packet&& packet)
try {
lp::Packet pkt(packet.packet);
if (m_options.reliabilityOptions.isEnabled) {
m_reliability.processIncomingPacket(pkt);
}
if (!pkt.has<lp::FragmentField>()) {
NFD_LOG_FACE_TRACE("received IDLE packet: DROP");
return;
@@ -270,14 +310,10 @@ GenericLinkService::decodeData(const Block& netPkt, const lp::Packet& firstPkt)
}
if (firstPkt.has<lp::CachePolicyField>()) {
if (m_options.allowLocalFields) {
// In case of an invalid CachePolicyType, get<lp::CachePolicyField> will throw,
// so it's unnecessary to check here.
data->setTag(make_shared<lp::CachePolicyTag>(firstPkt.get<lp::CachePolicyField>()));
}
else {
NFD_LOG_FACE_WARN("received CachePolicy, but local fields disabled: IGNORE");
}
// CachePolicy is unprivileged and does not require allowLocalFields option.
// In case of an invalid CachePolicyType, get<lp::CachePolicyField> will throw,
// so it's unnecessary to check here.
data->setTag(make_shared<lp::CachePolicyTag>(firstPkt.get<lp::CachePolicyField>()));
}
if (firstPkt.has<lp::IncomingFaceIdField>()) {
+40 -18
View File
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2014-2016, Regents of the University of California,
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -26,12 +26,10 @@
#ifndef NFD_DAEMON_FACE_GENERIC_LINK_SERVICE_HPP
#define NFD_DAEMON_FACE_GENERIC_LINK_SERVICE_HPP
#include "core/common.hpp"
#include "core/logger.hpp"
#include "link-service.hpp"
#include "lp-fragmenter.hpp"
#include "lp-reassembler.hpp"
#include "lp-reliability.hpp"
namespace nfd {
namespace face {
@@ -43,9 +41,6 @@ namespace face {
class GenericLinkServiceCounters : public virtual LinkService::Counters
{
public:
explicit
GenericLinkServiceCounters(const LpReassembler& reassembler);
/** \brief count of failed fragmentations
*/
PacketCounter nFragmentationErrors;
@@ -71,10 +66,24 @@ public:
/** \brief count of invalid reassembled network-layer packets dropped
*/
PacketCounter nInNetInvalid;
/** \brief count of network-layer packets that did not require retransmission of a fragment
*/
PacketCounter nAcknowledged;
/** \brief count of network-layer packets that had at least one fragment retransmitted, but were
* eventually received in full
*/
PacketCounter nRetransmitted;
/** \brief count of network-layer packets dropped because a fragment reached the maximum number
* of retransmissions
*/
PacketCounter nRetxExhausted;
};
/** \brief GenericLinkService is a LinkService that implements the NDNLPv2 protocol
* \sa http://redmine.named-data.net/projects/nfd/wiki/NDNLPv2
* \sa https://redmine.named-data.net/projects/nfd/wiki/NDNLPv2
*/
class GenericLinkService : public LinkService
, protected virtual GenericLinkServiceCounters
@@ -107,11 +116,15 @@ public:
/** \brief options for reassembly
*/
LpReassembler::Options reassemblerOptions;
/** \brief options for reliability
*/
LpReliability::Options reliabilityOptions;
};
/** \brief counters provided by GenericLinkService
*/
typedef GenericLinkServiceCounters Counters;
using Counters = GenericLinkServiceCounters;
explicit
GenericLinkService(const Options& options = Options());
@@ -126,10 +139,21 @@ public:
void
setOptions(const Options& options);
virtual const Counters&
const Counters&
getCounters() const override;
private: // send path
PROTECTED_WITH_TESTS_ELSE_PRIVATE: // send path
/** \brief request an IDLE packet to transmit pending service fields
*/
void
requestIdlePacket();
/** \brief send an LpPacket fragment
* \param pkt LpPacket to send
*/
void
sendLpPacket(lp::Packet&& pkt);
/** \brief send Interest
*/
void
@@ -145,6 +169,7 @@ private: // send path
void
doSendNack(const ndn::lp::Nack& nack) override;
private: // send path
/** \brief encode link protocol fields from tags onto an outgoing LpPacket
* \param netPkt network-layer packet to extract tags from
* \param lpPacket LpPacket to add link protocol fields to
@@ -220,11 +245,14 @@ private: // receive path
void
decodeNack(const Block& netPkt, const lp::Packet& firstPkt);
private:
PROTECTED_WITH_TESTS_ELSE_PRIVATE:
Options m_options;
LpFragmenter m_fragmenter;
LpReassembler m_reassembler;
LpReliability m_reliability;
lp::Sequence m_lastSeqNo;
friend class LpReliability;
};
inline const GenericLinkService::Options&
@@ -233,12 +261,6 @@ GenericLinkService::getOptions() const
return m_options;
}
inline void
GenericLinkService::setOptions(const GenericLinkService::Options& options)
{
m_options = options;
}
inline const GenericLinkService::Counters&
GenericLinkService::getCounters() const
{
+5 -2
View File
@@ -1,5 +1,5 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
/*
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
@@ -37,6 +37,9 @@ namespace face {
class InternalTransportBase
{
public:
virtual
~InternalTransportBase() = default;
/** \brief causes the transport to receive a link-layer packet
*/
virtual void
@@ -50,7 +53,7 @@ protected:
/** \brief implements a forwarder-side transport that can be paired with another
*/
class InternalForwarderTransport : public face::Transport, public InternalTransportBase
class InternalForwarderTransport : public Transport, public InternalTransportBase
{
public:
InternalForwarderTransport(const FaceUri& localUri = FaceUri("internal://"),
+341
View File
@@ -0,0 +1,341 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
* Washington University in St. Louis,
* Beijing Institute of Technology,
* The University of Memphis.
*
* This file is part of NFD (Named Data Networking Forwarding Daemon).
* See AUTHORS.md for complete list of NFD authors and contributors.
*
* NFD is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Foundation,
* either version 3 of the License, or (at your option) any later version.
*
* NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
*/
#include "lp-reliability.hpp"
#include "generic-link-service.hpp"
#include "transport.hpp"
namespace nfd {
namespace face {
LpReliability::LpReliability(const LpReliability::Options& options, GenericLinkService* linkService)
: m_options(options)
, m_linkService(linkService)
, m_firstUnackedFrag(m_unackedFrags.begin())
, m_lastTxSeqNo(-1) // set to "-1" to start TxSequence numbers at 0
, m_isIdleAckTimerRunning(false)
{
BOOST_ASSERT(m_linkService != nullptr);
BOOST_ASSERT(m_options.idleAckTimerPeriod > time::nanoseconds::zero());
}
void
LpReliability::setOptions(const Options& options)
{
BOOST_ASSERT(options.idleAckTimerPeriod > time::nanoseconds::zero());
if (m_options.isEnabled && !options.isEnabled) {
this->stopIdleAckTimer();
}
m_options = options;
}
const GenericLinkService*
LpReliability::getLinkService() const
{
return m_linkService;
}
void
LpReliability::handleOutgoing(std::vector<lp::Packet>& frags)
{
BOOST_ASSERT(m_options.isEnabled);
auto unackedFragsIt = m_unackedFrags.begin();
auto sendTime = time::steady_clock::now();
auto netPkt = make_shared<NetPkt>();
netPkt->unackedFrags.reserve(frags.size());
for (lp::Packet& frag : frags) {
// Assign TxSequence number
lp::Sequence txSeq = assignTxSequence(frag);
// Store LpPacket for future retransmissions
unackedFragsIt = m_unackedFrags.emplace_hint(unackedFragsIt,
std::piecewise_construct,
std::forward_as_tuple(txSeq),
std::forward_as_tuple(frag));
unackedFragsIt->second.sendTime = sendTime;
unackedFragsIt->second.rtoTimer =
scheduler::schedule(m_rto.computeRto(), bind(&LpReliability::onLpPacketLost, this, unackedFragsIt));
unackedFragsIt->second.netPkt = netPkt;
if (m_unackedFrags.size() == 1) {
m_firstUnackedFrag = m_unackedFrags.begin();
}
// Add to associated NetPkt
netPkt->unackedFrags.push_back(unackedFragsIt);
}
}
void
LpReliability::processIncomingPacket(const lp::Packet& pkt)
{
BOOST_ASSERT(m_options.isEnabled);
auto now = time::steady_clock::now();
// Extract and parse Acks
for (lp::Sequence ackSeq : pkt.list<lp::AckField>()) {
auto fragIt = m_unackedFrags.find(ackSeq);
if (fragIt == m_unackedFrags.end()) {
// Ignore an Ack for an unknown TxSequence number
continue;
}
auto& frag = fragIt->second;
// Cancel the RTO timer for the acknowledged fragment
frag.rtoTimer.cancel();
if (frag.retxCount == 0) {
// This sequence had no retransmissions, so use it to calculate the RTO
m_rto.addMeasurement(time::duration_cast<RttEstimator::Duration>(now - frag.sendTime));
}
// Look for frags with TxSequence numbers < ackSeq (allowing for wraparound) and consider them
// lost if a configurable number of Acks containing greater TxSequence numbers have been
// received.
auto lostLpPackets = findLostLpPackets(fragIt);
// Remove the fragment from the map of unacknowledged fragments and from its associated network
// packet. Potentially increment the start of the window.
onLpPacketAcknowledged(fragIt);
// Resend or fail fragments considered lost. Potentially increment the start of the window.
for (UnackedFrags::iterator txSeqIt : lostLpPackets) {
this->onLpPacketLost(txSeqIt);
}
}
// If packet has Fragment and TxSequence fields, extract TxSequence and add to AckQueue
if (pkt.has<lp::FragmentField>() && pkt.has<lp::TxSequenceField>()) {
m_ackQueue.push(pkt.get<lp::TxSequenceField>());
if (!m_isIdleAckTimerRunning) {
this->startIdleAckTimer();
}
}
}
void
LpReliability::piggyback(lp::Packet& pkt, ssize_t mtu)
{
BOOST_ASSERT(m_options.isEnabled);
BOOST_ASSERT(pkt.wireEncode().type() == lp::tlv::LpPacket);
// up to 2 extra octets reserved for potential TLV-LENGTH size increases
ssize_t pktSize = pkt.wireEncode().size();
ssize_t reservedSpace = tlv::sizeOfVarNumber(ndn::MAX_NDN_PACKET_SIZE) -
tlv::sizeOfVarNumber(pktSize);
ssize_t remainingSpace = (mtu == MTU_UNLIMITED ? ndn::MAX_NDN_PACKET_SIZE : mtu) - reservedSpace;
remainingSpace -= pktSize;
while (!m_ackQueue.empty()) {
lp::Sequence ackSeq = m_ackQueue.front();
// Ack Size = Ack Type (3 octets) + Ack Length (1 octet) + Value (1, 2, 4, or 8 octets)
ssize_t ackSize = tlv::sizeOfVarNumber(lp::tlv::Ack) +
tlv::sizeOfVarNumber(
tlv::sizeOfNonNegativeInteger(std::numeric_limits<lp::Sequence>::max())) +
tlv::sizeOfNonNegativeInteger(ackSeq);
if (ackSize > remainingSpace) {
break;
}
pkt.add<lp::AckField>(ackSeq);
m_ackQueue.pop();
remainingSpace -= ackSize;
}
}
lp::Sequence
LpReliability::assignTxSequence(lp::Packet& frag)
{
lp::Sequence txSeq = ++m_lastTxSeqNo;
frag.set<lp::TxSequenceField>(txSeq);
if (m_unackedFrags.size() > 0 && m_lastTxSeqNo == m_firstUnackedFrag->first) {
BOOST_THROW_EXCEPTION(std::length_error("TxSequence range exceeded"));
}
return m_lastTxSeqNo;
}
void
LpReliability::startIdleAckTimer()
{
BOOST_ASSERT(!m_isIdleAckTimerRunning);
m_isIdleAckTimerRunning = true;
m_idleAckTimer = scheduler::schedule(m_options.idleAckTimerPeriod, [this] {
while (!m_ackQueue.empty()) {
m_linkService->requestIdlePacket();
}
m_isIdleAckTimerRunning = false;
});
}
void
LpReliability::stopIdleAckTimer()
{
m_idleAckTimer.cancel();
m_isIdleAckTimerRunning = false;
}
std::vector<LpReliability::UnackedFrags::iterator>
LpReliability::findLostLpPackets(LpReliability::UnackedFrags::iterator ackIt)
{
std::vector<UnackedFrags::iterator> lostLpPackets;
for (auto it = m_firstUnackedFrag; ; ++it) {
if (it == m_unackedFrags.end()) {
it = m_unackedFrags.begin();
}
if (it->first == ackIt->first) {
break;
}
auto& unackedFrag = it->second;
unackedFrag.nGreaterSeqAcks++;
if (unackedFrag.nGreaterSeqAcks >= m_options.seqNumLossThreshold) {
lostLpPackets.push_back(it);
}
}
return lostLpPackets;
}
void
LpReliability::onLpPacketLost(UnackedFrags::iterator txSeqIt)
{
BOOST_ASSERT(m_unackedFrags.count(txSeqIt->first) > 0);
auto& txFrag = txSeqIt->second;
txFrag.rtoTimer.cancel();
auto netPkt = txFrag.netPkt;
// Check if maximum number of retransmissions exceeded
if (txFrag.retxCount >= m_options.maxRetx) {
// Delete all LpPackets of NetPkt from m_unackedFrags (except this one)
for (size_t i = 0; i < netPkt->unackedFrags.size(); i++) {
if (netPkt->unackedFrags[i] != txSeqIt) {
deleteUnackedFrag(netPkt->unackedFrags[i]);
}
}
++m_linkService->nRetxExhausted;
deleteUnackedFrag(txSeqIt);
}
else {
// Assign new TxSequence
lp::Sequence newTxSeq = assignTxSequence(txFrag.pkt);
// Move fragment to new TxSequence mapping
auto newTxFragIt = m_unackedFrags.emplace_hint(
m_firstUnackedFrag != m_unackedFrags.end() && m_firstUnackedFrag->first > newTxSeq
? m_firstUnackedFrag
: m_unackedFrags.end(),
std::piecewise_construct,
std::forward_as_tuple(newTxSeq),
std::forward_as_tuple(txFrag.pkt));
auto& newTxFrag = newTxFragIt->second;
newTxFrag.retxCount = txFrag.retxCount + 1;
newTxFrag.netPkt = netPkt;
// Update associated NetPkt
auto fragInNetPkt = std::find(netPkt->unackedFrags.begin(), netPkt->unackedFrags.end(), txSeqIt);
BOOST_ASSERT(fragInNetPkt != netPkt->unackedFrags.end());
*fragInNetPkt = newTxFragIt;
deleteUnackedFrag(txSeqIt);
// Retransmit fragment
m_linkService->sendLpPacket(lp::Packet(newTxFrag.pkt));
// Start RTO timer for this sequence
newTxFrag.rtoTimer = scheduler::schedule(m_rto.computeRto(),
bind(&LpReliability::onLpPacketLost, this, newTxFragIt));
}
}
void
LpReliability::onLpPacketAcknowledged(UnackedFrags::iterator fragIt)
{
auto netPkt = fragIt->second.netPkt;
// Remove from NetPkt unacked fragment list
auto fragInNetPkt = std::find(netPkt->unackedFrags.begin(), netPkt->unackedFrags.end(), fragIt);
BOOST_ASSERT(fragInNetPkt != netPkt->unackedFrags.end());
*fragInNetPkt = netPkt->unackedFrags.back();
netPkt->unackedFrags.pop_back();
// Check if network-layer packet completely received. If so, increment counters
if (netPkt->unackedFrags.empty()) {
if (netPkt->didRetx) {
++m_linkService->nRetransmitted;
}
else {
++m_linkService->nAcknowledged;
}
}
deleteUnackedFrag(fragIt);
}
void
LpReliability::deleteUnackedFrag(UnackedFrags::iterator fragIt)
{
lp::Sequence firstUnackedTxSeq = m_firstUnackedFrag->first;
lp::Sequence currentTxSeq = fragIt->first;
auto nextFragIt = m_unackedFrags.erase(fragIt);
if (!m_unackedFrags.empty() && firstUnackedTxSeq == currentTxSeq) {
// If "first" fragment in send window (allowing for wraparound), increment window begin
if (nextFragIt == m_unackedFrags.end()) {
m_firstUnackedFrag = m_unackedFrags.begin();
}
else {
m_firstUnackedFrag = nextFragIt;
}
}
else if (m_unackedFrags.empty()) {
m_firstUnackedFrag = m_unackedFrags.end();
}
}
LpReliability::UnackedFrag::UnackedFrag(lp::Packet pkt)
: pkt(std::move(pkt))
, sendTime(time::steady_clock::now())
, retxCount(0)
, nGreaterSeqAcks(0)
{
}
} // namespace face
} // namespace nfd
+213
View File
@@ -0,0 +1,213 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
* Washington University in St. Louis,
* Beijing Institute of Technology,
* The University of Memphis.
*
* This file is part of NFD (Named Data Networking Forwarding Daemon).
* See AUTHORS.md for complete list of NFD authors and contributors.
*
* NFD is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Foundation,
* either version 3 of the License, or (at your option) any later version.
*
* NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef NFD_DAEMON_FACE_LP_RELIABILITY_HPP
#define NFD_DAEMON_FACE_LP_RELIABILITY_HPP
#include "core/rtt-estimator.hpp"
#include "core/scheduler.hpp"
#include <ndn-cxx/lp/packet.hpp>
#include <ndn-cxx/lp/sequence.hpp>
#include <queue>
namespace nfd {
namespace face {
class GenericLinkService;
/** \brief provides for reliable sending and receiving of link-layer packets
* \sa https://redmine.named-data.net/projects/nfd/wiki/NDNLPv2
*/
class LpReliability : noncopyable
{
public:
struct Options
{
/** \brief enables link-layer reliability
*/
bool isEnabled = false;
/** \brief maximum number of retransmissions for an LpPacket
*/
size_t maxRetx = 3;
/** \brief period between sending pending Acks in an IDLE packet
*/
time::nanoseconds idleAckTimerPeriod = time::milliseconds(5);
/** \brief a fragment is considered lost if this number of fragments with greater sequence
* numbers are acknowledged
*/
size_t seqNumLossThreshold = 3;
};
LpReliability(const Options& options, GenericLinkService* linkService);
/** \brief set options for reliability
*/
void
setOptions(const Options& options);
/** \return GenericLinkService that owns this instance
*
* This is only used for logging, and may be nullptr.
*/
const GenericLinkService*
getLinkService() const;
/** \brief observe outgoing fragment(s) of a network packet and store for potential retransmission
* \param frags fragments of network packet
*/
void
handleOutgoing(std::vector<lp::Packet>& frags);
/** \brief extract and parse all Acks and add Ack for contained Fragment (if any) to AckQueue
* \param pkt incoming LpPacket
*/
void
processIncomingPacket(const lp::Packet& pkt);
/** \brief called by GenericLinkService to attach Acks onto an outgoing LpPacket
* \param pkt outgoing LpPacket to attach Acks to
* \param mtu MTU of the Transport
*/
void
piggyback(lp::Packet& pkt, ssize_t mtu);
PUBLIC_WITH_TESTS_ELSE_PRIVATE:
class UnackedFrag;
class NetPkt;
using UnackedFrags = std::map<lp::Sequence, UnackedFrag>;
PUBLIC_WITH_TESTS_ELSE_PRIVATE:
/** \brief assign TxSequence number to a fragment
* \param frag fragment to assign TxSequence to
* \return assigned TxSequence number
* \throw std::length_error assigned TxSequence is equal to the start of the existing window
*/
lp::Sequence
assignTxSequence(lp::Packet& frag);
/** \brief start the idle Ack timer
*
* This timer requests an IDLE packet to acknowledge pending fragments not already piggybacked.
* It is called regularly on a period configured in Options::idleAckTimerPeriod. This allows Acks
* to be returned to the sender, even if the link goes idle.
*/
void
startIdleAckTimer();
/** \brief cancel the idle Ack timer
*/
void
stopIdleAckTimer();
/** \brief find and mark as lost fragments where a configurable number of Acks
* (\p m_options.seqNumLossThreshold) have been received for greater TxSequence numbers
* \param ackIt iterator pointing to acknowledged fragment
* \return vector containing iterators to fragments marked lost by this mechanism
*/
std::vector<UnackedFrags::iterator>
findLostLpPackets(UnackedFrags::iterator ackIt);
/** \brief resend (or give up on) a lost fragment
*/
void
onLpPacketLost(UnackedFrags::iterator txSeqIt);
/** \brief remove the fragment with the given sequence number from the map of unacknowledged
* fragments, as well as its associated network packet (if any)
* \param fragIt iterator to acknowledged fragment
*
* If the given TxSequence marks the beginning of the send window, the window will be incremented.
* If the associated network packet has been fully transmitted, it will be removed.
*/
void
onLpPacketAcknowledged(UnackedFrags::iterator fragIt);
/** \brief delete a fragment from UnackedFrags and advance acknowledge window if necessary
* \param fragIt iterator to an UnackedFrag, must be dereferencable
* \post fragIt is not in m_unackedFrags
* \post if was equal to m_firstUnackedFrag,
* m_firstUnackedFrag is set to the UnackedFrag after fragIt with consideration of
* TxSequence number wraparound, or set to m_unackedFrags.end() if m_unackedFrags is empty
*/
void
deleteUnackedFrag(UnackedFrags::iterator fragIt);
PUBLIC_WITH_TESTS_ELSE_PRIVATE:
/** \brief contains a sent fragment that has not been acknowledged and associated data
*/
class UnackedFrag
{
public:
explicit
UnackedFrag(lp::Packet pkt);
public:
lp::Packet pkt;
scheduler::ScopedEventId rtoTimer;
time::steady_clock::TimePoint sendTime;
size_t retxCount;
size_t nGreaterSeqAcks; //!< number of Acks received for sequences greater than this fragment
shared_ptr<NetPkt> netPkt;
};
/** \brief contains a network-layer packet with unacknowledged fragments
*/
class NetPkt
{
public:
std::vector<UnackedFrags::iterator> unackedFrags;
bool didRetx = false;
};
public:
/// TxSequence TLV-TYPE (3 octets) + TxSequence TLV-LENGTH (1 octet) + sizeof(lp::Sequence)
static constexpr size_t RESERVED_HEADER_SPACE = 3 + 1 + sizeof(lp::Sequence);
PUBLIC_WITH_TESTS_ELSE_PRIVATE:
Options m_options;
GenericLinkService* m_linkService;
UnackedFrags m_unackedFrags;
/** An iterator that points to the first unacknowledged fragment in the current window. The window
* can wrap around so that the beginning of the window is at a TxSequence greater than other
* fragments in the window. When the window is moved past the last item in the iterator, the
* first fragment in the map will become the start of the window.
*/
UnackedFrags::iterator m_firstUnackedFrag;
std::queue<lp::Sequence> m_ackQueue;
lp::Sequence m_lastTxSeqNo;
scheduler::ScopedEventId m_idleAckTimer;
bool m_isIdleAckTimerRunning;
RttEstimator m_rto;
};
} // namespace face
} // namespace nfd
#endif // NFD_DAEMON_FACE_LP_RELIABILITY_HPP
@@ -0,0 +1,151 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
* Washington University in St. Louis,
* Beijing Institute of Technology,
* The University of Memphis.
*
* This file is part of NFD (Named Data Networking Forwarding Daemon).
* See AUTHORS.md for complete list of NFD authors and contributors.
*
* NFD is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Foundation,
* either version 3 of the License, or (at your option) any later version.
*
* NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
*/
#include "multicast-ethernet-transport.hpp"
#include "core/global-io.hpp"
#include <cerrno> // for errno
#include <cstring> // for memcpy(), strerror(), strncpy()
#include <net/if.h> // for struct ifreq
#include <stdio.h> // for snprintf()
#include <sys/ioctl.h> // for ioctl()
#if defined(__linux__)
#include <netpacket/packet.h> // for struct packet_mreq
#include <sys/socket.h> // for setsockopt()
#endif
#ifdef SIOCADDMULTI
#if defined(__APPLE__) || defined(__FreeBSD__)
#include <net/if_dl.h> // for struct sockaddr_dl
#endif
#endif
namespace nfd {
namespace face {
NFD_LOG_INIT("MulticastEthernetTransport");
MulticastEthernetTransport::MulticastEthernetTransport(const ndn::net::NetworkInterface& localEndpoint,
const ethernet::Address& mcastAddress,
ndn::nfd::LinkType linkType)
: EthernetTransport(localEndpoint, mcastAddress)
#if defined(__linux__)
, m_interfaceIndex(localEndpoint.getIndex())
#endif
{
this->setLocalUri(FaceUri::fromDev(m_interfaceName));
this->setRemoteUri(FaceUri(m_destAddress));
this->setScope(ndn::nfd::FACE_SCOPE_NON_LOCAL);
this->setPersistency(ndn::nfd::FACE_PERSISTENCY_PERMANENT);
this->setLinkType(linkType);
this->setMtu(localEndpoint.getMtu());
NFD_LOG_FACE_INFO("Creating transport");
char filter[110];
// note #1: we cannot use std::snprintf because it's not available
// on some platforms (see #2299)
// note #2: "not vlan" must appear last in the filter expression, or the
// rest of the filter won't work as intended (see pcap-filter(7))
snprintf(filter, sizeof(filter),
"(ether proto 0x%x) && (ether dst %s) && (not ether src %s) && (not vlan)",
ethernet::ETHERTYPE_NDN,
m_destAddress.toString().data(),
m_srcAddress.toString().data());
m_pcap.setPacketFilter(filter);
BOOST_ASSERT(m_destAddress.isMulticast());
if (!m_destAddress.isBroadcast())
joinMulticastGroup();
}
void
MulticastEthernetTransport::joinMulticastGroup()
{
#if defined(__linux__)
packet_mreq mr{};
mr.mr_ifindex = m_interfaceIndex;
mr.mr_type = PACKET_MR_MULTICAST;
mr.mr_alen = m_destAddress.size();
std::memcpy(mr.mr_address, m_destAddress.data(), m_destAddress.size());
if (::setsockopt(m_socket.native_handle(), SOL_PACKET,
PACKET_ADD_MEMBERSHIP, &mr, sizeof(mr)) == 0)
return; // success
NFD_LOG_FACE_WARN("setsockopt(PACKET_ADD_MEMBERSHIP) failed: " << std::strerror(errno));
#endif
#if defined(SIOCADDMULTI)
ifreq ifr{};
std::strncpy(ifr.ifr_name, m_interfaceName.data(), sizeof(ifr.ifr_name) - 1);
#if defined(__APPLE__) || defined(__FreeBSD__)
// see bug #2327
using boost::asio::ip::udp;
udp::socket sock(getGlobalIoService(), udp::v4());
int fd = sock.native_handle();
// Differences between Linux and the BSDs (including macOS):
// o BSD does not have ifr_hwaddr; use ifr_addr instead.
// o While macOS seems to accept both AF_LINK and AF_UNSPEC as the address
// family, FreeBSD explicitly requires AF_LINK, so we have to use AF_LINK
// and sockaddr_dl instead of the generic sockaddr structure.
// o BSD's sockaddr (and sockaddr_dl in particular) contains an additional
// field, sa_len (sdl_len), which must be set to the total length of the
// structure, including the length field itself.
// o We do not specify the interface name, thus sdl_nlen is left at 0 and
// LLADDR is effectively the same as sdl_data.
sockaddr_dl* sdl = reinterpret_cast<sockaddr_dl*>(&ifr.ifr_addr);
sdl->sdl_len = sizeof(ifr.ifr_addr);
sdl->sdl_family = AF_LINK;
sdl->sdl_alen = m_destAddress.size();
std::memcpy(LLADDR(sdl), m_destAddress.data(), m_destAddress.size());
static_assert(sizeof(ifr.ifr_addr) >= offsetof(sockaddr_dl, sdl_data) + ethernet::ADDR_LEN,
"ifr_addr in struct ifreq is too small on this platform");
#else
int fd = m_socket.native_handle();
ifr.ifr_hwaddr.sa_family = AF_UNSPEC;
std::memcpy(ifr.ifr_hwaddr.sa_data, m_destAddress.data(), m_destAddress.size());
static_assert(sizeof(ifr.ifr_hwaddr.sa_data) >= ethernet::ADDR_LEN,
"ifr_hwaddr in struct ifreq is too small on this platform");
#endif
if (::ioctl(fd, SIOCADDMULTI, &ifr) == 0)
return; // success
NFD_LOG_FACE_WARN("ioctl(SIOCADDMULTI) failed: " << std::strerror(errno));
#endif
BOOST_THROW_EXCEPTION(Error("Failed to join multicast group"));
}
} // namespace face
} // namespace nfd
@@ -0,0 +1,63 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
* Washington University in St. Louis,
* Beijing Institute of Technology,
* The University of Memphis.
*
* This file is part of NFD (Named Data Networking Forwarding Daemon).
* See AUTHORS.md for complete list of NFD authors and contributors.
*
* NFD is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Foundation,
* either version 3 of the License, or (at your option) any later version.
*
* NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef NFD_DAEMON_FACE_MULTICAST_ETHERNET_TRANSPORT_HPP
#define NFD_DAEMON_FACE_MULTICAST_ETHERNET_TRANSPORT_HPP
#include "ethernet-transport.hpp"
namespace nfd {
namespace face {
/**
* @brief A multicast Transport that uses raw Ethernet II frames
*/
class MulticastEthernetTransport final : public EthernetTransport
{
public:
/**
* @brief Creates an Ethernet-based transport for multicast communication
*/
MulticastEthernetTransport(const ndn::net::NetworkInterface& localEndpoint,
const ethernet::Address& mcastAddress,
ndn::nfd::LinkType linkType);
private:
/**
* @brief Enables receiving frames addressed to our MAC multicast group
*/
void
joinMulticastGroup();
private:
#if defined(__linux__)
int m_interfaceIndex;
#endif
};
} // namespace face
} // namespace nfd
#endif // NFD_DAEMON_FACE_MULTICAST_ETHERNET_TRANSPORT_HPP
+3 -2
View File
@@ -35,7 +35,8 @@ NFD_LOG_INCLASS_2TEMPLATE_SPECIALIZATION_DEFINE(DatagramTransport, MulticastUdpT
MulticastUdpTransport::MulticastUdpTransport(const protocol::endpoint& localEndpoint,
const protocol::endpoint& multicastGroup,
protocol::socket&& recvSocket,
protocol::socket&& sendSocket)
protocol::socket&& sendSocket,
ndn::nfd::LinkType linkType)
: DatagramTransport(std::move(recvSocket))
, m_multicastGroup(multicastGroup)
, m_sendSocket(std::move(sendSocket))
@@ -44,7 +45,7 @@ MulticastUdpTransport::MulticastUdpTransport(const protocol::endpoint& localEndp
this->setRemoteUri(FaceUri(multicastGroup));
this->setScope(ndn::nfd::FACE_SCOPE_NON_LOCAL);
this->setPersistency(ndn::nfd::FACE_PERSISTENCY_PERMANENT);
this->setLinkType(ndn::nfd::LINK_TYPE_MULTI_ACCESS);
this->setLinkType(linkType);
this->setMtu(udp::computeMtu(localEndpoint));
NFD_LOG_FACE_INFO("Creating transport");
+3 -1
View File
@@ -51,11 +51,13 @@ public:
* \param multicastGroup multicast group
* \param recvSocket socket used to receive packets
* \param sendSocket socket used to send to the multicast group
* \param linkType either LINK_TYPE_MULTI_ACCESS or LINK_TYPE_AD_HOC
*/
MulticastUdpTransport(const protocol::endpoint& localEndpoint,
const protocol::endpoint& multicastGroup,
protocol::socket&& recvSocket,
protocol::socket&& sendSocket);
protocol::socket&& sendSocket,
ndn::nfd::LinkType linkType);
private:
void
+141
View File
@@ -0,0 +1,141 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
* Washington University in St. Louis,
* Beijing Institute of Technology,
* The University of Memphis.
*
* This file is part of NFD (Named Data Networking Forwarding Daemon).
* See AUTHORS.md for complete list of NFD authors and contributors.
*
* NFD is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Foundation,
* either version 3 of the License, or (at your option) any later version.
*
* NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
*/
#include "pcap-helper.hpp"
#include <pcap/pcap.h>
#include <unistd.h>
#if !defined(PCAP_NETMASK_UNKNOWN)
#define PCAP_NETMASK_UNKNOWN 0xffffffff
#endif
namespace nfd {
namespace face {
PcapHelper::PcapHelper(const std::string& interfaceName)
: m_pcap(nullptr)
{
char errbuf[PCAP_ERRBUF_SIZE] = {};
m_pcap = pcap_create(interfaceName.data(), errbuf);
if (!m_pcap)
BOOST_THROW_EXCEPTION(Error("pcap_create: " + std::string(errbuf)));
#ifdef HAVE_PCAP_SET_IMMEDIATE_MODE
// Enable "immediate mode", effectively disabling any read buffering in the kernel.
// This corresponds to the BIOCIMMEDIATE ioctl on BSD-like systems (including macOS)
// where libpcap uses a BPF device. On Linux this forces libpcap not to use TPACKET_V3,
// even if the kernel supports it, thus preventing bug #1511.
if (pcap_set_immediate_mode(m_pcap, 1) < 0)
BOOST_THROW_EXCEPTION(Error("pcap_set_immediate_mode failed"));
#endif
}
PcapHelper::~PcapHelper()
{
close();
}
void
PcapHelper::activate(int dlt)
{
int ret = pcap_activate(m_pcap);
if (ret < 0)
BOOST_THROW_EXCEPTION(Error("pcap_activate: " + std::string(pcap_statustostr(ret))));
if (pcap_set_datalink(m_pcap, dlt) < 0)
BOOST_THROW_EXCEPTION(Error("pcap_set_datalink: " + getLastError()));
if (pcap_setdirection(m_pcap, PCAP_D_IN) < 0)
BOOST_THROW_EXCEPTION(Error("pcap_setdirection: " + getLastError()));
}
void
PcapHelper::close()
{
if (m_pcap) {
pcap_close(m_pcap);
m_pcap = nullptr;
}
}
int
PcapHelper::getFd() const
{
int fd = pcap_get_selectable_fd(m_pcap);
if (fd < 0)
BOOST_THROW_EXCEPTION(Error("pcap_get_selectable_fd failed"));
// we need to duplicate the fd, otherwise both pcap_close() and the
// caller may attempt to close the same fd and one of them will fail
return ::dup(fd);
}
std::string
PcapHelper::getLastError() const
{
return pcap_geterr(m_pcap);
}
size_t
PcapHelper::getNDropped() const
{
pcap_stat ps{};
if (pcap_stats(m_pcap, &ps) < 0)
BOOST_THROW_EXCEPTION(Error("pcap_stats: " + getLastError()));
return ps.ps_drop;
}
void
PcapHelper::setPacketFilter(const char* filter) const
{
bpf_program prog;
if (pcap_compile(m_pcap, &prog, filter, 1, PCAP_NETMASK_UNKNOWN) < 0)
BOOST_THROW_EXCEPTION(Error("pcap_compile: " + getLastError()));
int ret = pcap_setfilter(m_pcap, &prog);
pcap_freecode(&prog);
if (ret < 0)
BOOST_THROW_EXCEPTION(Error("pcap_setfilter: " + getLastError()));
}
std::tuple<const uint8_t*, size_t, std::string>
PcapHelper::readNextPacket() const
{
pcap_pkthdr* header;
const uint8_t* packet;
int ret = pcap_next_ex(m_pcap, &header, &packet);
if (ret < 0)
return std::make_tuple(nullptr, 0, getLastError());
else if (ret == 0)
return std::make_tuple(nullptr, 0, "timed out");
else
return std::make_tuple(packet, header->caplen, "");
}
} // namespace face
} // namespace nfd
+146
View File
@@ -0,0 +1,146 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
* Washington University in St. Louis,
* Beijing Institute of Technology,
* The University of Memphis.
*
* This file is part of NFD (Named Data Networking Forwarding Daemon).
* See AUTHORS.md for complete list of NFD authors and contributors.
*
* NFD is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Foundation,
* either version 3 of the License, or (at your option) any later version.
*
* NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef NFD_DAEMON_FACE_PCAP_HELPER_HPP
#define NFD_DAEMON_FACE_PCAP_HELPER_HPP
#include "core/common.hpp"
#ifndef HAVE_LIBPCAP
#error "Cannot include this file when libpcap is not available"
#endif
// forward declarations
struct pcap;
typedef pcap pcap_t;
namespace nfd {
namespace face {
/**
* @brief Helper class for dealing with libpcap handles.
*/
class PcapHelper : noncopyable
{
public:
class Error : public std::runtime_error
{
public:
explicit
Error(const std::string& what)
: std::runtime_error(what)
{
}
};
/**
* @brief Create a libpcap context for live packet capture on a network interface.
* @throw Error on any error
* @sa pcap_create(3pcap)
*/
explicit
PcapHelper(const std::string& interfaceName);
~PcapHelper();
/**
* @brief Start capturing packets.
* @param dlt The link-layer header type to be used.
* @throw Error on any error
* @sa pcap_activate(3pcap), pcap_set_datalink(3pcap)
*/
void
activate(int dlt);
/**
* @brief Stop capturing and close the handle.
* @sa pcap_close(3pcap)
*/
void
close();
/**
* @brief Obtain a file descriptor that can be used in calls such as select(2) and poll(2).
* @pre activate() has been called.
* @return A selectable file descriptor. It is the caller's responsibility to close the fd.
* @throw Error on any error
* @sa pcap_get_selectable_fd(3pcap)
*/
int
getFd() const;
/**
* @brief Get last error message.
* @return Human-readable explanation of the last libpcap error.
* @warning The behavior is undefined if no error occurred.
* @sa pcap_geterr(3pcap)
*/
std::string
getLastError() const;
/**
* @brief Get the number of packets dropped by the kernel, as reported by libpcap.
* @throw Error on any error
* @sa pcap_stats(3pcap)
*/
size_t
getNDropped() const;
/**
* @brief Install a BPF filter on the receiving socket.
* @param filter Null-terminated string containing the BPF program source.
* @pre activate() has been called.
* @throw Error on any error
* @sa pcap_setfilter(3pcap), pcap-filter(7)
*/
void
setPacketFilter(const char* filter) const;
/**
* @brief Read the next packet captured on the interface.
* @return If successful, returns a tuple containing a pointer to the received packet
* (including the link-layer header) and the size of the packet; the third
* element must be ignored. On failure, returns a tuple containing nullptr,
* 0, and the reason for the failure.
* @warning The returned pointer must not be freed by the caller, and is valid only
* until the next call to this function.
* @sa pcap_next_ex(3pcap)
*/
std::tuple<const uint8_t*, size_t, std::string>
readNextPacket() const;
operator pcap_t*() const
{
return m_pcap;
}
private:
pcap_t* m_pcap;
};
} // namespace face
} // namespace nfd
#endif // NFD_DAEMON_FACE_PCAP_HELPER_HPP
+15 -3
View File
@@ -1,5 +1,5 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
/*
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
@@ -38,11 +38,15 @@ ProtocolFactory::getRegistry()
}
unique_ptr<ProtocolFactory>
ProtocolFactory::create(const std::string& id)
ProtocolFactory::create(const std::string& id, const CtorParams& params)
{
Registry& registry = getRegistry();
auto found = registry.find(id);
return found == registry.end() ? nullptr : found->second();
if (found == registry.end()) {
return nullptr;
}
return found->second(params);
}
std::set<std::string>
@@ -54,5 +58,13 @@ ProtocolFactory::listRegistered()
return factoryIds;
}
ProtocolFactory::ProtocolFactory(const CtorParams& params)
: addFace(params.addFace)
, netmon(params.netmon)
{
BOOST_ASSERT(addFace != nullptr);
BOOST_ASSERT(netmon != nullptr);
}
} // namespace face
} // namespace nfd
+61 -37
View File
@@ -1,5 +1,5 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
/*
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
@@ -28,14 +28,28 @@
#include "channel.hpp"
#include "face-system.hpp"
#include <ndn-cxx/encoding/nfd-constants.hpp>
#include <boost/range/adaptor/map.hpp>
#include <boost/range/algorithm/copy.hpp>
#include <ndn-cxx/encoding/nfd-constants.hpp>
namespace nfd {
namespace face {
/** \brief provide support for an underlying protocol
/** \brief Parameters to ProtocolFactory constructor
*
* Every ProtocolFactory subclass is expected to have a constructor that accepts CtorParams,
* which in turn passes it to ProtocolFactory base class constructor. Parameters are passed as a
* struct rather than individually, so that a future change in list of parameters does not
* require updates to subclass constructors.
*/
struct ProtocolFactoryCtorParams
{
FaceCreatedCallback addFace;
shared_ptr<ndn::net::NetworkMonitor> netmon;
};
/** \brief Provides support for an underlying protocol
* \sa FaceSystem
*
* A protocol factory provides support for an underlying protocol and owns Channel objects.
@@ -45,7 +59,9 @@ namespace face {
class ProtocolFactory : noncopyable
{
public: // registry
/** \brief register a protocol factory type
using CtorParams = ProtocolFactoryCtorParams;
/** \brief Register a protocol factory type
* \tparam S subclass of ProtocolFactory
* \param id factory identifier
*/
@@ -55,23 +71,22 @@ public: // registry
{
Registry& registry = getRegistry();
BOOST_ASSERT(registry.count(id) == 0);
registry[id] = &make_unique<PF>;
registry[id] = &make_unique<PF, const CtorParams&>;
}
/** \return a protocol factory instance
/** \brief Create a protocol factory instance
* \retval nullptr if factory with \p id is not registered
*/
static unique_ptr<ProtocolFactory>
create(const std::string& id);
create(const std::string& id, const CtorParams& params);
/** \return registered protocol factory ids
/** \brief Get registered protocol factory ids
*/
static std::set<std::string>
listRegistered();
public:
/**
* \brief Base class for all exceptions thrown by protocol factories
/** \brief Base class for all exceptions thrown by ProtocolFactory subclasses
*/
class Error : public std::runtime_error
{
@@ -87,7 +102,7 @@ public:
~ProtocolFactory() = default;
#ifdef DOXYGEN
/** \return protocol factory id
/** \brief Get id for this ProtocolFactory
*
* face_system.factory-id config section is processed by the protocol factory.
*/
@@ -95,7 +110,7 @@ public:
getId();
#endif
/** \brief process face_system subsection that corresponds to this ProtocolFactory type
/** \brief Process face_system subsection that corresponds to this ProtocolFactory type
* \param configSection the configuration section or boost::null to indicate it is omitted
* \param context provides access to data structures and contextual information
* \throw ConfigFile::Error invalid configuration
@@ -104,13 +119,9 @@ public:
*/
virtual void
processConfig(OptionalConfigSection configSection,
FaceSystem::ConfigContext& context)
{
///\todo implement in every subclass and make this pure-virtual
BOOST_THROW_EXCEPTION(Error("processConfig is not implemented"));
}
FaceSystem::ConfigContext& context) = 0;
/** \return FaceUri schemes accepted by this ProtocolFactory
/** \brief Get FaceUri schemes accepted by this ProtocolFactory
*/
const std::set<std::string>&
getProvidedSchemes()
@@ -118,23 +129,29 @@ public:
return providedSchemes;
}
/** \brief Try to create Face using the supplied FaceUri
/** \brief Parameters to ProtocolFactory::createFace
*
* This method should automatically choose channel, based on supplied FaceUri
* and create face.
* Parameters are passed as a struct rather than individually, so that a future change in the list
* of parameters does not require an update to the method signature in all subclasses.
*/
struct CreateFaceParams
{
FaceUri remoteUri;
ndn::optional<FaceUri> localUri;
ndn::nfd::FacePersistency persistency;
bool wantLocalFields;
bool wantLpReliability;
};
/** \brief Try to create face using the supplied parameters
*
* \param uri remote URI of the new face
* \param persistency persistency of the new face
* \param wantLocalFieldsEnabled whether local fields should be enabled on the face
* \param onCreated callback if face creation succeeds
* If a face with the same remote URI already exists, its persistency and
* LocalFieldsEnabled setting will not be modified.
* \param params parameters to create face with
* \param onCreated callback if face creation succeeds or face already exists;
* persistency and local fields settings are not updated on an existing face
* \param onFailure callback if face creation fails
*/
virtual void
createFace(const FaceUri& uri,
ndn::nfd::FacePersistency persistency,
bool wantLocalFieldsEnabled,
createFace(const CreateFaceParams& params,
const FaceCreatedCallback& onCreated,
const FaceCreationFailedCallback& onFailure) = 0;
@@ -142,6 +159,9 @@ public:
getChannels() const = 0;
protected:
explicit
ProtocolFactory(const CtorParams& params);
template<typename ChannelMap>
static std::vector<shared_ptr<const Channel>>
getChannelsFromMap(const ChannelMap& channelMap)
@@ -152,22 +172,26 @@ protected:
}
private: // registry
typedef std::function<unique_ptr<ProtocolFactory>()> CreateFunc;
typedef std::map<std::string, CreateFunc> Registry; // indexed by factory id
using CreateFunc = std::function<unique_ptr<ProtocolFactory>(const CtorParams&)>;
using Registry = std::map<std::string, CreateFunc>; // indexed by factory id
static Registry&
getRegistry();
protected:
/** \brief FaceUri schemes provided by this ProtocolFactory
std::set<std::string> providedSchemes; ///< FaceUri schemes provided by this ProtocolFactory
FaceCreatedCallback addFace; ///< callback when a new face is created
/** \brief NetworkMonitor for listing available network interfaces and monitoring their changes
*
* ProtocolFactory subclass should check the NetworkMonitor has sufficient capabilities prior
* to usage.
*/
std::set<std::string> providedSchemes;
shared_ptr<ndn::net::NetworkMonitor> netmon;
};
} // namespace face
using face::ProtocolFactory;
} // namespace nfd
/** \brief registers a protocol factory
+14 -13
View File
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2014-2016, Regents of the University of California,
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -52,13 +52,13 @@ public:
StreamTransport(typename protocol::socket&& socket);
protected:
virtual void
void
doClose() override;
void
deferredClose();
virtual void
void
doSend(Transport::Packet&& packet) override;
void
@@ -124,9 +124,9 @@ StreamTransport<T>::doClose()
// Ensure that the Transport stays alive at least until
// all pending handlers are dispatched
getGlobalIoService().post(bind(&StreamTransport<T>::deferredClose, this));
getGlobalIoService().post([this] { deferredClose(); });
// Some bug or feature of Boost.Asio (see http://redmine.named-data.net/issues/1856):
// Some bug or feature of Boost.Asio (see https://redmine.named-data.net/issues/1856):
//
// When doClose is called from a socket event handler (e.g., from handleReceive),
// m_socket.shutdown() does not trigger the cancellation of the handleSend callback.
@@ -221,12 +221,10 @@ StreamTransport<T>::handleReceive(const boost::system::error_code& error,
NFD_LOG_FACE_TRACE("Received: " << nBytesReceived << " bytes");
m_receiveBufferSize += nBytesReceived;
size_t offset = 0;
bool isOk = true;
Block element;
while (m_receiveBufferSize - offset > 0) {
Block element;
std::tie(isOk, element) = Block::fromBuffer(m_receiveBuffer + offset, m_receiveBufferSize - offset);
if (!isOk)
break;
@@ -238,7 +236,7 @@ StreamTransport<T>::handleReceive(const boost::system::error_code& error,
}
if (!isOk && m_receiveBufferSize == ndn::MAX_NDN_PACKET_SIZE && offset == 0) {
NFD_LOG_FACE_WARN("Failed to parse incoming packet or packet too large to process");
NFD_LOG_FACE_ERROR("Failed to parse incoming packet or packet too large to process");
this->setState(TransportState::FAILED);
doClose();
return;
@@ -278,10 +276,13 @@ template<class T>
void
StreamTransport<T>::handleError(const boost::system::error_code& error)
{
if (error != boost::asio::error::eof)
NFD_LOG_FACE_WARN("Send or receive operation failed: " << error.message());
this->setState(TransportState::FAILED);
if (error == boost::asio::error::eof) {
this->setState(TransportState::CLOSING);
}
else {
NFD_LOG_FACE_ERROR("Send or receive operation failed: " << error.message());
this->setState(TransportState::FAILED);
}
doClose();
}
+57 -64
View File
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2014-2016, Regents of the University of California,
/*
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -29,6 +29,7 @@
#include "core/global-io.hpp"
namespace nfd {
namespace face {
NFD_LOG_INIT("TcpChannel");
@@ -37,9 +38,10 @@ namespace ip = boost::asio::ip;
TcpChannel::TcpChannel(const tcp::Endpoint& localEndpoint)
: m_localEndpoint(localEndpoint)
, m_acceptor(getGlobalIoService())
, m_acceptSocket(getGlobalIoService())
, m_socket(getGlobalIoService())
{
setUri(FaceUri(m_localEndpoint));
NFD_LOG_CHAN_INFO("Creating channel");
}
void
@@ -48,57 +50,57 @@ TcpChannel::listen(const FaceCreatedCallback& onFaceCreated,
int backlog/* = tcp::acceptor::max_connections*/)
{
if (isListening()) {
NFD_LOG_WARN("[" << m_localEndpoint << "] Already listening");
NFD_LOG_CHAN_WARN("Already listening");
return;
}
m_acceptor.open(m_localEndpoint.protocol());
m_acceptor.set_option(ip::tcp::acceptor::reuse_address(true));
if (m_localEndpoint.address().is_v6())
if (m_localEndpoint.address().is_v6()) {
m_acceptor.set_option(ip::v6_only(true));
}
m_acceptor.bind(m_localEndpoint);
m_acceptor.listen(backlog);
// start accepting connections
accept(onFaceCreated, onAcceptFailed);
NFD_LOG_CHAN_DEBUG("Started listening");
}
void
TcpChannel::connect(const tcp::Endpoint& remoteEndpoint,
bool wantLocalFieldsEnabled,
ndn::nfd::FacePersistency persistency,
bool wantLocalFields,
bool wantLpReliability,
const FaceCreatedCallback& onFaceCreated,
const FaceCreationFailedCallback& onConnectFailed,
const time::seconds& timeout/* = time::seconds(4)*/)
time::nanoseconds timeout)
{
auto it = m_channelFaces.find(remoteEndpoint);
if (it != m_channelFaces.end()) {
NFD_LOG_CHAN_TRACE("Reusing existing face for " << remoteEndpoint);
onFaceCreated(it->second);
return;
}
auto clientSocket = make_shared<ip::tcp::socket>(ref(getGlobalIoService()));
auto timeoutEvent = scheduler::schedule(timeout, bind(&TcpChannel::handleConnectTimeout, this,
remoteEndpoint, clientSocket, onConnectFailed));
scheduler::EventId connectTimeoutEvent = scheduler::schedule(timeout,
bind(&TcpChannel::handleConnectTimeout, this, clientSocket, onConnectFailed));
NFD_LOG_CHAN_TRACE("Connecting to " << remoteEndpoint);
clientSocket->async_connect(remoteEndpoint,
bind(&TcpChannel::handleConnect, this,
boost::asio::placeholders::error, clientSocket,
wantLocalFieldsEnabled, connectTimeoutEvent,
onFaceCreated, onConnectFailed));
}
size_t
TcpChannel::size() const
{
return m_channelFaces.size();
boost::asio::placeholders::error, remoteEndpoint, clientSocket,
// Creating a parameters struct works around limit on number of
// parameters to bind
ConnectParams{persistency, wantLocalFields, wantLpReliability},
timeoutEvent, onFaceCreated, onConnectFailed));
}
void
TcpChannel::createFace(ip::tcp::socket&& socket,
bool isOnDemand,
bool wantLocalFieldsEnabled,
ndn::nfd::FacePersistency persistency,
bool wantLocalFields,
bool wantLpReliability,
const FaceCreatedCallback& onFaceCreated)
{
shared_ptr<Face> face;
@@ -106,27 +108,21 @@ TcpChannel::createFace(ip::tcp::socket&& socket,
auto it = m_channelFaces.find(remoteEndpoint);
if (it == m_channelFaces.end()) {
auto persistency = isOnDemand ? ndn::nfd::FACE_PERSISTENCY_ON_DEMAND
: ndn::nfd::FACE_PERSISTENCY_PERSISTENT;
auto linkService = make_unique<face::GenericLinkService>();
auto options = linkService->getOptions();
options.allowLocalFields = wantLocalFieldsEnabled;
linkService->setOptions(options);
auto transport = make_unique<face::TcpTransport>(std::move(socket), persistency);
GenericLinkService::Options options;
options.allowLocalFields = wantLocalFields;
options.reliabilityOptions.isEnabled = wantLpReliability;
auto linkService = make_unique<GenericLinkService>(options);
auto transport = make_unique<TcpTransport>(std::move(socket), persistency);
face = make_shared<Face>(std::move(linkService), std::move(transport));
m_channelFaces[remoteEndpoint] = face;
connectFaceClosedSignal(*face,
[this, remoteEndpoint] {
NFD_LOG_TRACE("Erasing " << remoteEndpoint << " from channel face map");
m_channelFaces.erase(remoteEndpoint);
});
connectFaceClosedSignal(*face, [this, remoteEndpoint] { m_channelFaces.erase(remoteEndpoint); });
}
else {
// we already have a face for this endpoint, just reuse it
face = it->second;
NFD_LOG_CHAN_TRACE("Reusing existing face for " << remoteEndpoint);
boost::system::error_code error;
socket.shutdown(ip::tcp::socket::shutdown_both, error);
@@ -142,9 +138,9 @@ void
TcpChannel::accept(const FaceCreatedCallback& onFaceCreated,
const FaceCreationFailedCallback& onAcceptFailed)
{
m_acceptor.async_accept(m_acceptSocket, bind(&TcpChannel::handleAccept, this,
boost::asio::placeholders::error,
onFaceCreated, onAcceptFailed));
m_acceptor.async_accept(m_socket, bind(&TcpChannel::handleAccept, this,
boost::asio::placeholders::error,
onFaceCreated, onAcceptFailed));
}
void
@@ -153,18 +149,16 @@ TcpChannel::handleAccept(const boost::system::error_code& error,
const FaceCreationFailedCallback& onAcceptFailed)
{
if (error) {
if (error == boost::asio::error::operation_aborted) // when the socket is closed by someone
return;
NFD_LOG_DEBUG("[" << m_localEndpoint << "] Accept failed: " << error.message());
if (onAcceptFailed)
onAcceptFailed(500, "Accept failed: " + error.message());
if (error != boost::asio::error::operation_aborted) {
NFD_LOG_CHAN_DEBUG("Accept failed: " << error.message());
if (onAcceptFailed)
onAcceptFailed(500, "Accept failed: " + error.message());
}
return;
}
NFD_LOG_DEBUG("[" << m_localEndpoint << "] Connection from " << m_acceptSocket.remote_endpoint());
createFace(std::move(m_acceptSocket), true, false, onFaceCreated);
NFD_LOG_CHAN_TRACE("Incoming connection from " << m_socket.remote_endpoint());
createFace(std::move(m_socket), ndn::nfd::FACE_PERSISTENCY_ON_DEMAND, false, false, onFaceCreated);
// prepare accepting the next connection
accept(onFaceCreated, onAcceptFailed);
@@ -172,8 +166,9 @@ TcpChannel::handleAccept(const boost::system::error_code& error,
void
TcpChannel::handleConnect(const boost::system::error_code& error,
const tcp::Endpoint& remoteEndpoint,
const shared_ptr<ip::tcp::socket>& socket,
bool wantLocalFieldsEnabled,
TcpChannel::ConnectParams params,
const scheduler::EventId& connectTimeoutEvent,
const FaceCreatedCallback& onFaceCreated,
const FaceCreationFailedCallback& onConnectFailed)
@@ -189,35 +184,33 @@ TcpChannel::handleConnect(const boost::system::error_code& error,
#else
if (error) {
#endif
if (error == boost::asio::error::operation_aborted) // when the socket is closed by someone
return;
NFD_LOG_WARN("[" << m_localEndpoint << "] Connect failed: " << error.message());
socket->close();
if (onConnectFailed)
onConnectFailed(504, "Connect failed: " + error.message());
if (error != boost::asio::error::operation_aborted) {
NFD_LOG_CHAN_DEBUG("Connection to " << remoteEndpoint << " failed: " << error.message());
if (onConnectFailed)
onConnectFailed(504, "Connection failed: " + error.message());
}
return;
}
NFD_LOG_DEBUG("[" << m_localEndpoint << "] Connected to " << socket->remote_endpoint());
createFace(std::move(*socket), false, wantLocalFieldsEnabled, onFaceCreated);
NFD_LOG_CHAN_TRACE("Connected to " << socket->remote_endpoint());
createFace(std::move(*socket), params.persistency, params.wantLocalFields,
params.wantLpReliability, onFaceCreated);
}
void
TcpChannel::handleConnectTimeout(const shared_ptr<ip::tcp::socket>& socket,
TcpChannel::handleConnectTimeout(const tcp::Endpoint& remoteEndpoint,
const shared_ptr<ip::tcp::socket>& socket,
const FaceCreationFailedCallback& onConnectFailed)
{
NFD_LOG_DEBUG("Connect to remote endpoint timed out");
NFD_LOG_CHAN_DEBUG("Connection to " << remoteEndpoint << " timed out");
// abort the connection attempt
boost::system::error_code error;
socket->close(error);
if (onConnectFailed)
onConnectFailed(504, "Connect to remote endpoint timed out");
onConnectFailed(504, "Connection timed out");
}
} // namespace face
} // namespace nfd
+39 -27
View File
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2014-2016, Regents of the University of California,
/*
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -35,6 +35,8 @@ namespace tcp {
typedef boost::asio::ip::tcp::endpoint Endpoint;
} // namespace tcp
namespace face {
/**
* \brief Class implementing TCP-based channel to create faces
*
@@ -54,6 +56,18 @@ public:
explicit
TcpChannel(const tcp::Endpoint& localEndpoint);
bool
isListening() const override
{
return m_acceptor.is_open();
}
size_t
size() const override
{
return m_channelFaces.size();
}
/**
* \brief Enable listening on the local endpoint, accept connections,
* and create faces when remote host makes a connection
@@ -69,29 +83,31 @@ public:
int backlog = boost::asio::ip::tcp::acceptor::max_connections);
/**
* \brief Create a face by establishing connection to remote endpoint
* \brief Create a face by establishing a TCP connection to \p remoteEndpoint
*/
void
connect(const tcp::Endpoint& remoteEndpoint,
bool wantLocalFieldsEnabled,
ndn::nfd::FacePersistency persistency,
bool wantLocalFields,
bool wantLpReliability,
const FaceCreatedCallback& onFaceCreated,
const FaceCreationFailedCallback& onConnectFailed,
const time::seconds& timeout = time::seconds(4));
time::nanoseconds timeout = time::seconds(4));
/**
* \brief Get number of faces in the channel
*/
size_t
size() const;
bool
isListening() const;
private:
struct ConnectParams
{
ndn::nfd::FacePersistency persistency;
bool wantLocalFields;
bool wantLpReliability;
};
private:
void
createFace(boost::asio::ip::tcp::socket&& socket,
bool isOnDemand,
bool wantLocalFieldsEnabled,
ndn::nfd::FacePersistency persistency,
bool wantLocalFields,
bool wantLpReliability,
const FaceCreatedCallback& onFaceCreated);
void
@@ -105,30 +121,26 @@ private:
void
handleConnect(const boost::system::error_code& error,
const tcp::Endpoint& remoteEndpoint,
const shared_ptr<boost::asio::ip::tcp::socket>& socket,
bool wantLocalFieldsEnabled,
ConnectParams params,
const scheduler::EventId& connectTimeoutEvent,
const FaceCreatedCallback& onFaceCreated,
const FaceCreationFailedCallback& onConnectFailed);
void
handleConnectTimeout(const shared_ptr<boost::asio::ip::tcp::socket>& socket,
handleConnectTimeout(const tcp::Endpoint& remoteEndpoint,
const shared_ptr<boost::asio::ip::tcp::socket>& socket,
const FaceCreationFailedCallback& onConnectFailed);
private:
std::map<tcp::Endpoint, shared_ptr<Face>> m_channelFaces;
tcp::Endpoint m_localEndpoint;
const tcp::Endpoint m_localEndpoint;
boost::asio::ip::tcp::acceptor m_acceptor;
boost::asio::ip::tcp::socket m_acceptSocket;
boost::asio::ip::tcp::socket m_socket;
std::map<tcp::Endpoint, shared_ptr<Face>> m_channelFaces;
};
inline bool
TcpChannel::isListening() const
{
return m_acceptor.is_open();
}
} // namespace face
} // namespace nfd
#endif // NFD_DAEMON_FACE_TCP_CHANNEL_HPP
+32 -94
View File
@@ -1,5 +1,5 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
/*
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
@@ -24,7 +24,8 @@
*/
#include "tcp-factory.hpp"
#include "core/logger.hpp"
#include <ndn-cxx/net/address-converter.hpp>
namespace nfd {
namespace face {
@@ -41,6 +42,11 @@ TcpFactory::getId()
return id;
}
TcpFactory::TcpFactory(const CtorParams& params)
: ProtocolFactory(params)
{
}
void
TcpFactory::processConfig(OptionalConfigSection configSection,
FaceSystem::ConfigContext& context)
@@ -98,7 +104,7 @@ TcpFactory::processConfig(OptionalConfigSection configSection,
tcp::Endpoint endpoint(ip::tcp::v4(), port);
shared_ptr<TcpChannel> v4Channel = this->createChannel(endpoint);
if (wantListen && !v4Channel->isListening()) {
v4Channel->listen(context.addFace, nullptr);
v4Channel->listen(this->addFace, nullptr);
}
providedSchemes.insert("tcp4");
}
@@ -110,7 +116,7 @@ TcpFactory::processConfig(OptionalConfigSection configSection,
tcp::Endpoint endpoint(ip::tcp::v6(), port);
shared_ptr<TcpChannel> v6Channel = this->createChannel(endpoint);
if (wantListen && !v6Channel->isListening()) {
v6Channel->listen(context.addFace, nullptr);
v6Channel->listen(this->addFace, nullptr);
}
providedSchemes.insert("tcp6");
}
@@ -121,37 +127,31 @@ TcpFactory::processConfig(OptionalConfigSection configSection,
}
void
TcpFactory::createFace(const FaceUri& uri,
ndn::nfd::FacePersistency persistency,
bool wantLocalFieldsEnabled,
TcpFactory::createFace(const CreateFaceParams& params,
const FaceCreatedCallback& onCreated,
const FaceCreationFailedCallback& onFailure)
{
BOOST_ASSERT(uri.isCanonical());
BOOST_ASSERT(params.remoteUri.isCanonical());
if (persistency != ndn::nfd::FACE_PERSISTENCY_PERSISTENT) {
NFD_LOG_TRACE("createFace only supports FACE_PERSISTENCY_PERSISTENT");
onFailure(406, "Outgoing TCP faces only support persistent persistency");
if (params.localUri) {
NFD_LOG_TRACE("Cannot create unicast TCP face with LocalUri");
onFailure(406, "Unicast TCP faces cannot be created with a LocalUri");
return;
}
tcp::Endpoint endpoint(ip::address::from_string(uri.getHost()),
boost::lexical_cast<uint16_t>(uri.getPort()));
if (endpoint.address().is_multicast()) {
NFD_LOG_TRACE("createFace cannot create multicast faces");
onFailure(406, "Cannot create multicast TCP faces");
return;
}
if (m_prohibitedEndpoints.find(endpoint) != m_prohibitedEndpoints.end()) {
NFD_LOG_TRACE("Requested endpoint is prohibited "
"(reserved by NFD or disallowed by face management protocol)");
onFailure(406, "Requested endpoint is prohibited");
if (params.persistency == ndn::nfd::FACE_PERSISTENCY_ON_DEMAND) {
NFD_LOG_TRACE("createFace does not support FACE_PERSISTENCY_ON_DEMAND");
onFailure(406, "Outgoing TCP faces do not support on-demand persistency");
return;
}
if (wantLocalFieldsEnabled && !endpoint.address().is_loopback()) {
tcp::Endpoint endpoint(ndn::ip::addressFromString(params.remoteUri.getHost()),
boost::lexical_cast<uint16_t>(params.remoteUri.getPort()));
// a canonical tcp4/tcp6 FaceUri cannot have a multicast address
BOOST_ASSERT(!endpoint.address().is_multicast());
if (params.wantLocalFields && !endpoint.address().is_loopback()) {
NFD_LOG_TRACE("createFace cannot create non-local face with local fields enabled");
onFailure(406, "Local fields can only be enabled on faces with local scope");
return;
@@ -161,95 +161,33 @@ TcpFactory::createFace(const FaceUri& uri,
for (const auto& i : m_channels) {
if ((i.first.address().is_v4() && endpoint.address().is_v4()) ||
(i.first.address().is_v6() && endpoint.address().is_v6())) {
i.second->connect(endpoint, wantLocalFieldsEnabled, onCreated, onFailure);
i.second->connect(endpoint, params.persistency, params.wantLocalFields,
params.wantLpReliability, onCreated, onFailure);
return;
}
}
NFD_LOG_TRACE("No channels available to connect to " + boost::lexical_cast<std::string>(endpoint));
NFD_LOG_TRACE("No channels available to connect to " << endpoint);
onFailure(504, "No channels available to connect");
}
void
TcpFactory::prohibitEndpoint(const tcp::Endpoint& endpoint)
{
if (endpoint.address().is_v4() &&
endpoint.address() == ip::address_v4::any()) {
prohibitAllIpv4Endpoints(endpoint.port());
}
else if (endpoint.address().is_v6() &&
endpoint.address() == ip::address_v6::any()) {
prohibitAllIpv6Endpoints(endpoint.port());
}
NFD_LOG_TRACE("prohibiting TCP " << endpoint);
m_prohibitedEndpoints.insert(endpoint);
}
void
TcpFactory::prohibitAllIpv4Endpoints(uint16_t port)
{
///\todo prohibited endpoints need to react to dynamic NIC changes
for (const NetworkInterfaceInfo& nic : listNetworkInterfaces()) {
for (const auto& addr : nic.ipv4Addresses) {
if (addr != ip::address_v4::any()) {
prohibitEndpoint(tcp::Endpoint(addr, port));
}
}
}
}
void
TcpFactory::prohibitAllIpv6Endpoints(uint16_t port)
{
///\todo prohibited endpoints need to react to dynamic NIC changes
for (const NetworkInterfaceInfo& nic : listNetworkInterfaces()) {
for (const auto& addr : nic.ipv6Addresses) {
if (addr != ip::address_v6::any()) {
prohibitEndpoint(tcp::Endpoint(addr, port));
}
}
}
}
shared_ptr<TcpChannel>
TcpFactory::createChannel(const tcp::Endpoint& endpoint)
{
auto channel = findChannel(endpoint);
if (channel)
return channel;
auto it = m_channels.find(endpoint);
if (it != m_channels.end())
return it->second;
channel = make_shared<TcpChannel>(endpoint);
auto channel = make_shared<TcpChannel>(endpoint);
m_channels[endpoint] = channel;
prohibitEndpoint(endpoint);
NFD_LOG_DEBUG("Channel [" << endpoint << "] created");
return channel;
}
shared_ptr<TcpChannel>
TcpFactory::createChannel(const std::string& localIp, const std::string& localPort)
{
tcp::Endpoint endpoint(ip::address::from_string(localIp),
boost::lexical_cast<uint16_t>(localPort));
return createChannel(endpoint);
}
std::vector<shared_ptr<const Channel>>
TcpFactory::getChannels() const
{
return getChannelsFromMap(m_channels);
}
shared_ptr<TcpChannel>
TcpFactory::findChannel(const tcp::Endpoint& localEndpoint) const
{
auto i = m_channels.find(localEndpoint);
if (i != m_channels.end())
return i->second;
else
return nullptr;
}
} // namespace face
} // namespace nfd
+9 -46
View File
@@ -1,5 +1,5 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
/*
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
@@ -40,6 +40,9 @@ public:
static const std::string&
getId();
explicit
TcpFactory(const CtorParams& params);
/** \brief process face_system.tcp config section
*/
void
@@ -47,9 +50,7 @@ public:
FaceSystem::ConfigContext& context) override;
void
createFace(const FaceUri& uri,
ndn::nfd::FacePersistency persistency,
bool wantLocalFieldsEnabled,
createFace(const CreateFaceParams& params,
const FaceCreatedCallback& onCreated,
const FaceCreationFailedCallback& onFailure) override;
@@ -58,58 +59,20 @@ public:
*
* tcp::Endpoint is really an alias for boost::asio::ip::tcp::endpoint.
*
* If this method called twice with the same endpoint, only one channel
* will be created. The second call will just retrieve the existing
* channel.
* If this method is called twice with the same endpoint, only one channel
* will be created. The second call will just return the existing channel.
*
* \returns always a valid pointer to a TcpChannel object, an exception
* is thrown if it cannot be created.
*
* \see http://www.boost.org/doc/libs/1_42_0/doc/html/boost_asio/reference/ip__tcp/endpoint.html
* for details on ways to create tcp::Endpoint
* \return always a valid pointer to a TcpChannel object, an exception
* is thrown if it cannot be created.
*/
shared_ptr<TcpChannel>
createChannel(const tcp::Endpoint& localEndpoint);
/**
* \brief Create TCP-based channel using specified IP address and port number
*
* This method is just a helper that converts a string representation of localIp and port to
* tcp::Endpoint and calls the other createChannel overload.
*
* \throws std::runtime_error
*/
shared_ptr<TcpChannel>
createChannel(const std::string& localIp, const std::string& localPort);
std::vector<shared_ptr<const Channel>>
getChannels() const override;
PUBLIC_WITH_TESTS_ELSE_PRIVATE:
void
prohibitEndpoint(const tcp::Endpoint& endpoint);
void
prohibitAllIpv4Endpoints(uint16_t port);
void
prohibitAllIpv6Endpoints(uint16_t port);
private:
/**
* \brief Look up TcpChannel using specified local endpoint
*
* \returns shared pointer to the existing TcpChannel object
* or empty shared pointer when such channel does not exist
*/
shared_ptr<TcpChannel>
findChannel(const tcp::Endpoint& localEndpoint) const;
private:
std::map<tcp::Endpoint, shared_ptr<TcpChannel>> m_channels;
PUBLIC_WITH_TESTS_ELSE_PRIVATE:
std::set<tcp::Endpoint> m_prohibitedEndpoints;
};
} // namespace face
+3 -3
View File
@@ -40,9 +40,9 @@ class LinkService;
*/
enum class TransportState {
NONE,
UP, ///< the transport is up
DOWN, ///< the transport is down temporarily, and is being recovered
CLOSING, ///< the transport is requested to be closed
UP, ///< the transport is up and can transmit packets
DOWN, ///< the transport is temporarily down, and is being recovered
CLOSING, ///< the transport is being closed gracefully, either by the peer or by a call to close()
FAILED, ///< the transport is being closed due to a failure
CLOSED ///< the transport is closed, and can be safely deallocated
};
+45 -55
View File
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2014-2016, Regents of the University of California,
/*
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -29,40 +29,37 @@
#include "core/global-io.hpp"
namespace nfd {
namespace face {
NFD_LOG_INIT("UdpChannel");
using namespace boost::asio;
namespace ip = boost::asio::ip;
UdpChannel::UdpChannel(const udp::Endpoint& localEndpoint,
const time::seconds& timeout)
time::nanoseconds idleTimeout)
: m_localEndpoint(localEndpoint)
, m_socket(getGlobalIoService())
, m_idleFaceTimeout(timeout)
, m_idleFaceTimeout(idleTimeout)
{
setUri(FaceUri(m_localEndpoint));
}
size_t
UdpChannel::size() const
{
return m_channelFaces.size();
NFD_LOG_CHAN_INFO("Creating channel");
}
void
UdpChannel::connect(const udp::Endpoint& remoteEndpoint,
ndn::nfd::FacePersistency persistency,
bool wantLpReliability,
const FaceCreatedCallback& onFaceCreated,
const FaceCreationFailedCallback& onConnectFailed)
{
shared_ptr<Face> face;
try {
face = createFace(remoteEndpoint, persistency).second;
face = createFace(remoteEndpoint, persistency, wantLpReliability).second;
}
catch (const boost::system::system_error& e) {
NFD_LOG_WARN("[" << m_localEndpoint << "] Connect failed: " << e.what());
NFD_LOG_CHAN_DEBUG("Face creation for " << remoteEndpoint << " failed: " << e.what());
if (onConnectFailed)
onConnectFailed(504, std::string("Connect failed: ") + e.what());
onConnectFailed(504, std::string("Face creation failed: ") + e.what());
return;
}
@@ -73,28 +70,29 @@ UdpChannel::connect(const udp::Endpoint& remoteEndpoint,
void
UdpChannel::listen(const FaceCreatedCallback& onFaceCreated,
const FaceCreationFailedCallback& onReceiveFailed)
const FaceCreationFailedCallback& onFaceCreationFailed)
{
if (isListening()) {
NFD_LOG_WARN("[" << m_localEndpoint << "] Already listening");
NFD_LOG_CHAN_WARN("Already listening");
return;
}
m_socket.open(m_localEndpoint.protocol());
m_socket.set_option(ip::udp::socket::reuse_address(true));
if (m_localEndpoint.address().is_v6())
if (m_localEndpoint.address().is_v6()) {
m_socket.set_option(ip::v6_only(true));
}
m_socket.bind(m_localEndpoint);
this->waitForNewPeer(onFaceCreated, onReceiveFailed);
waitForNewPeer(onFaceCreated, onFaceCreationFailed);
NFD_LOG_CHAN_DEBUG("Started listening");
}
void
UdpChannel::waitForNewPeer(const FaceCreatedCallback& onFaceCreated,
const FaceCreationFailedCallback& onReceiveFailed)
{
m_socket.async_receive_from(boost::asio::buffer(m_inputBuffer, ndn::MAX_NDN_PACKET_SIZE),
m_remoteEndpoint,
m_socket.async_receive_from(boost::asio::buffer(m_receiveBuffer), m_remoteEndpoint,
bind(&UdpChannel::handleNewPeer, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred,
@@ -108,56 +106,51 @@ UdpChannel::handleNewPeer(const boost::system::error_code& error,
const FaceCreationFailedCallback& onReceiveFailed)
{
if (error) {
if (error == boost::asio::error::operation_aborted) // when the socket is closed by someone
return;
NFD_LOG_DEBUG("[" << m_localEndpoint << "] Receive failed: " << error.message());
if (onReceiveFailed)
onReceiveFailed(500, "Receive failed: " + error.message());
if (error != boost::asio::error::operation_aborted) {
NFD_LOG_CHAN_DEBUG("Receive failed: " << error.message());
if (onReceiveFailed)
onReceiveFailed(500, "Receive failed: " + error.message());
}
return;
}
NFD_LOG_DEBUG("[" << m_localEndpoint << "] New peer " << m_remoteEndpoint);
NFD_LOG_CHAN_TRACE("New peer " << m_remoteEndpoint);
bool isCreated = false;
shared_ptr<Face> face;
try {
std::tie(isCreated, face) = createFace(m_remoteEndpoint, ndn::nfd::FACE_PERSISTENCY_ON_DEMAND);
std::tie(isCreated, face) = createFace(m_remoteEndpoint, ndn::nfd::FACE_PERSISTENCY_ON_DEMAND,
false);
}
catch (const boost::system::system_error& e) {
NFD_LOG_WARN("[" << m_localEndpoint << "] Failed to create face for peer "
<< m_remoteEndpoint << ": " << e.what());
NFD_LOG_CHAN_DEBUG("Face creation for " << m_remoteEndpoint << " failed: " << e.what());
if (onReceiveFailed)
onReceiveFailed(504, "Failed to create face for peer");
onReceiveFailed(504, std::string("Face creation failed: ") + e.what());
return;
}
if (isCreated)
onFaceCreated(face);
else
NFD_LOG_CHAN_DEBUG("Received datagram for existing face");
// dispatch the datagram to the face for processing
static_cast<face::UnicastUdpTransport*>(face->getTransport())->receiveDatagram(m_inputBuffer, nBytesReceived, error);
auto* transport = static_cast<UnicastUdpTransport*>(face->getTransport());
transport->receiveDatagram(m_receiveBuffer.data(), nBytesReceived, error);
this->waitForNewPeer(onFaceCreated, onReceiveFailed);
waitForNewPeer(onFaceCreated, onReceiveFailed);
}
std::pair<bool, shared_ptr<Face>>
UdpChannel::createFace(const udp::Endpoint& remoteEndpoint, ndn::nfd::FacePersistency persistency)
UdpChannel::createFace(const udp::Endpoint& remoteEndpoint,
ndn::nfd::FacePersistency persistency,
bool wantLpReliability)
{
auto it = m_channelFaces.find(remoteEndpoint);
if (it != m_channelFaces.end()) {
// we already have a face for this endpoint, so reuse it
auto face = it->second;
// TODO #3232: Remove persistency transitions from faces/create
// only on-demand -> persistent -> permanent transition is allowed
bool isTransitionAllowed = persistency != face->getPersistency() &&
(face->getPersistency() == ndn::nfd::FACE_PERSISTENCY_ON_DEMAND ||
persistency == ndn::nfd::FACE_PERSISTENCY_PERMANENT);
if (isTransitionAllowed) {
face->setPersistency(persistency);
}
return {false, face};
NFD_LOG_CHAN_TRACE("Reusing existing face for " << remoteEndpoint);
return {false, it->second};
}
// else, create a new face
@@ -166,20 +159,17 @@ UdpChannel::createFace(const udp::Endpoint& remoteEndpoint, ndn::nfd::FacePersis
socket.bind(m_localEndpoint);
socket.connect(remoteEndpoint);
auto linkService = make_unique<face::GenericLinkService>();
auto transport = make_unique<face::UnicastUdpTransport>(std::move(socket), persistency, m_idleFaceTimeout);
GenericLinkService::Options options;
options.reliabilityOptions.isEnabled = wantLpReliability;
auto linkService = make_unique<GenericLinkService>(options);
auto transport = make_unique<UnicastUdpTransport>(std::move(socket), persistency, m_idleFaceTimeout);
auto face = make_shared<Face>(std::move(linkService), std::move(transport));
face->setPersistency(persistency);
m_channelFaces[remoteEndpoint] = face;
connectFaceClosedSignal(*face,
[this, remoteEndpoint] {
NFD_LOG_TRACE("Erasing " << remoteEndpoint << " from channel face map");
m_channelFaces.erase(remoteEndpoint);
});
connectFaceClosedSignal(*face, [this, remoteEndpoint] { m_channelFaces.erase(remoteEndpoint); });
return {true, face};
}
} // namespace face
} // namespace nfd
+43 -53
View File
@@ -1,5 +1,5 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
/*
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
@@ -29,7 +29,10 @@
#include "channel.hpp"
#include "udp-protocol.hpp"
#include <array>
namespace nfd {
namespace face {
/**
* \brief Class implementing UDP-based channel to create faces
@@ -38,53 +41,57 @@ class UdpChannel : public Channel
{
public:
/**
* \brief Create UDP channel for the local endpoint
* \brief Create a UDP channel on the given \p localEndpoint
*
* To enable creation of faces upon incoming connections,
* one needs to explicitly call UdpChannel::listen method.
* The created socket is bound to the localEndpoint.
* reuse_address option is set
*
* \throw UdpChannel::Error if bind on the socket fails
* The created socket is bound to \p localEndpoint.
*/
UdpChannel(const udp::Endpoint& localEndpoint,
const time::seconds& timeout);
time::nanoseconds idleTimeout);
bool
isListening() const override
{
return m_socket.is_open();
}
/**
* \brief Get number of faces in the channel
*/
size_t
size() const;
size() const override
{
return m_channelFaces.size();
}
/**
* \brief Create a face by establishing connection to remote endpoint
* \brief Create a unicast UDP face toward \p remoteEndpoint
*
* \throw UdpChannel::Error if bind or connect on the socket fail
* \param remoteEndpoint The remote UDP endpoint
* \param persistency Persistency of the newly created face
* \param wantLpReliability whether LpReliability should be enabled
* \param onFaceCreated Callback to notify successful creation of the face
* \param onConnectFailed Callback to notify errors
*/
void
connect(const udp::Endpoint& remoteEndpoint,
ndn::nfd::FacePersistency persistency,
bool wantLpReliability,
const FaceCreatedCallback& onFaceCreated,
const FaceCreationFailedCallback& onConnectFailed);
/**
* \brief Enable listening on the local endpoint, accept connections,
* and create faces when remote host makes a connection
* \param onFaceCreated Callback to notify successful creation of the face
* \param onReceiveFailed Callback to notify when channel fails
* \brief Start listening
*
* Once a face is created, if it doesn't send/receive anything for
* a period of time equal to timeout, it will be destroyed
* \todo this functionality has to be implemented
* Enable listening on the local endpoint, waiting for incoming datagrams,
* and creating a face when a datagram is received from a new remote host.
*
* \throws UdpChannel::Error if called multiple times
* Faces created in this way will have on-demand persistency.
*
* \param onFaceCreated Callback to notify successful creation of a face
* \param onFaceCreationFailed Callback to notify errors
*/
void
listen(const FaceCreatedCallback& onFaceCreated,
const FaceCreationFailedCallback& onReceiveFailed);
bool
isListening() const;
const FaceCreationFailedCallback& onFaceCreationFailed);
private:
void
@@ -92,8 +99,8 @@ private:
const FaceCreationFailedCallback& onReceiveFailed);
/**
* \brief The channel has received a new packet from a remote
* endpoint that is not associated with any UdpFace yet
* \brief The channel has received a packet from a remote
* endpoint not associated with any UDP face yet
*/
void
handleNewPeer(const boost::system::error_code& error,
@@ -102,37 +109,20 @@ private:
const FaceCreationFailedCallback& onReceiveFailed);
std::pair<bool, shared_ptr<Face>>
createFace(const udp::Endpoint& remoteEndpoint, ndn::nfd::FacePersistency persistency);
createFace(const udp::Endpoint& remoteEndpoint,
ndn::nfd::FacePersistency persistency,
bool wantLpReliability);
private:
const udp::Endpoint m_localEndpoint;
udp::Endpoint m_remoteEndpoint; ///< The latest peer that started communicating with us
boost::asio::ip::udp::socket m_socket; ///< Socket used to "accept" new peers
std::array<uint8_t, ndn::MAX_NDN_PACKET_SIZE> m_receiveBuffer;
std::map<udp::Endpoint, shared_ptr<Face>> m_channelFaces;
udp::Endpoint m_localEndpoint;
/**
* \brief The latest peer that started communicating with us
*/
udp::Endpoint m_remoteEndpoint;
/**
* \brief Socket used to "accept" new communication
*/
boost::asio::ip::udp::socket m_socket;
/**
* \brief When this timeout expires, all idle on-demand faces will be closed
*/
time::seconds m_idleFaceTimeout;
uint8_t m_inputBuffer[ndn::MAX_NDN_PACKET_SIZE];
const time::nanoseconds m_idleFaceTimeout; ///< Timeout for automatic closure of idle on-demand faces
};
inline bool
UdpChannel::isListening() const
{
return m_socket.is_open();
}
} // namespace face
} // namespace nfd
#endif // NFD_DAEMON_FACE_UDP_CHANNEL_HPP
+179 -222
View File
@@ -1,5 +1,5 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
/*
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
@@ -27,6 +27,8 @@
#include "generic-link-service.hpp"
#include "multicast-udp-transport.hpp"
#include "core/global-io.hpp"
#include <ndn-cxx/net/address-converter.hpp>
#include <boost/range/adaptors.hpp>
#include <boost/range/algorithm/copy.hpp>
@@ -51,6 +53,11 @@ UdpFactory::getId()
return id;
}
UdpFactory::UdpFactory(const CtorParams& params)
: ProtocolFactory(params)
{
m_netifAddConn = netmon->onInterfaceAdded.connect(bind(&UdpFactory::applyMcastConfigToNetif, this, _1));
}
void
UdpFactory::processConfig(OptionalConfigSection configSection,
@@ -66,6 +73,7 @@ UdpFactory::processConfig(OptionalConfigSection configSection,
// mcast yes
// mcast_group 224.0.23.170
// mcast_port 56363
// mcast_ad_hoc no
// whitelist
// {
// *
@@ -123,6 +131,10 @@ UdpFactory::processConfig(OptionalConfigSection configSection,
else if (key == "mcast_port") {
mcastConfig.group.port(ConfigFile::parseNumber<uint16_t>(pair, "face_system.udp"));
}
else if (key == "mcast_ad_hoc") {
bool wantAdHoc = ConfigFile::parseYesNo(pair, "face_system.udp");
mcastConfig.linkType = wantAdHoc ? ndn::nfd::LINK_TYPE_AD_HOC : ndn::nfd::LINK_TYPE_MULTI_ACCESS;
}
else if (key == "whitelist") {
mcastConfig.netifPredicate.parseWhitelist(value);
}
@@ -141,75 +153,84 @@ UdpFactory::processConfig(OptionalConfigSection configSection,
}
}
if (!context.isDryRun) {
if (enableV4) {
udp::Endpoint endpoint(ip::udp::v4(), port);
shared_ptr<UdpChannel> v4Channel = this->createChannel(endpoint, time::seconds(idleTimeout));
if (!v4Channel->isListening()) {
v4Channel->listen(context.addFace, nullptr);
}
providedSchemes.insert("udp");
providedSchemes.insert("udp4");
}
else if (providedSchemes.count("udp4") > 0) {
NFD_LOG_WARN("Cannot close udp4 channel after its creation");
}
if (enableV6) {
udp::Endpoint endpoint(ip::udp::v6(), port);
shared_ptr<UdpChannel> v6Channel = this->createChannel(endpoint, time::seconds(idleTimeout));
if (!v6Channel->isListening()) {
v6Channel->listen(context.addFace, nullptr);
}
providedSchemes.insert("udp");
providedSchemes.insert("udp6");
}
else if (providedSchemes.count("udp6") > 0) {
NFD_LOG_WARN("Cannot close udp6 channel after its creation");
}
if (m_mcastConfig.isEnabled != mcastConfig.isEnabled) {
if (mcastConfig.isEnabled) {
NFD_LOG_INFO("enabling multicast on " << mcastConfig.group);
}
else {
NFD_LOG_INFO("disabling multicast");
}
}
else if (m_mcastConfig.group != mcastConfig.group) {
NFD_LOG_INFO("changing multicast group from " << m_mcastConfig.group <<
" to " << mcastConfig.group);
}
else if (m_mcastConfig.netifPredicate != mcastConfig.netifPredicate) {
NFD_LOG_INFO("changing whitelist/blacklist");
}
else {
// There's no configuration change, but we still need to re-apply configuration because
// netifs may have changed.
}
m_mcastConfig = mcastConfig;
this->applyMulticastConfig(context);
}
}
void
UdpFactory::createFace(const FaceUri& uri,
ndn::nfd::FacePersistency persistency,
bool wantLocalFieldsEnabled,
const FaceCreatedCallback& onCreated,
const FaceCreationFailedCallback& onFailure)
{
BOOST_ASSERT(uri.isCanonical());
if (persistency == ndn::nfd::FACE_PERSISTENCY_ON_DEMAND) {
NFD_LOG_TRACE("createFace does not support FACE_PERSISTENCY_ON_DEMAND");
onFailure(406, "Outgoing unicast UDP faces do not support on-demand persistency");
if (context.isDryRun) {
return;
}
udp::Endpoint endpoint(ip::address::from_string(uri.getHost()),
boost::lexical_cast<uint16_t>(uri.getPort()));
if (enableV4) {
udp::Endpoint endpoint(ip::udp::v4(), port);
shared_ptr<UdpChannel> v4Channel = this->createChannel(endpoint, time::seconds(idleTimeout));
if (!v4Channel->isListening()) {
v4Channel->listen(this->addFace, nullptr);
}
providedSchemes.insert("udp");
providedSchemes.insert("udp4");
}
else if (providedSchemes.count("udp4") > 0) {
NFD_LOG_WARN("Cannot close udp4 channel after its creation");
}
if (enableV6) {
udp::Endpoint endpoint(ip::udp::v6(), port);
shared_ptr<UdpChannel> v6Channel = this->createChannel(endpoint, time::seconds(idleTimeout));
if (!v6Channel->isListening()) {
v6Channel->listen(this->addFace, nullptr);
}
providedSchemes.insert("udp");
providedSchemes.insert("udp6");
}
else if (providedSchemes.count("udp6") > 0) {
NFD_LOG_WARN("Cannot close udp6 channel after its creation");
}
if (m_mcastConfig.isEnabled != mcastConfig.isEnabled) {
if (mcastConfig.isEnabled) {
NFD_LOG_INFO("enabling multicast on " << mcastConfig.group);
}
else {
NFD_LOG_INFO("disabling multicast");
}
}
else if (mcastConfig.isEnabled) {
if (m_mcastConfig.linkType != mcastConfig.linkType && !m_mcastFaces.empty()) {
NFD_LOG_WARN("Cannot change ad hoc setting on existing faces");
}
if (m_mcastConfig.group != mcastConfig.group) {
NFD_LOG_INFO("changing multicast group from " << m_mcastConfig.group <<
" to " << mcastConfig.group);
}
if (m_mcastConfig.netifPredicate != mcastConfig.netifPredicate) {
NFD_LOG_INFO("changing whitelist/blacklist");
}
}
// Even if there's no configuration change, we still need to re-apply configuration because
// netifs may have changed.
m_mcastConfig = mcastConfig;
this->applyMcastConfig(context);
}
void
UdpFactory::createFace(const CreateFaceParams& params,
const FaceCreatedCallback& onCreated,
const FaceCreationFailedCallback& onFailure)
{
BOOST_ASSERT(params.remoteUri.isCanonical());
if (params.localUri) {
NFD_LOG_TRACE("Cannot create unicast UDP face with LocalUri");
onFailure(406, "Unicast UDP faces cannot be created with a LocalUri");
return;
}
if (params.persistency == ndn::nfd::FACE_PERSISTENCY_ON_DEMAND) {
NFD_LOG_TRACE("createFace does not support FACE_PERSISTENCY_ON_DEMAND");
onFailure(406, "Outgoing UDP faces do not support on-demand persistency");
return;
}
udp::Endpoint endpoint(ndn::ip::addressFromString(params.remoteUri.getHost()),
boost::lexical_cast<uint16_t>(params.remoteUri.getPort()));
if (endpoint.address().is_multicast()) {
NFD_LOG_TRACE("createFace does not support multicast faces");
@@ -217,14 +238,7 @@ UdpFactory::createFace(const FaceUri& uri,
return;
}
if (m_prohibitedEndpoints.find(endpoint) != m_prohibitedEndpoints.end()) {
NFD_LOG_TRACE("Requested endpoint is prohibited "
"(reserved by this NFD or disallowed by face management protocol)");
onFailure(406, "Requested endpoint is prohibited");
return;
}
if (wantLocalFieldsEnabled) {
if (params.wantLocalFields) {
// UDP faces are never local
NFD_LOG_TRACE("createFace cannot create non-local face with local fields enabled");
onFailure(406, "Local fields can only be enabled on faces with local scope");
@@ -235,126 +249,57 @@ UdpFactory::createFace(const FaceUri& uri,
for (const auto& i : m_channels) {
if ((i.first.address().is_v4() && endpoint.address().is_v4()) ||
(i.first.address().is_v6() && endpoint.address().is_v6())) {
i.second->connect(endpoint, persistency, onCreated, onFailure);
i.second->connect(endpoint, params.persistency, params.wantLpReliability,
onCreated, onFailure);
return;
}
}
NFD_LOG_TRACE("No channels available to connect to " + boost::lexical_cast<std::string>(endpoint));
NFD_LOG_TRACE("No channels available to connect to " << endpoint);
onFailure(504, "No channels available to connect");
}
void
UdpFactory::prohibitEndpoint(const udp::Endpoint& endpoint)
{
if (endpoint.address().is_v4() &&
endpoint.address() == ip::address_v4::any()) {
prohibitAllIpv4Endpoints(endpoint.port());
}
else if (endpoint.address().is_v6() &&
endpoint.address() == ip::address_v6::any()) {
prohibitAllIpv6Endpoints(endpoint.port());
}
NFD_LOG_TRACE("prohibiting UDP " << endpoint);
m_prohibitedEndpoints.insert(endpoint);
}
void
UdpFactory::prohibitAllIpv4Endpoints(uint16_t port)
{
for (const NetworkInterfaceInfo& nic : listNetworkInterfaces()) {
for (const auto& addr : nic.ipv4Addresses) {
if (addr != ip::address_v4::any()) {
prohibitEndpoint(udp::Endpoint(addr, port));
}
}
if (nic.isBroadcastCapable() &&
nic.broadcastAddress != ip::address_v4::any()) {
prohibitEndpoint(udp::Endpoint(nic.broadcastAddress, port));
}
}
prohibitEndpoint(udp::Endpoint(ip::address_v4::broadcast(), port));
}
void
UdpFactory::prohibitAllIpv6Endpoints(uint16_t port)
{
for (const NetworkInterfaceInfo& nic : listNetworkInterfaces()) {
for (const auto& addr : nic.ipv6Addresses) {
if (addr != ip::address_v6::any()) {
prohibitEndpoint(udp::Endpoint(addr, port));
}
}
}
}
shared_ptr<UdpChannel>
UdpFactory::createChannel(const udp::Endpoint& endpoint,
const time::seconds& timeout)
UdpFactory::createChannel(const udp::Endpoint& localEndpoint,
time::nanoseconds idleTimeout)
{
NFD_LOG_DEBUG("Creating unicast channel " << endpoint);
auto it = m_channels.find(localEndpoint);
if (it != m_channels.end())
return it->second;
auto channel = findChannel(endpoint);
if (channel)
return channel;
if (endpoint.address().is_multicast()) {
if (localEndpoint.address().is_multicast()) {
BOOST_THROW_EXCEPTION(Error("createChannel is only for unicast channels. The provided endpoint "
"is multicast. Use createMulticastFace to create a multicast face"));
}
// check if the endpoint is already used by a multicast face
auto face = findMulticastFace(endpoint);
if (face) {
if (m_mcastFaces.find(localEndpoint) != m_mcastFaces.end()) {
BOOST_THROW_EXCEPTION(Error("Cannot create the requested UDP unicast channel, local "
"endpoint is already allocated for a UDP multicast face"));
}
channel = std::make_shared<UdpChannel>(endpoint, timeout);
m_channels[endpoint] = channel;
prohibitEndpoint(endpoint);
auto channel = std::make_shared<UdpChannel>(localEndpoint, idleTimeout);
m_channels[localEndpoint] = channel;
return channel;
}
shared_ptr<UdpChannel>
UdpFactory::createChannel(const std::string& localIp, const std::string& localPort,
const time::seconds& timeout)
{
udp::Endpoint endpoint(ip::address::from_string(localIp),
boost::lexical_cast<uint16_t>(localPort));
return createChannel(endpoint, timeout);
}
std::vector<shared_ptr<const Channel>>
UdpFactory::getChannels() const
{
return getChannelsFromMap(m_channels);
}
shared_ptr<UdpChannel>
UdpFactory::findChannel(const udp::Endpoint& localEndpoint) const
{
auto i = m_channels.find(localEndpoint);
if (i != m_channels.end())
return i->second;
else
return nullptr;
}
shared_ptr<Face>
UdpFactory::createMulticastFace(const udp::Endpoint& localEndpoint,
const udp::Endpoint& multicastEndpoint,
const std::string& networkInterfaceName/* = ""*/)
const std::string& networkInterfaceName)
{
// checking if the local and multicast endpoints are already in use for a multicast face
auto face = findMulticastFace(localEndpoint);
if (face) {
if (face->getRemoteUri() == FaceUri(multicastEndpoint))
return face;
auto it = m_mcastFaces.find(localEndpoint);
if (it != m_mcastFaces.end()) {
if (it->second->getRemoteUri() == FaceUri(multicastEndpoint))
return it->second;
else
BOOST_THROW_EXCEPTION(Error("Cannot create the requested UDP multicast face, local "
"endpoint is already allocated for a UDP multicast face "
@@ -362,17 +307,11 @@ UdpFactory::createMulticastFace(const udp::Endpoint& localEndpoint,
}
// checking if the local endpoint is already in use for a unicast channel
auto unicastCh = findChannel(localEndpoint);
if (unicastCh) {
if (m_channels.find(localEndpoint) != m_channels.end()) {
BOOST_THROW_EXCEPTION(Error("Cannot create the requested UDP multicast face, local "
"endpoint is already allocated for a UDP unicast channel"));
}
if (m_prohibitedEndpoints.find(multicastEndpoint) != m_prohibitedEndpoints.end()) {
BOOST_THROW_EXCEPTION(Error("Cannot create the requested UDP multicast face, "
"remote endpoint is owned by this NFD instance"));
}
if (localEndpoint.address().is_v6() || multicastEndpoint.address().is_v6()) {
BOOST_THROW_EXCEPTION(Error("IPv6 multicast is not supported yet. Please provide an IPv4 "
"address"));
@@ -407,14 +346,12 @@ UdpFactory::createMulticastFace(const udp::Endpoint& localEndpoint,
localEndpoint.address().to_v4()));
#ifdef __linux__
/*
* On Linux, if there is more than one MulticastUdpFace for the same multicast
* group but they are bound to different network interfaces, the socket needs
* to be bound to the specific interface using SO_BINDTODEVICE, otherwise the
* face will receive all packets sent to the other interfaces as well.
* This happens only on Linux. On OS X, the ip::multicast::join_group option
* is enough to get the desired behaviour.
*/
// On Linux, if there is more than one multicast UDP face for the same multicast
// group but they are bound to different network interfaces, the socket needs
// to be bound to the specific interface using SO_BINDTODEVICE, otherwise the
// face will receive all packets sent to the other interfaces as well.
// This happens only on Linux. On macOS, the ip::multicast::join_group option
// is enough to get the desired behaviour.
if (!networkInterfaceName.empty()) {
if (::setsockopt(receiveSocket.native_handle(), SOL_SOCKET, SO_BINDTODEVICE,
networkInterfaceName.c_str(), networkInterfaceName.size() + 1) < 0) {
@@ -427,8 +364,9 @@ UdpFactory::createMulticastFace(const udp::Endpoint& localEndpoint,
auto linkService = make_unique<GenericLinkService>();
auto transport = make_unique<MulticastUdpTransport>(localEndpoint, multicastEndpoint,
std::move(receiveSocket),
std::move(sendSocket));
face = make_shared<Face>(std::move(linkService), std::move(transport));
std::move(sendSocket),
m_mcastConfig.linkType);
auto face = make_shared<Face>(std::move(linkService), std::move(transport));
m_mcastFaces[localEndpoint] = face;
connectFaceClosedSignal(*face, [this, localEndpoint] { m_mcastFaces.erase(localEndpoint); });
@@ -440,65 +378,84 @@ shared_ptr<Face>
UdpFactory::createMulticastFace(const std::string& localIp,
const std::string& multicastIp,
const std::string& multicastPort,
const std::string& networkInterfaceName/* = ""*/)
const std::string& networkInterfaceName)
{
udp::Endpoint localEndpoint(ip::address::from_string(localIp),
udp::Endpoint localEndpoint(ndn::ip::addressFromString(localIp),
boost::lexical_cast<uint16_t>(multicastPort));
udp::Endpoint multicastEndpoint(ip::address::from_string(multicastIp),
udp::Endpoint multicastEndpoint(ndn::ip::addressFromString(multicastIp),
boost::lexical_cast<uint16_t>(multicastPort));
return createMulticastFace(localEndpoint, multicastEndpoint, networkInterfaceName);
}
shared_ptr<Face>
UdpFactory::findMulticastFace(const udp::Endpoint& localEndpoint) const
static ndn::optional<ndn::net::NetworkAddress>
getV4Address(const ndn::net::NetworkInterface& netif)
{
auto i = m_mcastFaces.find(localEndpoint);
if (i != m_mcastFaces.end())
return i->second;
else
using namespace ndn::net;
for (const NetworkAddress& na : netif.getNetworkAddresses()) {
if (na.getFamily() == AddressFamily::V4 && na.getScope() != AddressScope::NOWHERE) {
return na;
}
}
return ndn::nullopt;
}
shared_ptr<Face>
UdpFactory::applyMcastConfigToNetif(const shared_ptr<const ndn::net::NetworkInterface>& netif)
{
if (!m_mcastConfig.isEnabled) {
return nullptr;
}
if (!netif->isUp()) {
NFD_LOG_DEBUG("Not creating multicast face on " << netif->getName() << ": netif is down");
return nullptr;
}
if (!netif->canMulticast()) {
NFD_LOG_DEBUG("Not creating multicast face on " << netif->getName() << ": netif cannot multicast");
return nullptr;
}
auto address = getV4Address(*netif);
if (!address) {
NFD_LOG_DEBUG("Not creating multicast face on " << netif->getName() << ": no IPv4 address");
// keep an eye on new addresses
m_netifConns[netif->getIndex()].addrAddConn =
netif->onAddressAdded.connectSingleShot(bind(&UdpFactory::applyMcastConfigToNetif, this, netif));
return nullptr;
}
if (!m_mcastConfig.netifPredicate(*netif)) {
NFD_LOG_DEBUG("Not creating multicast face on " << netif->getName() << ": rejected by whitelist/blacklist");
return nullptr;
}
NFD_LOG_DEBUG("Creating multicast face on " << netif->getName());
udp::Endpoint localEndpoint(address->getIp(), m_mcastConfig.group.port());
auto face = this->createMulticastFace(localEndpoint, m_mcastConfig.group, netif->getName());
// ifname is only used on Linux. It is not required if there is only one multicast-capable netif,
// but it is always supplied because a new netif can be added at anytime.
if (face->getId() == INVALID_FACEID) {
// new face: register with forwarding
this->addFace(face);
}
return face;
}
void
UdpFactory::applyMulticastConfig(const FaceSystem::ConfigContext& context)
UdpFactory::applyMcastConfig(const FaceSystem::ConfigContext& context)
{
// collect old faces
std::set<shared_ptr<Face>> oldFaces;
boost::copy(m_mcastFaces | boost::adaptors::map_values,
std::inserter(oldFaces, oldFaces.end()));
boost::copy(m_mcastFaces | boost::adaptors::map_values, std::inserter(oldFaces, oldFaces.end()));
if (m_mcastConfig.isEnabled) {
// determine interfaces on which faces should be created or retained
auto capableNetifRange = context.listNetifs() |
boost::adaptors::filtered([this] (const NetworkInterfaceInfo& netif) {
return netif.isUp() && netif.isMulticastCapable() &&
!netif.ipv4Addresses.empty() &&
m_mcastConfig.netifPredicate(netif);
});
bool needIfname = false;
#ifdef __linux__
std::vector<NetworkInterfaceInfo> capableNetifs;
boost::copy(capableNetifRange, std::back_inserter(capableNetifs));
// on Linux, ifname is needed to create more than one UDP multicast face on the same group
needIfname = capableNetifs.size() > 1;
#else
auto& capableNetifs = capableNetifRange;
#endif // __linux__
// create faces
for (const auto& netif : capableNetifs) {
udp::Endpoint localEndpoint(netif.ipv4Addresses.front(), m_mcastConfig.group.port());
shared_ptr<Face> face = this->createMulticastFace(localEndpoint, m_mcastConfig.group,
needIfname ? netif.name : "");
if (face->getId() == INVALID_FACEID) {
// new face: register with forwarding
context.addFace(face);
}
else {
// existing face: don't destroy
oldFaces.erase(face);
}
// create faces if requested by config
for (const auto& netif : netmon->listNetworkInterfaces()) {
auto face = this->applyMcastConfigToNetif(netif);
if (face != nullptr) {
// don't destroy face
oldFaces.erase(face);
}
}
+36 -83
View File
@@ -1,5 +1,5 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
/*
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
@@ -56,6 +56,9 @@ public:
static const std::string&
getId();
explicit
UdpFactory(const CtorParams& params);
/** \brief process face_system.udp config section
*/
void
@@ -63,9 +66,7 @@ public:
FaceSystem::ConfigContext& context) override;
void
createFace(const FaceUri& uri,
ndn::nfd::FacePersistency persistency,
bool wantLocalFieldsEnabled,
createFace(const CreateFaceParams& params,
const FaceCreatedCallback& onCreated,
const FaceCreationFailedCallback& onFailure) override;
@@ -74,80 +75,47 @@ public:
*
* udp::Endpoint is really an alias for boost::asio::ip::udp::endpoint.
*
* If this method called twice with the same endpoint, only one channel
* will be created. The second call will just retrieve the existing
* channel.
* If this method is called twice with the same endpoint, only one channel
* will be created. The second call will just return the existing channel.
*
* If a multicast face is already active on the same local endpoint,
* the creation fails and an exception is thrown
* the creation fails and an exception is thrown.
*
* Once a face is created, if it doesn't send/receive anything for
* a period of time equal to timeout, it will be destroyed
* @todo this funcionality has to be implemented
*
* \returns always a valid pointer to a UdpChannel object, an exception
* is thrown if it cannot be created.
*
* \throws UdpFactory::Error
*
* \see http://www.boost.org/doc/libs/1_42_0/doc/html/boost_asio/reference/ip__udp/endpoint.html
* for details on ways to create udp::Endpoint
* \return always a valid pointer to a UdpChannel object, an exception
* is thrown if it cannot be created.
* \throw UdpFactory::Error
*/
shared_ptr<UdpChannel>
createChannel(const udp::Endpoint& localEndpoint,
const time::seconds& timeout = time::seconds(600));
/**
* \brief Create UDP-based channel using specified IP address and port number
*
* This method is just a helper that converts a string representation of localIp and port to
* udp::Endpoint and calls the other createChannel overload.
*
* If localHost is a IPv6 address of a specific device, it must be in the form:
* ip address%interface name
* Example: fe80::5e96:9dff:fe7d:9c8d%en1
* Otherwise, you can use ::
*
* \throws UdpChannel::Error if the bind on the socket fails
* \throws UdpFactory::Error
*/
shared_ptr<UdpChannel>
createChannel(const std::string& localIp, const std::string& localPort,
const time::seconds& timeout = time::seconds(600));
time::nanoseconds idleTimeout);
std::vector<shared_ptr<const Channel>>
getChannels() const override;
/**
* \brief Create MulticastUdpFace using udp::Endpoint
* \brief Create multicast UDP face using udp::Endpoint
*
* udp::Endpoint is really an alias for boost::asio::ip::udp::endpoint.
*
* The face will join the multicast group
* The face will join the specified multicast group.
*
* If this method called twice with the same endpoint and group, only one face
* will be created. The second call will just retrieve the existing
* channel.
* If this method is called twice with the same pair of endpoints, only one
* face will be created. The second call will just return the existing face.
*
* If an unicast face is already active on the same local NIC and port, the
* creation fails and an exception is thrown
* creation fails and an exception is thrown.
*
* \param localEndpoint local endpoint
* \param multicastEndpoint multicast endpoint
* \param networkInterfaceName name of the network interface on which the face will be bound
* (Used only on multihomed linux machine with more than one MulticastUdpFace for
* the same multicast group. If specified, will requires CAP_NET_RAW capability)
* (Used only on multihomed linux machine with more than one multicast UDP face for
* the same multicast group. If specified, will require CAP_NET_RAW capability)
* An empty string can be provided in other system or in linux machine with only one
* MulticastUdpFace per multicast group
* multicast UDP face per multicast group
*
*
* \returns always a valid pointer to a MulticastUdpFace object, an exception
* is thrown if it cannot be created.
*
* \throws UdpFactory::Error
*
* \see http://www.boost.org/doc/libs/1_42_0/doc/html/boost_asio/reference/ip__udp/endpoint.html
* for details on ways to create udp::Endpoint
* \return always a valid pointer to a MulticastUdpFace object, an exception
* is thrown if it cannot be created.
* \throw UdpFactory::Error
*/
shared_ptr<Face>
createMulticastFace(const udp::Endpoint& localEndpoint,
@@ -160,37 +128,17 @@ public:
const std::string& multicastPort,
const std::string& networkInterfaceName = "");
PUBLIC_WITH_TESTS_ELSE_PRIVATE:
void
prohibitEndpoint(const udp::Endpoint& endpoint);
void
prohibitAllIpv4Endpoints(uint16_t port);
void
prohibitAllIpv6Endpoints(uint16_t port);
private:
/**
* \brief Look up UdpChannel using specified local endpoint
*
* \returns shared pointer to the existing UdpChannel object
* or nullptr when such channel does not exist
*/
shared_ptr<UdpChannel>
findChannel(const udp::Endpoint& localEndpoint) const;
/**
* \brief Look up multicast UdpFace using specified local endpoint
*
* \returns shared pointer to the existing multicast UdpFace object
* or nullptr when such face does not exist
/** \brief Create UDP multicast face on \p netif if needed by \p m_mcastConfig.
* \return new or existing face, or nullptr if no face should be created
*/
shared_ptr<Face>
findMulticastFace(const udp::Endpoint& localEndpoint) const;
applyMcastConfigToNetif(const shared_ptr<const ndn::net::NetworkInterface>& netif);
/** \brief Create and destroy UDP multicast faces according to \p m_mcastConfig.
*/
void
applyMulticastConfig(const FaceSystem::ConfigContext& context);
applyMcastConfig(const FaceSystem::ConfigContext& context);
private:
std::map<udp::Endpoint, shared_ptr<UdpChannel>> m_channels;
@@ -199,14 +147,19 @@ private:
{
bool isEnabled = false;
udp::Endpoint group = udp::getDefaultMulticastGroup();
ndn::nfd::LinkType linkType = ndn::nfd::LINK_TYPE_MULTI_ACCESS;
NetworkInterfacePredicate netifPredicate;
};
MulticastConfig m_mcastConfig;
std::map<udp::Endpoint, shared_ptr<Face>> m_mcastFaces;
PUBLIC_WITH_TESTS_ELSE_PRIVATE:
std::set<udp::Endpoint> m_prohibitedEndpoints;
signal::ScopedConnection m_netifAddConn;
struct NetifConns
{
signal::ScopedConnection addrAddConn;
};
std::map<int, NetifConns> m_netifConns; // ifindex => signal connections
};
} // namespace face
+105
View File
@@ -0,0 +1,105 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
* Washington University in St. Louis,
* Beijing Institute of Technology,
* The University of Memphis.
*
* This file is part of NFD (Named Data Networking Forwarding Daemon).
* See AUTHORS.md for complete list of NFD authors and contributors.
*
* NFD is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Foundation,
* either version 3 of the License, or (at your option) any later version.
*
* NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
*/
#include "unicast-ethernet-transport.hpp"
#include <stdio.h> // for snprintf()
namespace nfd {
namespace face {
NFD_LOG_INIT("UnicastEthernetTransport");
UnicastEthernetTransport::UnicastEthernetTransport(const ndn::net::NetworkInterface& localEndpoint,
const ethernet::Address& remoteEndpoint,
ndn::nfd::FacePersistency persistency,
time::nanoseconds idleTimeout)
: EthernetTransport(localEndpoint, remoteEndpoint)
, m_idleTimeout(idleTimeout)
{
this->setLocalUri(FaceUri::fromDev(m_interfaceName));
this->setRemoteUri(FaceUri(m_destAddress));
this->setScope(ndn::nfd::FACE_SCOPE_NON_LOCAL);
this->setPersistency(persistency);
this->setLinkType(ndn::nfd::LINK_TYPE_POINT_TO_POINT);
this->setMtu(localEndpoint.getMtu());
NFD_LOG_FACE_INFO("Creating transport");
char filter[110];
// note #1: we cannot use std::snprintf because it's not available
// on some platforms (see #2299)
// note #2: "not vlan" must appear last in the filter expression, or the
// rest of the filter won't work as intended (see pcap-filter(7))
snprintf(filter, sizeof(filter),
"(ether proto 0x%x) && (ether src %s) && (ether dst %s) && (not vlan)",
ethernet::ETHERTYPE_NDN,
m_destAddress.toString().data(),
m_srcAddress.toString().data());
m_pcap.setPacketFilter(filter);
if (getPersistency() == ndn::nfd::FACE_PERSISTENCY_ON_DEMAND &&
m_idleTimeout > time::nanoseconds::zero()) {
scheduleClosureWhenIdle();
}
}
bool
UnicastEthernetTransport::canChangePersistencyToImpl(ndn::nfd::FacePersistency newPersistency) const
{
return true;
}
void
UnicastEthernetTransport::afterChangePersistency(ndn::nfd::FacePersistency oldPersistency)
{
if (getPersistency() == ndn::nfd::FACE_PERSISTENCY_ON_DEMAND &&
m_idleTimeout > time::nanoseconds::zero()) {
scheduleClosureWhenIdle();
}
else {
m_closeIfIdleEvent.cancel();
setExpirationTime(time::steady_clock::TimePoint::max());
}
}
void
UnicastEthernetTransport::scheduleClosureWhenIdle()
{
m_closeIfIdleEvent = scheduler::schedule(m_idleTimeout, [this] {
if (!hasRecentlyReceived()) {
NFD_LOG_FACE_INFO("Closing due to inactivity");
this->close();
}
else {
resetRecentlyReceived();
scheduleClosureWhenIdle();
}
});
setExpirationTime(time::steady_clock::now() + m_idleTimeout);
}
} // namespace face
} // namespace nfd
@@ -0,0 +1,68 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
* Washington University in St. Louis,
* Beijing Institute of Technology,
* The University of Memphis.
*
* This file is part of NFD (Named Data Networking Forwarding Daemon).
* See AUTHORS.md for complete list of NFD authors and contributors.
*
* NFD is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Foundation,
* either version 3 of the License, or (at your option) any later version.
*
* NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef NFD_DAEMON_FACE_UNICAST_ETHERNET_TRANSPORT_HPP
#define NFD_DAEMON_FACE_UNICAST_ETHERNET_TRANSPORT_HPP
#include "ethernet-transport.hpp"
#include "core/scheduler.hpp"
namespace nfd {
namespace face {
/**
* @brief A unicast Transport that uses raw Ethernet II frames
*/
class UnicastEthernetTransport final : public EthernetTransport
{
public:
/**
* @brief Creates an Ethernet-based transport for unicast communication
*/
UnicastEthernetTransport(const ndn::net::NetworkInterface& localEndpoint,
const ethernet::Address& remoteEndpoint,
ndn::nfd::FacePersistency persistency,
time::nanoseconds idleTimeout);
protected:
bool
canChangePersistencyToImpl(ndn::nfd::FacePersistency newPersistency) const final;
void
afterChangePersistency(ndn::nfd::FacePersistency oldPersistency) final;
private:
void
scheduleClosureWhenIdle();
private:
const time::nanoseconds m_idleTimeout;
scheduler::ScopedEventId m_closeIfIdleEvent;
};
} // namespace face
} // namespace nfd
#endif // NFD_DAEMON_FACE_UNICAST_ETHERNET_TRANSPORT_HPP
+2 -2
View File
@@ -103,12 +103,12 @@ void
UnicastUdpTransport::scheduleClosureWhenIdle()
{
m_closeIfIdleEvent = scheduler::schedule(m_idleTimeout, [this] {
if (!hasBeenUsedRecently()) {
if (!hasRecentlyReceived()) {
NFD_LOG_FACE_INFO("Closing due to inactivity");
this->close();
}
else {
resetRecentUsage();
resetRecentlyReceived();
scheduleClosureWhenIdle();
}
});
+23 -18
View File
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2014-2016, Regents of the University of California,
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -32,6 +32,7 @@
#include <sys/stat.h> // for chmod()
namespace nfd {
namespace face {
NFD_LOG_INIT("UnixStreamChannel");
@@ -39,8 +40,10 @@ UnixStreamChannel::UnixStreamChannel(const unix_stream::Endpoint& endpoint)
: m_endpoint(endpoint)
, m_acceptor(getGlobalIoService())
, m_socket(getGlobalIoService())
, m_size(0)
{
setUri(FaceUri(m_endpoint));
NFD_LOG_CHAN_INFO("Creating channel");
}
UnixStreamChannel::~UnixStreamChannel()
@@ -50,7 +53,7 @@ UnixStreamChannel::~UnixStreamChannel()
// and ignore any errors
boost::system::error_code error;
m_acceptor.close(error);
NFD_LOG_DEBUG("[" << m_endpoint << "] Removing socket file");
NFD_LOG_CHAN_DEBUG("Removing socket file");
boost::filesystem::remove(m_endpoint.path(), error);
}
}
@@ -61,7 +64,7 @@ UnixStreamChannel::listen(const FaceCreatedCallback& onFaceCreated,
int backlog/* = acceptor::max_connections*/)
{
if (isListening()) {
NFD_LOG_WARN("[" << m_endpoint << "] Already listening");
NFD_LOG_CHAN_WARN("Already listening");
return;
}
@@ -74,8 +77,7 @@ UnixStreamChannel::listen(const FaceCreatedCallback& onFaceCreated,
boost::system::error_code error;
boost::asio::local::stream_protocol::socket socket(getGlobalIoService());
socket.connect(m_endpoint, error);
NFD_LOG_TRACE("[" << m_endpoint << "] connect() on existing socket file returned: "
+ error.message());
NFD_LOG_CHAN_TRACE("connect() on existing socket file returned: " << error.message());
if (!error) {
// someone answered, leave the socket alone
BOOST_THROW_EXCEPTION(Error("Socket file at " + m_endpoint.path()
@@ -85,7 +87,7 @@ UnixStreamChannel::listen(const FaceCreatedCallback& onFaceCreated,
error == boost::asio::error::timed_out) {
// no one is listening on the remote side,
// we can safely remove the stale socket
NFD_LOG_DEBUG("[" << m_endpoint << "] Removing stale socket file");
NFD_LOG_CHAN_DEBUG("Removing stale socket file");
fs::remove(socketPath);
}
}
@@ -98,12 +100,11 @@ UnixStreamChannel::listen(const FaceCreatedCallback& onFaceCreated,
m_acceptor.listen(backlog);
if (::chmod(m_endpoint.path().c_str(), 0666) < 0) {
BOOST_THROW_EXCEPTION(Error("chmod(" + m_endpoint.path() + ") failed: " +
std::strerror(errno)));
BOOST_THROW_EXCEPTION(Error("chmod(" + m_endpoint.path() + ") failed: " + std::strerror(errno)));
}
// start accepting connections
accept(onFaceCreated, onAcceptFailed);
NFD_LOG_CHAN_DEBUG("Started listening");
}
void
@@ -121,24 +122,28 @@ UnixStreamChannel::handleAccept(const boost::system::error_code& error,
const FaceCreationFailedCallback& onAcceptFailed)
{
if (error) {
if (error == boost::asio::error::operation_aborted) // when the socket is closed by someone
return;
NFD_LOG_DEBUG("[" << m_endpoint << "] Accept failed: " << error.message());
if (onAcceptFailed)
onAcceptFailed(500, "Accept failed: " + error.message());
if (error != boost::asio::error::operation_aborted) {
NFD_LOG_CHAN_DEBUG("Accept failed: " << error.message());
if (onAcceptFailed)
onAcceptFailed(500, "Accept failed: " + error.message());
}
return;
}
NFD_LOG_DEBUG("[" << m_endpoint << "] Incoming connection");
NFD_LOG_CHAN_TRACE("Incoming connection via fd " << m_socket.native_handle());
auto linkService = make_unique<face::GenericLinkService>();
auto transport = make_unique<face::UnixStreamTransport>(std::move(m_socket));
auto linkService = make_unique<GenericLinkService>();
auto transport = make_unique<UnixStreamTransport>(std::move(m_socket));
auto face = make_shared<Face>(std::move(linkService), std::move(transport));
++m_size;
connectFaceClosedSignal(*face, [this] { --m_size; });
onFaceCreated(face);
// prepare accepting the next connection
accept(onFaceCreated, onAcceptFailed);
}
} // namespace face
} // namespace nfd
+33 -15
View File
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2014-2016, Regents of the University of California,
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -34,6 +34,8 @@ namespace unix_stream {
typedef boost::asio::local::stream_protocol::endpoint Endpoint;
} // namespace unix_stream
namespace face {
/**
* \brief Class implementing a local channel to create faces
*
@@ -46,9 +48,14 @@ public:
/**
* \brief UnixStreamChannel-related error
*/
struct Error : public std::runtime_error
class Error : public std::runtime_error
{
Error(const std::string& what) : std::runtime_error(what) {}
public:
explicit
Error(const std::string& what)
: std::runtime_error(what)
{
}
};
/**
@@ -62,23 +69,38 @@ public:
~UnixStreamChannel() override;
bool
isListening() const override
{
return m_acceptor.is_open();
}
size_t
size() const override
{
return m_size;
}
/**
* \brief Enable listening on the local endpoint, accept connections,
* and create faces when a connection is made
* \brief Start listening
*
* Enable listening on the Unix socket, waiting for incoming connections,
* and creating a face when a connection is made.
*
* Faces created in this way will have on-demand persistency.
*
* \param onFaceCreated Callback to notify successful creation of the face
* \param onAcceptFailed Callback to notify when channel fails (accept call
* returns an error)
* \param backlog The maximum length of the queue of pending incoming
* connections
* \throw Error
*/
void
listen(const FaceCreatedCallback& onFaceCreated,
const FaceCreationFailedCallback& onAcceptFailed,
int backlog = boost::asio::local::stream_protocol::acceptor::max_connections);
bool
isListening() const;
private:
void
accept(const FaceCreatedCallback& onFaceCreated,
@@ -90,17 +112,13 @@ private:
const FaceCreationFailedCallback& onAcceptFailed);
private:
unix_stream::Endpoint m_endpoint;
const unix_stream::Endpoint m_endpoint;
boost::asio::local::stream_protocol::acceptor m_acceptor;
boost::asio::local::stream_protocol::socket m_socket;
size_t m_size;
};
inline bool
UnixStreamChannel::isListening() const
{
return m_acceptor.is_open();
}
} // namespace face
} // namespace nfd
#endif // NFD_DAEMON_FACE_UNIX_STREAM_CHANNEL_HPP
+7 -6
View File
@@ -1,5 +1,5 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
/*
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
@@ -24,7 +24,6 @@
*/
#include "unix-stream-factory.hpp"
#include "core/logger.hpp"
#include <boost/filesystem.hpp>
@@ -41,6 +40,10 @@ UnixStreamFactory::getId()
return id;
}
UnixStreamFactory::UnixStreamFactory(const CtorParams& params)
: ProtocolFactory(params)
{
}
void
UnixStreamFactory::processConfig(OptionalConfigSection configSection,
@@ -75,15 +78,13 @@ UnixStreamFactory::processConfig(OptionalConfigSection configSection,
if (!context.isDryRun) {
auto channel = this->createChannel(path);
if (!channel->isListening()) {
channel->listen(context.addFace, nullptr);
channel->listen(this->addFace, nullptr);
}
}
}
void
UnixStreamFactory::createFace(const FaceUri& uri,
ndn::nfd::FacePersistency persistency,
bool wantLocalFieldsEnabled,
UnixStreamFactory::createFace(const CreateFaceParams& params,
const FaceCreatedCallback& onCreated,
const FaceCreationFailedCallback& onFailure)
{
+5 -4
View File
@@ -1,5 +1,5 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
/*
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
@@ -40,6 +40,9 @@ public:
static const std::string&
getId();
explicit
UnixStreamFactory(const CtorParams& params);
/** \brief process face_system.unix config section
*/
void
@@ -47,9 +50,7 @@ public:
FaceSystem::ConfigContext& context) override;
void
createFace(const FaceUri& uri,
ndn::nfd::FacePersistency persistency,
bool wantLocalFieldsEnabled,
createFace(const CreateFaceParams& params,
const FaceCreatedCallback& onCreated,
const FaceCreationFailedCallback& onFailure) override;
+21 -19
View File
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2014-2015, Regents of the University of California,
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -29,14 +29,16 @@
#include "core/global-io.hpp"
namespace nfd {
namespace face {
NFD_LOG_INIT("WebSocketChannel");
WebSocketChannel::WebSocketChannel(const websocket::Endpoint& localEndpoint)
: m_localEndpoint(localEndpoint)
, m_pingInterval(10000)
, m_pingInterval(time::seconds(10))
{
setUri(FaceUri(m_localEndpoint, "ws"));
NFD_LOG_CHAN_INFO("Creating channel");
// Be quiet
m_server.clear_access_channels(websocketpp::log::alevel::all);
@@ -77,10 +79,10 @@ WebSocketChannel::handlePongTimeout(websocketpp::connection_hdl hdl)
{
auto it = m_channelFaces.find(hdl);
if (it != m_channelFaces.end()) {
static_cast<face::WebSocketTransport*>(it->second->getTransport())->handlePongTimeout();
static_cast<WebSocketTransport*>(it->second->getTransport())->handlePongTimeout();
}
else {
NFD_LOG_WARN("Pong timeout on unknown transport");
NFD_LOG_CHAN_WARN("Pong timeout on unknown transport");
}
}
@@ -89,10 +91,10 @@ WebSocketChannel::handlePong(websocketpp::connection_hdl hdl)
{
auto it = m_channelFaces.find(hdl);
if (it != m_channelFaces.end()) {
static_cast<face::WebSocketTransport*>(it->second->getTransport())->handlePong();
static_cast<WebSocketTransport*>(it->second->getTransport())->handlePong();
}
else {
NFD_LOG_WARN("Pong received on unknown transport");
NFD_LOG_CHAN_WARN("Pong received on unknown transport");
}
}
@@ -102,20 +104,23 @@ WebSocketChannel::handleMessage(websocketpp::connection_hdl hdl,
{
auto it = m_channelFaces.find(hdl);
if (it != m_channelFaces.end()) {
static_cast<face::WebSocketTransport*>(it->second->getTransport())->receiveMessage(msg->get_payload());
static_cast<WebSocketTransport*>(it->second->getTransport())->receiveMessage(msg->get_payload());
}
else {
NFD_LOG_WARN("Message received on unknown transport");
NFD_LOG_CHAN_WARN("Message received on unknown transport");
}
}
void
WebSocketChannel::handleOpen(websocketpp::connection_hdl hdl)
{
auto linkService = make_unique<face::GenericLinkService>();
auto transport = make_unique<face::WebSocketTransport>(hdl, ref(m_server), m_pingInterval);
NFD_LOG_CHAN_TRACE("Incoming connection from " << m_server.get_con_from_hdl(hdl)->get_remote_endpoint());
auto linkService = make_unique<GenericLinkService>();
auto transport = make_unique<WebSocketTransport>(hdl, ref(m_server), m_pingInterval);
auto face = make_shared<Face>(std::move(linkService), std::move(transport));
BOOST_ASSERT(m_channelFaces.count(hdl) == 0);
m_channelFaces[hdl] = face;
connectFaceClosedSignal(*face, [this, hdl] { m_channelFaces.erase(hdl); });
@@ -130,27 +135,24 @@ WebSocketChannel::handleClose(websocketpp::connection_hdl hdl)
it->second->close();
}
else {
NFD_LOG_WARN("Close on unknown transport");
NFD_LOG_CHAN_WARN("Close on unknown transport");
}
}
void
WebSocketChannel::listen(const FaceCreatedCallback& onFaceCreated)
{
if (m_server.is_listening()) {
NFD_LOG_WARN("[" << m_localEndpoint << "] Already listening");
if (isListening()) {
NFD_LOG_CHAN_WARN("Already listening");
return;
}
m_onFaceCreatedCallback = onFaceCreated;
m_server.listen(m_localEndpoint);
m_server.start_accept();
NFD_LOG_CHAN_DEBUG("Started listening");
}
size_t
WebSocketChannel::size() const
{
return m_channelFaces.size();
}
} // namespace face
} // namespace nfd
+17 -19
View File
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2014-2015, Regents of the University of California,
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -35,6 +35,8 @@ namespace websocket {
typedef boost::asio::ip::tcp::endpoint Endpoint;
} // namespace websocket
namespace face {
/**
* \brief Class implementing WebSocket-based channel to create faces
*/
@@ -51,6 +53,18 @@ public:
explicit
WebSocketChannel(const websocket::Endpoint& localEndpoint);
bool
isListening() const override
{
return m_server.is_listening();
}
size_t
size() const override
{
return m_channelFaces.size();
}
/**
* \brief Enable listening on the local endpoint, accept connections,
* and create faces when remote host makes a connection
@@ -60,15 +74,6 @@ public:
void
listen(const FaceCreatedCallback& onFaceCreated);
/**
* \brief Get number of faces in the channel
*/
size_t
size() const;
bool
isListening() const;
PUBLIC_WITH_TESTS_ELSE_PRIVATE:
/** \pre listen hasn't been invoked
*/
@@ -98,22 +103,15 @@ private:
handleClose(websocketpp::connection_hdl hdl);
private:
websocket::Endpoint m_localEndpoint;
const websocket::Endpoint m_localEndpoint;
websocket::Server m_server;
std::map<websocketpp::connection_hdl, shared_ptr<Face>,
std::owner_less<websocketpp::connection_hdl>> m_channelFaces;
FaceCreatedCallback m_onFaceCreatedCallback;
time::milliseconds m_pingInterval;
};
inline bool
WebSocketChannel::isListening() const
{
return m_server.is_listening();
}
} // namespace face
} // namespace nfd
#endif // NFD_DAEMON_FACE_WEBSOCKET_CHANNEL_HPP
+11 -28
View File
@@ -1,5 +1,5 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
/*
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
@@ -24,7 +24,6 @@
*/
#include "websocket-factory.hpp"
#include "core/logger.hpp"
namespace nfd {
namespace face {
@@ -41,6 +40,10 @@ WebSocketFactory::getId()
return id;
}
WebSocketFactory::WebSocketFactory(const CtorParams& params)
: ProtocolFactory(params)
{
}
void
WebSocketFactory::processConfig(OptionalConfigSection configSection,
@@ -106,7 +109,7 @@ WebSocketFactory::processConfig(OptionalConfigSection configSection,
auto channel = this->createChannel(endpoint);
if (!channel->isListening()) {
channel->listen(context.addFace);
channel->listen(this->addFace);
if (m_channels.size() > 1) {
NFD_LOG_WARN("Adding WebSocket channel for new endpoint; cannot close existing channels");
}
@@ -115,9 +118,7 @@ WebSocketFactory::processConfig(OptionalConfigSection configSection,
}
void
WebSocketFactory::createFace(const FaceUri& uri,
ndn::nfd::FacePersistency persistency,
bool wantLocalFieldsEnabled,
WebSocketFactory::createFace(const CreateFaceParams& params,
const FaceCreatedCallback& onCreated,
const FaceCreationFailedCallback& onFailure)
{
@@ -127,39 +128,21 @@ WebSocketFactory::createFace(const FaceUri& uri,
shared_ptr<WebSocketChannel>
WebSocketFactory::createChannel(const websocket::Endpoint& endpoint)
{
auto channel = findChannel(endpoint);
if (channel)
return channel;
auto it = m_channels.find(endpoint);
if (it != m_channels.end())
return it->second;
channel = make_shared<WebSocketChannel>(endpoint);
auto channel = make_shared<WebSocketChannel>(endpoint);
m_channels[endpoint] = channel;
return channel;
}
shared_ptr<WebSocketChannel>
WebSocketFactory::createChannel(const std::string& localIp, const std::string& localPort)
{
websocket::Endpoint endpoint(ip::address::from_string(localIp),
boost::lexical_cast<uint16_t>(localPort));
return createChannel(endpoint);
}
std::vector<shared_ptr<const Channel>>
WebSocketFactory::getChannels() const
{
return getChannelsFromMap(m_channels);
}
shared_ptr<WebSocketChannel>
WebSocketFactory::findChannel(const websocket::Endpoint& endpoint) const
{
auto i = m_channels.find(endpoint);
if (i != m_channels.end())
return i->second;
else
return nullptr;
}
} // namespace face
} // namespace nfd
+5 -23
View File
@@ -1,5 +1,5 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
/*
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
@@ -40,6 +40,9 @@ public:
static const std::string&
getId();
explicit
WebSocketFactory(const CtorParams& params);
/** \brief process face_system.websocket config section
*/
void
@@ -49,9 +52,7 @@ public:
/** \brief unicast face creation is not supported and will always fail
*/
void
createFace(const FaceUri& uri,
ndn::nfd::FacePersistency persistency,
bool wantLocalFieldsEnabled,
createFace(const CreateFaceParams& params,
const FaceCreatedCallback& onCreated,
const FaceCreationFailedCallback& onFailure) override;
@@ -70,28 +71,9 @@ public:
shared_ptr<WebSocketChannel>
createChannel(const websocket::Endpoint& localEndpoint);
/**
* \brief Create WebSocket-based channel using specified IP address and port number
*
* This method is just a helper that converts a string representation of localIp and port to
* websocket::Endpoint and calls the other createChannel overload.
*/
shared_ptr<WebSocketChannel>
createChannel(const std::string& localIp, const std::string& localPort);
std::vector<shared_ptr<const Channel>>
getChannels() const override;
private:
/**
* \brief Look up WebSocketChannel using specified local endpoint
*
* \returns shared pointer to the existing WebSocketChannel object
* or empty shared pointer when such channel does not exist
*/
shared_ptr<WebSocketChannel>
findChannel(const websocket::Endpoint& endpoint) const;
private:
std::map<websocket::Endpoint, shared_ptr<WebSocketChannel>> m_channels;
};
+4 -5
View File
@@ -117,13 +117,13 @@ WebSocketTransport::sendPing()
{
NFD_LOG_FACE_TRACE(__func__);
++this->nOutPings;
websocketpp::lib::error_code error;
m_server.ping(m_handle, "NFD-WebSocket", error);
if (error)
return processErrorCode(error);
++this->nOutPings;
this->schedulePing();
}
@@ -138,7 +138,7 @@ WebSocketTransport::handlePong()
void
WebSocketTransport::handlePongTimeout()
{
NFD_LOG_FACE_WARN(__func__);
NFD_LOG_FACE_ERROR("Pong timeout");
this->setState(TransportState::FAILED);
doClose();
}
@@ -154,8 +154,7 @@ WebSocketTransport::processErrorCode(const websocketpp::lib::error_code& error)
// transport is shutting down, ignore any errors
return;
NFD_LOG_FACE_WARN("Send or ping operation failed: " << error.message());
NFD_LOG_FACE_ERROR("Send or ping operation failed: " << error.message());
this->setState(TransportState::FAILED);
doClose();
}
+6 -6
View File
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2014-2016, Regents of the University of California,
/*
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -60,15 +60,15 @@ void
AccessStrategy::afterReceiveInterest(const Face& inFace, const Interest& interest,
const shared_ptr<pit::Entry>& pitEntry)
{
RetxSuppression::Result suppressResult = m_retxSuppression.decide(inFace, interest, *pitEntry);
RetxSuppressionResult suppressResult = m_retxSuppression.decidePerPitEntry(*pitEntry);
switch (suppressResult) {
case RetxSuppression::NEW:
case RetxSuppressionResult::NEW:
this->afterReceiveNewInterest(inFace, interest, pitEntry);
break;
case RetxSuppression::FORWARD:
case RetxSuppressionResult::FORWARD:
this->afterReceiveRetxInterest(inFace, interest, pitEntry);
break;
case RetxSuppression::SUPPRESS:
case RetxSuppressionResult::SUPPRESS:
NFD_LOG_DEBUG(interest << " interestFrom " << inFace.getId() << " retx-suppress");
break;
default:
+4 -4
View File
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2014-2016, Regents of the University of California,
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -27,7 +27,7 @@
#define NFD_DAEMON_FW_ACCESS_STRATEGY_HPP
#include "strategy.hpp"
#include "rtt-estimator.hpp"
#include "core/rtt-estimator.hpp"
#include "retx-suppression-fixed.hpp"
#include <unordered_set>
#include <unordered_map>
@@ -56,11 +56,11 @@ public:
getStrategyName();
public: // triggers
virtual void
void
afterReceiveInterest(const Face& inFace, const Interest& interest,
const shared_ptr<pit::Entry>& pitEntry) override;
virtual void
void
beforeSatisfyInterest(const shared_ptr<pit::Entry>& pitEntry,
const Face& inFace, const Data& data) override;
+15 -2
View File
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2014-2016, Regents of the University of California,
/*
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -120,5 +120,18 @@ hasPendingOutRecords(const pit::Entry& pitEntry)
});
}
time::steady_clock::TimePoint
getLastOutgoing(const pit::Entry& pitEntry)
{
pit::OutRecordCollection::const_iterator lastOutgoing = std::max_element(
pitEntry.out_begin(), pitEntry.out_end(),
[] (const pit::OutRecord& a, const pit::OutRecord& b) {
return a.getLastRenewed() < b.getLastRenewed();
});
BOOST_ASSERT(lastOutgoing != pitEntry.out_end());
return lastOutgoing->getLastRenewed();
}
} // namespace fw
} // namespace nfd
+8 -2
View File
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2014-2016, Regents of the University of California,
/*
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -108,6 +108,12 @@ findDuplicateNonce(const pit::Entry& pitEntry, uint32_t nonce, const Face& face)
bool
hasPendingOutRecords(const pit::Entry& pitEntry);
/** \return last out-record time
* \pre pitEntry has one or more unexpired out-records
*/
time::steady_clock::TimePoint
getLastOutgoing(const pit::Entry& pitEntry);
} // namespace fw
} // namespace nfd
+9 -4
View File
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2014-2016, Regents of the University of California,
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -26,7 +26,7 @@
#ifndef NFD_DAEMON_FW_ASF_MEASUREMENTS_HPP
#define NFD_DAEMON_FW_ASF_MEASUREMENTS_HPP
#include "fw/rtt-estimator.hpp"
#include "core/rtt-estimator.hpp"
#include "fw/strategy-info.hpp"
#include "table/measurements-accessor.hpp"
@@ -215,10 +215,15 @@ public:
void
extendFaceInfoLifetime(FaceInfo& info, const Face& face);
FaceInfo&
FaceInfo*
get(nfd::face::FaceId faceId)
{
return m_fit.at(faceId);
if (m_fit.find(faceId) != m_fit.end()) {
return &m_fit.at(faceId);
}
else {
return nullptr;
}
}
FaceInfoTable::iterator
+19 -13
View File
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2014-2016, Regents of the University of California,
/*
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -60,7 +60,7 @@ AsfStrategy::AsfStrategy(Forwarder& forwarder, const Name& name)
const Name&
AsfStrategy::getStrategyName()
{
static Name strategyName("/localhost/nfd/strategy/asf/%FD%01");
static Name strategyName("/localhost/nfd/strategy/asf/%FD%02");
return strategyName;
}
@@ -69,13 +69,13 @@ AsfStrategy::afterReceiveInterest(const Face& inFace, const Interest& interest,
const shared_ptr<pit::Entry>& pitEntry)
{
// Should the Interest be suppressed?
RetxSuppression::Result suppressResult = m_retxSuppression.decide(inFace, interest, *pitEntry);
RetxSuppressionResult suppressResult = m_retxSuppression.decidePerPitEntry(*pitEntry);
switch (suppressResult) {
case RetxSuppression::NEW:
case RetxSuppression::FORWARD:
case RetxSuppressionResult::NEW:
case RetxSuppressionResult::FORWARD:
break;
case RetxSuppression::SUPPRESS:
case RetxSuppressionResult::SUPPRESS:
NFD_LOG_DEBUG(interest << " from=" << inFace.getId() << " suppressed");
return;
}
@@ -97,6 +97,8 @@ AsfStrategy::afterReceiveInterest(const Face& inFace, const Interest& interest,
return;
}
NFD_LOG_TRACE("Forwarding interest to face: " << faceToUse->getId());
forwardInterest(interest, fibEntry, pitEntry, *faceToUse);
// If necessary, send probe
@@ -123,14 +125,17 @@ AsfStrategy::beforeSatisfyInterest(const shared_ptr<pit::Entry>& pitEntry,
}
// Record the RTT between the Interest out to Data in
FaceInfo& faceInfo = namespaceInfo->get(inFace.getId());
faceInfo.recordRtt(pitEntry, inFace);
FaceInfo* faceInfo = namespaceInfo->get(inFace.getId());
if (faceInfo == nullptr) {
return;
}
faceInfo->recordRtt(pitEntry, inFace);
// Extend lifetime for measurements associated with Face
namespaceInfo->extendFaceInfoLifetime(faceInfo, inFace);
namespaceInfo->extendFaceInfoLifetime(*faceInfo, inFace);
if (faceInfo.isTimeoutScheduled()) {
faceInfo.cancelTimeoutEvent(data.getName());
if (faceInfo->isTimeoutScheduled()) {
faceInfo->cancelTimeoutEvent(data.getName());
}
}
@@ -241,7 +246,8 @@ AsfStrategy::getBestFaceForForwarding(const fib::Entry& fibEntry, const Interest
for (const fib::NextHop& hop : fibEntry.getNextHops()) {
Face& hopFace = hop.getFace();
if (hopFace.getId() == inFace.getId() || wouldViolateScope(inFace, interest, hopFace)) {
if ((hopFace.getId() == inFace.getId() && hopFace.getLinkType() != ndn::nfd::LINK_TYPE_AD_HOC) ||
wouldViolateScope(inFace, interest, hopFace)) {
continue;
}
+10 -67
View File
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2014-2016, Regents of the University of California,
/*
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -38,6 +38,7 @@ const time::milliseconds BestRouteStrategy2::RETX_SUPPRESSION_MAX(250);
BestRouteStrategy2::BestRouteStrategy2(Forwarder& forwarder, const Name& name)
: Strategy(forwarder)
, ProcessNackTraits(this)
, m_retxSuppression(RETX_SUPPRESSION_INITIAL,
RetxSuppressionExponential::DEFAULT_MULTIPLIER,
RETX_SUPPRESSION_MAX)
@@ -56,7 +57,7 @@ BestRouteStrategy2::BestRouteStrategy2(Forwarder& forwarder, const Name& name)
const Name&
BestRouteStrategy2::getStrategyName()
{
static Name strategyName("/localhost/nfd/strategy/best-route/%FD%04");
static Name strategyName("/localhost/nfd/strategy/best-route/%FD%05");
return strategyName;
}
@@ -77,8 +78,8 @@ isNextHopEligible(const Face& inFace, const Interest& interest,
{
const Face& outFace = nexthop.getFace();
// do not forward back to the same face
if (&outFace == &inFace)
// do not forward back to the same face, unless it is ad hoc
if (outFace.getId() == inFace.getId() && outFace.getLinkType() != ndn::nfd::LINK_TYPE_AD_HOC)
return false;
// forwarding would violate scope
@@ -123,8 +124,8 @@ void
BestRouteStrategy2::afterReceiveInterest(const Face& inFace, const Interest& interest,
const shared_ptr<pit::Entry>& pitEntry)
{
RetxSuppression::Result suppression = m_retxSuppression.decide(inFace, interest, *pitEntry);
if (suppression == RetxSuppression::SUPPRESS) {
RetxSuppressionResult suppression = m_retxSuppression.decidePerPitEntry(*pitEntry);
if (suppression == RetxSuppressionResult::SUPPRESS) {
NFD_LOG_DEBUG(interest << " from=" << inFace.getId()
<< " suppressed");
return;
@@ -134,7 +135,7 @@ BestRouteStrategy2::afterReceiveInterest(const Face& inFace, const Interest& int
const fib::NextHopList& nexthops = fibEntry.getNextHops();
fib::NextHopList::const_iterator it = nexthops.end();
if (suppression == RetxSuppression::NEW) {
if (suppression == RetxSuppressionResult::NEW) {
// forward to nexthop with lowest cost except downstream
it = std::find_if(nexthops.begin(), nexthops.end(),
bind(&isNextHopEligible, cref(inFace), interest, _1, pitEntry,
@@ -183,69 +184,11 @@ BestRouteStrategy2::afterReceiveInterest(const Face& inFace, const Interest& int
}
}
/** \return less severe NackReason between x and y
*
* lp::NackReason::NONE is treated as most severe
*/
inline lp::NackReason
compareLessSevere(lp::NackReason x, lp::NackReason y)
{
if (x == lp::NackReason::NONE) {
return y;
}
if (y == lp::NackReason::NONE) {
return x;
}
return static_cast<lp::NackReason>(std::min(static_cast<int>(x), static_cast<int>(y)));
}
void
BestRouteStrategy2::afterReceiveNack(const Face& inFace, const lp::Nack& nack,
const shared_ptr<pit::Entry>& pitEntry)
{
int nOutRecordsNotNacked = 0;
Face* lastFaceNotNacked = nullptr;
lp::NackReason leastSevereReason = lp::NackReason::NONE;
for (const pit::OutRecord& outR : pitEntry->getOutRecords()) {
const lp::NackHeader* inNack = outR.getIncomingNack();
if (inNack == nullptr) {
++nOutRecordsNotNacked;
lastFaceNotNacked = &outR.getFace();
continue;
}
leastSevereReason = compareLessSevere(leastSevereReason, inNack->getReason());
}
lp::NackHeader outNack;
outNack.setReason(leastSevereReason);
if (nOutRecordsNotNacked == 1) {
BOOST_ASSERT(lastFaceNotNacked != nullptr);
pit::InRecordCollection::iterator inR = pitEntry->getInRecord(*lastFaceNotNacked);
if (inR != pitEntry->in_end()) {
// one out-record not Nacked, which is also a downstream
NFD_LOG_DEBUG(nack.getInterest() << " nack-from=" << inFace.getId() <<
" nack=" << nack.getReason() <<
" nack-to(bidirectional)=" << lastFaceNotNacked->getId() <<
" out-nack=" << outNack.getReason());
this->sendNack(pitEntry, *lastFaceNotNacked, outNack);
return;
}
}
if (nOutRecordsNotNacked > 0) {
NFD_LOG_DEBUG(nack.getInterest() << " nack-from=" << inFace.getId() <<
" nack=" << nack.getReason() <<
" waiting=" << nOutRecordsNotNacked);
// continue waiting
return;
}
NFD_LOG_DEBUG(nack.getInterest() << " nack-from=" << inFace.getId() <<
" nack=" << nack.getReason() <<
" nack-to=all out-nack=" << outNack.getReason());
this->sendNacks(pitEntry, outNack);
this->processNack(inFace, nack, pitEntry);
}
} // namespace fw
+7 -3
View File
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2014-2016, Regents of the University of California,
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -27,6 +27,7 @@
#define NFD_DAEMON_FW_BEST_ROUTE_STRATEGY2_HPP
#include "strategy.hpp"
#include "process-nack-traits.hpp"
#include "retx-suppression-exponential.hpp"
namespace nfd {
@@ -50,6 +51,7 @@ namespace fw {
* The reason of the sent Nack equals the least severe reason among received Nacks.
*/
class BestRouteStrategy2 : public Strategy
, public ProcessNackTraits<BestRouteStrategy2>
{
public:
explicit
@@ -58,11 +60,11 @@ public:
static const Name&
getStrategyName();
virtual void
void
afterReceiveInterest(const Face& inFace, const Interest& interest,
const shared_ptr<pit::Entry>& pitEntry) override;
virtual void
void
afterReceiveNack(const Face& inFace, const lp::Nack& nack,
const shared_ptr<pit::Entry>& pitEntry) override;
@@ -70,6 +72,8 @@ PUBLIC_WITH_TESTS_ELSE_PRIVATE:
static const time::milliseconds RETX_SUPPRESSION_INITIAL;
static const time::milliseconds RETX_SUPPRESSION_MAX;
RetxSuppressionExponential m_retxSuppression;
friend ProcessNackTraits<BestRouteStrategy2>;
};
} // namespace fw
+10 -12
View File
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2014-2016, Regents of the University of California,
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -24,7 +24,6 @@
*/
#include "face-table.hpp"
#include "forwarder.hpp"
#include "core/asserts.hpp"
#include "core/global-io.hpp"
#include "core/logger.hpp"
@@ -45,10 +44,7 @@ Face*
FaceTable::get(FaceId id) const
{
auto i = m_faces.find(id);
if (i == m_faces.end()) {
return nullptr;
}
return i->second.get();
return i == m_faces.end() ? nullptr : i->second.get();
}
size_t
@@ -67,25 +63,27 @@ FaceTable::add(shared_ptr<Face> face)
FaceId faceId = ++m_lastFaceId;
BOOST_ASSERT(faceId > face::FACEID_RESERVED_MAX);
this->addImpl(face, faceId);
this->addImpl(std::move(face), faceId);
}
void
FaceTable::addReserved(shared_ptr<Face> face, FaceId faceId)
{
BOOST_ASSERT(face->getId() == face::INVALID_FACEID);
BOOST_ASSERT(m_faces.count(face->getId()) == 0);
BOOST_ASSERT(faceId <= face::FACEID_RESERVED_MAX);
this->addImpl(face, faceId);
this->addImpl(std::move(face), faceId);
}
void
FaceTable::addImpl(shared_ptr<Face> face, FaceId faceId)
{
face->setId(faceId);
m_faces[faceId] = face;
NFD_LOG_INFO("Added face id=" << faceId << " remote=" << face->getRemoteUri()
<< " local=" << face->getLocalUri());
auto ret = m_faces.emplace(faceId, face);
BOOST_VERIFY(ret.second);
NFD_LOG_INFO("Added face id=" << faceId <<
" remote=" << face->getRemoteUri() <<
" local=" << face->getLocalUri());
connectFaceClosedSignal(*face, bind(&FaceTable::remove, this, faceId));
+4 -5
View File
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2014-2016, Regents of the University of California,
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -65,13 +65,12 @@ public:
size() const;
public: // enumeration
typedef std::map<FaceId, shared_ptr<Face>> FaceMap;
typedef boost::indirected_range<const boost::select_second_const_range<FaceMap>> ForwardRange;
using FaceMap = std::map<FaceId, shared_ptr<Face>>;
using ForwardRange = boost::indirected_range<const boost::select_second_const_range<FaceMap>>;
/** \brief ForwardIterator for Face&
*/
typedef boost::range_iterator<ForwardRange>::type const_iterator;
using const_iterator = boost::range_iterator<ForwardRange>::type;
const_iterator
begin() const;
+11 -8
View File
@@ -1,12 +1,12 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2014, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
* Washington University in St. Louis,
* Beijing Institute of Technology,
* The University of Memphis
/*
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
* Washington University in St. Louis,
* Beijing Institute of Technology,
* The University of Memphis.
*
* This file is part of NFD (Named Data Networking Forwarding Daemon).
* See AUTHORS.md for complete list of NFD authors and contributors.
@@ -41,6 +41,9 @@ public:
PacketCounter nOutData;
PacketCounter nInNacks;
PacketCounter nOutNacks;
PacketCounter nCsHits;
PacketCounter nCsMisses;
};
} // namespace nfd
+34 -69
View File
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2014-2016, Regents of the University of California,
/*
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -72,53 +72,6 @@ Forwarder::Forwarder()
Forwarder::~Forwarder() = default;
void
Forwarder::startProcessInterest(Face& face, const Interest& interest)
{
// check fields used by forwarding are well-formed
try {
if (interest.hasLink()) {
interest.getLink();
}
}
catch (const tlv::Error&) {
NFD_LOG_DEBUG("startProcessInterest face=" << face.getId() <<
" interest=" << interest.getName() << " malformed");
// It's safe to call interest.getName() because Name has been fully parsed
return;
}
this->onIncomingInterest(face, interest);
}
void
Forwarder::startProcessData(Face& face, const Data& data)
{
// check fields used by forwarding are well-formed
// (none needed)
this->onIncomingData(face, data);
}
void
Forwarder::startProcessNack(Face& face, const lp::Nack& nack)
{
// check fields used by forwarding are well-formed
try {
if (nack.getInterest().hasLink()) {
nack.getInterest().getLink();
}
}
catch (const tlv::Error&) {
NFD_LOG_DEBUG("startProcessNack face=" << face.getId() <<
" nack=" << nack.getInterest().getName() <<
"~" << nack.getReason() << " malformed");
return;
}
this->onIncomingNack(face, nack);
}
void
Forwarder::onIncomingInterest(Face& inFace, const Interest& interest)
{
@@ -146,12 +99,24 @@ Forwarder::onIncomingInterest(Face& inFace, const Interest& interest)
return;
}
// strip forwarding hint if Interest has reached producer region
if (!interest.getForwardingHint().empty() &&
m_networkRegionTable.isInProducerRegion(interest.getForwardingHint())) {
NFD_LOG_DEBUG("onIncomingInterest face=" << inFace.getId() <<
" interest=" << interest.getName() << " reaching-producer-region");
const_cast<Interest&>(interest).setForwardingHint({});
}
// PIT insert
shared_ptr<pit::Entry> pitEntry = m_pit.insert(interest).first;
// detect duplicate Nonce in PIT entry
bool hasDuplicateNonceInPit = fw::findDuplicateNonce(*pitEntry, interest.getNonce(), inFace) !=
fw::DUPLICATE_NONCE_NONE;
int dnw = fw::findDuplicateNonce(*pitEntry, interest.getNonce(), inFace);
bool hasDuplicateNonceInPit = dnw != fw::DUPLICATE_NONCE_NONE;
if (inFace.getLinkType() == ndn::nfd::LINK_TYPE_POINT_TO_POINT) {
// for p2p face: duplicate Nonce from same incoming face is not loop
hasDuplicateNonceInPit = hasDuplicateNonceInPit && !(dnw & fw::DUPLICATE_NONCE_IN_SAME);
}
if (hasDuplicateNonceInPit) {
// goto Interest loop pipeline
this->onInterestLoop(inFace, interest);
@@ -175,8 +140,8 @@ Forwarder::onIncomingInterest(Face& inFace, const Interest& interest)
void
Forwarder::onInterestLoop(Face& inFace, const Interest& interest)
{
// if multi-access face, drop
if (inFace.getLinkType() == ndn::nfd::LINK_TYPE_MULTI_ACCESS) {
// if multi-access or ad hoc face, drop
if (inFace.getLinkType() != ndn::nfd::LINK_TYPE_POINT_TO_POINT) {
NFD_LOG_DEBUG("onInterestLoop face=" << inFace.getId() <<
" interest=" << interest.getName() <<
" drop");
@@ -199,6 +164,7 @@ Forwarder::onContentStoreMiss(const Face& inFace, const shared_ptr<pit::Entry>&
const Interest& interest)
{
NFD_LOG_DEBUG("onContentStoreMiss interest=" << interest.getName());
++m_counters.nCsMisses;
// insert in-record
pitEntry->insertOrUpdateInRecord(const_cast<Face&>(inFace), interest);
@@ -230,6 +196,7 @@ Forwarder::onContentStoreHit(const Face& inFace, const shared_ptr<pit::Entry>& p
const Interest& interest, const Data& data)
{
NFD_LOG_DEBUG("onContentStoreHit interest=" << interest.getName());
++m_counters.nCsHits;
data.setTag(make_shared<lp::IncomingFaceIdTag>(face::FACEID_CONTENT_STORE));
// XXX should we lookup PIT for other Interests that also match csMatch?
@@ -287,7 +254,7 @@ Forwarder::onInterestUnsatisfied(const shared_ptr<pit::Entry>& pitEntry)
void
Forwarder::onInterestFinalize(const shared_ptr<pit::Entry>& pitEntry, bool isSatisfied,
time::milliseconds dataFreshnessPeriod)
ndn::optional<time::milliseconds> dataFreshnessPeriod)
{
NFD_LOG_DEBUG("onInterestFinalize interest=" << pitEntry->getName() <<
(isSatisfied ? " satisfied" : " unsatisfied"));
@@ -362,7 +329,8 @@ Forwarder::onIncomingData(Face& inFace, const Data& data)
// foreach pending downstream
for (Face* pendingDownstream : pendingDownstreams) {
if (pendingDownstream == &inFace) {
if (pendingDownstream->getId() == inFace.getId() &&
pendingDownstream->getLinkType() != ndn::nfd::LINK_TYPE_AD_HOC) {
continue;
}
// goto outgoing Data pipeline
@@ -418,8 +386,8 @@ Forwarder::onIncomingNack(Face& inFace, const lp::Nack& nack)
nack.setTag(make_shared<lp::IncomingFaceIdTag>(inFace.getId()));
++m_counters.nInNacks;
// if multi-access face, drop
if (inFace.getLinkType() == ndn::nfd::LINK_TYPE_MULTI_ACCESS) {
// if multi-access or ad hoc face, drop
if (inFace.getLinkType() != ndn::nfd::LINK_TYPE_POINT_TO_POINT) {
NFD_LOG_DEBUG("onIncomingNack face=" << inFace.getId() <<
" nack=" << nack.getInterest().getName() <<
"~" << nack.getReason() << " face-is-multi-access");
@@ -489,8 +457,8 @@ Forwarder::onOutgoingNack(const shared_ptr<pit::Entry>& pitEntry, const Face& ou
return;
}
// if multi-access face, drop
if (outFace.getLinkType() == ndn::nfd::LINK_TYPE_MULTI_ACCESS) {
// if multi-access or ad hoc face, drop
if (outFace.getLinkType() != ndn::nfd::LINK_TYPE_POINT_TO_POINT) {
NFD_LOG_DEBUG("onOutgoingNack face=" << outFace.getId() <<
" nack=" << pitEntry->getInterest().getName() <<
"~" << nack.getReason() << " face-is-multi-access");
@@ -538,7 +506,7 @@ Forwarder::setUnsatisfyTimer(const shared_ptr<pit::Entry>& pitEntry)
void
Forwarder::setStragglerTimer(const shared_ptr<pit::Entry>& pitEntry, bool isSatisfied,
time::milliseconds dataFreshnessPeriod)
ndn::optional<time::milliseconds> dataFreshnessPeriod)
{
time::nanoseconds stragglerTime = time::milliseconds(100);
@@ -563,18 +531,15 @@ insertNonceToDnl(DeadNonceList& dnl, const pit::Entry& pitEntry,
void
Forwarder::insertDeadNonceList(pit::Entry& pitEntry, bool isSatisfied,
time::milliseconds dataFreshnessPeriod, Face* upstream)
ndn::optional<time::milliseconds> dataFreshnessPeriod, Face* upstream)
{
// need Dead Nonce List insert?
bool needDnl = false;
bool needDnl = true;
if (isSatisfied) {
bool hasFreshnessPeriod = dataFreshnessPeriod >= time::milliseconds::zero();
// Data never becomes stale if it doesn't have FreshnessPeriod field
BOOST_ASSERT(dataFreshnessPeriod);
BOOST_ASSERT(*dataFreshnessPeriod >= time::milliseconds::zero());
needDnl = static_cast<bool>(pitEntry.getInterest().getMustBeFresh()) &&
(hasFreshnessPeriod && dataFreshnessPeriod < m_deadNonceList.getLifetime());
}
else {
needDnl = true;
*dataFreshnessPeriod < m_deadNonceList.getLifetime();
}
if (!needDnl) {
@@ -582,7 +547,7 @@ Forwarder::insertDeadNonceList(pit::Entry& pitEntry, bool isSatisfied,
}
// Dead Nonce List insert
if (upstream == 0) {
if (upstream == nullptr) {
// insert all outgoing Nonces
const pit::OutRecordCollection& outRecords = pitEntry.getOutRecords();
std::for_each(outRecords.begin(), outRecords.end(),
+20 -11
View File
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2014-2016, Regents of the University of California,
/*
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -106,24 +106,33 @@ public: // faces and policies
public: // forwarding entrypoints and tables
/** \brief start incoming Interest processing
* \param face face on which Interest is received
* \param interest the incoming Interest, must be created with make_shared
* \param interest the incoming Interest, must be well-formed and created with make_shared
*/
void
startProcessInterest(Face& face, const Interest& interest);
startProcessInterest(Face& face, const Interest& interest)
{
this->onIncomingInterest(face, interest);
}
/** \brief start incoming Data processing
* \param face face on which Data is received
* \param data the incoming Data, must be created with make_shared
* \param data the incoming Data, must be well-formed and created with make_shared
*/
void
startProcessData(Face& face, const Data& data);
startProcessData(Face& face, const Data& data)
{
this->onIncomingData(face, data);
}
/** \brief start incoming Nack processing
* \param face face on which Nack is received
* \param nack the incoming Nack, must be created with make_shared
* \param nack the incoming Nack, must be well-formed
*/
void
startProcessNack(Face& face, const lp::Nack& nack);
startProcessNack(Face& face, const lp::Nack& nack)
{
this->onIncomingNack(face, nack);
}
NameTree&
getNameTree()
@@ -216,7 +225,7 @@ PUBLIC_WITH_TESTS_ELSE_PRIVATE: // pipelines
*/
VIRTUAL_WITH_TESTS void
onInterestFinalize(const shared_ptr<pit::Entry>& pitEntry, bool isSatisfied,
time::milliseconds dataFreshnessPeriod = time::milliseconds(-1));
ndn::optional<time::milliseconds> dataFreshnessPeriod = ndn::nullopt);
/** \brief incoming Data pipeline
*/
@@ -249,7 +258,7 @@ PROTECTED_WITH_TESTS_ELSE_PRIVATE:
VIRTUAL_WITH_TESTS void
setStragglerTimer(const shared_ptr<pit::Entry>& pitEntry, bool isSatisfied,
time::milliseconds dataFreshnessPeriod = time::milliseconds(-1));
ndn::optional<time::milliseconds> dataFreshnessPeriod = ndn::nullopt);
VIRTUAL_WITH_TESTS void
cancelUnsatisfyAndStragglerTimer(pit::Entry& pitEntry);
@@ -260,7 +269,7 @@ PROTECTED_WITH_TESTS_ELSE_PRIVATE:
*/
VIRTUAL_WITH_TESTS void
insertDeadNonceList(pit::Entry& pitEntry, bool isSatisfied,
time::milliseconds dataFreshnessPeriod, Face* upstream);
ndn::optional<time::milliseconds> dataFreshnessPeriod, Face* upstream);
/** \brief call trigger (method) on the effective strategy of pitEntry
*/
+55 -9
View File
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2014-2016, Regents of the University of California,
/*
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -25,14 +25,24 @@
#include "multicast-strategy.hpp"
#include "algorithm.hpp"
#include "core/logger.hpp"
namespace nfd {
namespace fw {
NFD_REGISTER_STRATEGY(MulticastStrategy);
NFD_LOG_INIT("MulticastStrategy");
const time::milliseconds MulticastStrategy::RETX_SUPPRESSION_INITIAL(10);
const time::milliseconds MulticastStrategy::RETX_SUPPRESSION_MAX(250);
MulticastStrategy::MulticastStrategy(Forwarder& forwarder, const Name& name)
: Strategy(forwarder)
, ProcessNackTraits(this)
, m_retxSuppression(RETX_SUPPRESSION_INITIAL,
RetxSuppressionExponential::DEFAULT_MULTIPLIER,
RETX_SUPPRESSION_MAX)
{
ParsedInstanceName parsed = parseInstanceName(name);
if (!parsed.parameters.empty()) {
@@ -48,7 +58,7 @@ MulticastStrategy::MulticastStrategy(Forwarder& forwarder, const Name& name)
const Name&
MulticastStrategy::getStrategyName()
{
static Name strategyName("/localhost/nfd/strategy/multicast/%FD%01");
static Name strategyName("/localhost/nfd/strategy/multicast/%FD%03");
return strategyName;
}
@@ -59,18 +69,54 @@ MulticastStrategy::afterReceiveInterest(const Face& inFace, const Interest& inte
const fib::Entry& fibEntry = this->lookupFib(*pitEntry);
const fib::NextHopList& nexthops = fibEntry.getNextHops();
for (fib::NextHopList::const_iterator it = nexthops.begin(); it != nexthops.end(); ++it) {
Face& outFace = it->getFace();
if (!wouldViolateScope(inFace, interest, outFace) &&
canForwardToLegacy(*pitEntry, outFace)) {
this->sendInterest(pitEntry, outFace, interest);
int nEligibleNextHops = 0;
bool isSuppressed = false;
for (const auto& nexthop : nexthops) {
Face& outFace = nexthop.getFace();
RetxSuppressionResult suppressResult = m_retxSuppression.decidePerUpstream(*pitEntry, outFace);
if (suppressResult == RetxSuppressionResult::SUPPRESS) {
NFD_LOG_DEBUG(interest << " from=" << inFace.getId()
<< "to=" << outFace.getId() << " suppressed");
isSuppressed = true;
continue;
}
if ((outFace.getId() == inFace.getId() && outFace.getLinkType() != ndn::nfd::LINK_TYPE_AD_HOC) ||
wouldViolateScope(inFace, interest, outFace)) {
continue;
}
this->sendInterest(pitEntry, outFace, interest);
NFD_LOG_DEBUG(interest << " from=" << inFace.getId()
<< " pitEntry-to=" << outFace.getId());
if (suppressResult == RetxSuppressionResult::FORWARD) {
m_retxSuppression.incrementIntervalForOutRecord(*pitEntry->getOutRecord(outFace));
}
++nEligibleNextHops;
}
if (!hasPendingOutRecords(*pitEntry)) {
if (nEligibleNextHops == 0 && !isSuppressed) {
NFD_LOG_DEBUG(interest << " from=" << inFace.getId() << " noNextHop");
lp::NackHeader nackHeader;
nackHeader.setReason(lp::NackReason::NO_ROUTE);
this->sendNack(pitEntry, inFace, nackHeader);
this->rejectPendingInterest(pitEntry);
}
}
void
MulticastStrategy::afterReceiveNack(const Face& inFace, const lp::Nack& nack,
const shared_ptr<pit::Entry>& pitEntry)
{
this->processNack(inFace, nack, pitEntry);
}
} // namespace fw
} // namespace nfd
+18 -2
View File
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2014-2016, Regents of the University of California,
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -27,6 +27,8 @@
#define NFD_DAEMON_FW_MULTICAST_STRATEGY_HPP
#include "strategy.hpp"
#include "process-nack-traits.hpp"
#include "retx-suppression-exponential.hpp"
namespace nfd {
namespace fw {
@@ -34,16 +36,30 @@ namespace fw {
/** \brief a forwarding strategy that forwards Interest to all FIB nexthops
*/
class MulticastStrategy : public Strategy
, public ProcessNackTraits<MulticastStrategy>
{
public:
explicit
MulticastStrategy(Forwarder& forwarder, const Name& name = getStrategyName());
static const Name&
getStrategyName();
virtual void
void
afterReceiveInterest(const Face& inFace, const Interest& interest,
const shared_ptr<pit::Entry>& pitEntry) override;
void
afterReceiveNack(const Face& inFace, const lp::Nack& nack,
const shared_ptr<pit::Entry>& pitEntry) override;
private:
friend ProcessNackTraits<MulticastStrategy>;
RetxSuppressionExponential m_retxSuppression;
PUBLIC_WITH_TESTS_ELSE_PRIVATE:
static const time::milliseconds RETX_SUPPRESSION_INITIAL;
static const time::milliseconds RETX_SUPPRESSION_MAX;
};
} // namespace fw
+86
View File
@@ -0,0 +1,86 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
* Washington University in St. Louis,
* Beijing Institute of Technology,
* The University of Memphis.
*
* This file is part of NFD (Named Data Networking Forwarding Daemon).
* See AUTHORS.md for complete list of NFD authors and contributors.
*
* NFD is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Foundation,
* either version 3 of the License, or (at your option) any later version.
*
* NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
*/
#include "process-nack-traits.hpp"
#include "core/logger.hpp"
namespace nfd {
namespace fw {
NFD_LOG_INIT("ProcessNackTraits");
void
ProcessNackTraitsBase::processNack(const Face& inFace, const lp::Nack& nack,
const shared_ptr<pit::Entry>& pitEntry)
{
int nOutRecordsNotNacked = 0;
Face* lastFaceNotNacked = nullptr;
lp::NackReason leastSevereReason = lp::NackReason::NONE;
for (const pit::OutRecord& outR : pitEntry->getOutRecords()) {
const lp::NackHeader* inNack = outR.getIncomingNack();
if (inNack == nullptr) {
++nOutRecordsNotNacked;
lastFaceNotNacked = &outR.getFace();
continue;
}
if (isLessSevere(inNack->getReason(), leastSevereReason)) {
leastSevereReason = inNack->getReason();
}
}
lp::NackHeader outNack;
outNack.setReason(leastSevereReason);
if (nOutRecordsNotNacked == 1) {
BOOST_ASSERT(lastFaceNotNacked != nullptr);
pit::InRecordCollection::iterator inR = pitEntry->getInRecord(*lastFaceNotNacked);
if (inR != pitEntry->in_end()) {
// one out-record not Nacked, which is also a downstream
NFD_LOG_DEBUG(nack.getInterest() << " nack-from=" << inFace.getId() <<
" nack=" << nack.getReason() <<
" nack-to(bidirectional)=" << lastFaceNotNacked->getId() <<
" out-nack=" << outNack.getReason());
this->sendNackForProcessNackTraits(pitEntry, *lastFaceNotNacked, outNack);
return;
}
}
if (nOutRecordsNotNacked > 0) {
NFD_LOG_DEBUG(nack.getInterest() << " nack-from=" << inFace.getId() <<
" nack=" << nack.getReason() <<
" waiting=" << nOutRecordsNotNacked);
// continue waiting
return;
}
NFD_LOG_DEBUG(nack.getInterest() << " nack-from=" << inFace.getId() <<
" nack=" << nack.getReason() <<
" nack-to=all out-nack=" << outNack.getReason());
this->sendNacksForProcessNackTraits(pitEntry, outNack);
}
} // namespace fw
} // namespace nfd
+101
View File
@@ -0,0 +1,101 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
* Washington University in St. Louis,
* Beijing Institute of Technology,
* The University of Memphis.
*
* This file is part of NFD (Named Data Networking Forwarding Daemon).
* See AUTHORS.md for complete list of NFD authors and contributors.
*
* NFD is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Foundation,
* either version 3 of the License, or (at your option) any later version.
*
* NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef NFD_DAEMON_FW_PROCESS_NACK_TRAITS_HPP
#define NFD_DAEMON_FW_PROCESS_NACK_TRAITS_HPP
#include "strategy.hpp"
namespace nfd {
namespace fw {
/** \brief provides a common procedure for processing Nacks
*
* This procedure works as follows:
* 1. If Nacks have been received from all upstream faces, return a Nack with least severe reason
* to downstream faces.
* 2. If there are more than one upstream face, Nacks have been received from all but one of
* them, and that face is also a downstream, return a Nack to that face. This is to address a
* specific "live deadlock" scenario where two hosts are waiting for each other to return the
* Nack.
* 3. Otherwise, wait for the arrival of more Nacks or Data.
*
* To use this helper, the strategy should inherit from ProcessNackTraits<MyStrategy>,
* and declare that specialization as a friend class.
* Then, invoke processNack from afterReceiveNack trigger.
*/
class ProcessNackTraitsBase : noncopyable
{
protected:
virtual
~ProcessNackTraitsBase() = default;
void
processNack(const Face& inFace, const lp::Nack& nack,
const shared_ptr<pit::Entry>& pitEntry);
private:
virtual void
sendNackForProcessNackTraits(const shared_ptr<pit::Entry>& pitEntry, const Face& outFace,
const lp::NackHeader& header) = 0;
virtual void
sendNacksForProcessNackTraits(const shared_ptr<pit::Entry>& pitEntry,
const lp::NackHeader& header) = 0;
};
template<typename S>
class ProcessNackTraits : public ProcessNackTraitsBase
{
protected:
explicit
ProcessNackTraits(S* strategy)
: m_strategy(strategy)
{
}
private:
void
sendNackForProcessNackTraits(const shared_ptr<pit::Entry>& pitEntry, const Face& outFace,
const lp::NackHeader& header) override
{
m_strategy->sendNack(pitEntry, outFace, header);
}
void
sendNacksForProcessNackTraits(const shared_ptr<pit::Entry>& pitEntry,
const lp::NackHeader& header) override
{
m_strategy->sendNacks(pitEntry, header);
}
private:
S* m_strategy;
};
} // namespace fw
} // namespace nfd
#endif // NFD_DAEMON_FW_PROCESS_NACK_TRAITS_HPP
+42 -11
View File
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2014-2016, Regents of the University of California,
/*
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -24,7 +24,6 @@
*/
#include "retx-suppression-exponential.hpp"
#include "algorithm.hpp"
namespace nfd {
namespace fw {
@@ -69,16 +68,15 @@ RetxSuppressionExponential::RetxSuppressionExponential(const Duration& initialIn
BOOST_ASSERT(maxInterval >= initialInterval);
}
RetxSuppression::Result
RetxSuppressionExponential::decide(const Face& inFace, const Interest& interest,
pit::Entry& pitEntry) const
RetxSuppressionResult
RetxSuppressionExponential::decidePerPitEntry(pit::Entry& pitEntry)
{
bool isNewPitEntry = !hasPendingOutRecords(pitEntry);
if (isNewPitEntry) {
return NEW;
return RetxSuppressionResult::NEW;
}
time::steady_clock::TimePoint lastOutgoing = this->getLastOutgoing(pitEntry);
time::steady_clock::TimePoint lastOutgoing = getLastOutgoing(pitEntry);
time::steady_clock::TimePoint now = time::steady_clock::now();
time::steady_clock::Duration sinceLastOutgoing = now - lastOutgoing;
@@ -86,12 +84,45 @@ RetxSuppressionExponential::decide(const Face& inFace, const Interest& interest,
bool shouldSuppress = sinceLastOutgoing < pi->suppressionInterval;
if (shouldSuppress) {
return SUPPRESS;
return RetxSuppressionResult::SUPPRESS;
}
pi->suppressionInterval = std::min(m_maxInterval,
time::duration_cast<Duration>(pi->suppressionInterval * m_multiplier));
return FORWARD;
time::duration_cast<Duration>(pi->suppressionInterval * m_multiplier));
return RetxSuppressionResult::FORWARD;
}
RetxSuppressionResult
RetxSuppressionExponential::decidePerUpstream(pit::Entry& pitEntry, Face& outFace)
{
// NEW if outRecord for the face does not exist
auto outRecord = pitEntry.getOutRecord(outFace);
if (outRecord == pitEntry.out_end()) {
return RetxSuppressionResult::NEW;
}
time::steady_clock::TimePoint lastOutgoing = outRecord->getLastRenewed();
time::steady_clock::TimePoint now = time::steady_clock::now();
time::steady_clock::Duration sinceLastOutgoing = now - lastOutgoing;
// insertStrategyInfo does not insert m_initialInterval again if it already exists
PitInfo* pi = outRecord->insertStrategyInfo<PitInfo>(m_initialInterval).first;
bool shouldSuppress = sinceLastOutgoing < pi->suppressionInterval;
if (shouldSuppress) {
return RetxSuppressionResult::SUPPRESS;
}
return RetxSuppressionResult::FORWARD;
}
void
RetxSuppressionExponential::incrementIntervalForOutRecord(pit::OutRecord& outRecord)
{
PitInfo* pi = outRecord.insertStrategyInfo<PitInfo>(m_initialInterval).first;
pi->suppressionInterval = std::min(m_maxInterval,
time::duration_cast<Duration>(pi->suppressionInterval * m_multiplier));
}
} // namespace fw
+18 -7
View File
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2014-2016, Regents of the University of California,
/*
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -26,6 +26,7 @@
#ifndef NFD_DAEMON_FW_RETX_SUPPRESSION_EXPONENTIAL_HPP
#define NFD_DAEMON_FW_RETX_SUPPRESSION_EXPONENTIAL_HPP
#include "algorithm.hpp"
#include "retx-suppression.hpp"
namespace nfd {
@@ -37,7 +38,7 @@ namespace fw {
* The i-th retransmission will be suppressed if the last transmission (out-record)
* occurred within MIN(initialInterval * multiplier^(i-1), maxInterval)
*/
class RetxSuppressionExponential : public RetxSuppression
class RetxSuppressionExponential
{
public:
/** \brief time granularity
@@ -49,12 +50,22 @@ public:
float multiplier = DEFAULT_MULTIPLIER,
const Duration& maxInterval = DEFAULT_MAX_INTERVAL);
/** \brief determines whether Interest is a retransmission,
/** \brief determines whether Interest is a retransmission per pit entry
* and if so, whether it shall be forwarded or suppressed
*/
virtual Result
decide(const Face& inFace, const Interest& interest,
pit::Entry& pitEntry) const override;
RetxSuppressionResult
decidePerPitEntry(pit::Entry& pitEntry);
/** \brief determines whether Interest is a retransmission per upstream
* and if so, whether it shall be forwarded or suppressed
*/
RetxSuppressionResult
decidePerUpstream(pit::Entry& pitEntry, Face& outFace);
/** \brief Increment the suppression interval for out record
*/
void
incrementIntervalForOutRecord(pit::OutRecord& outRecord);
public:
/** \brief StrategyInfo on pit::Entry
+7 -9
View File
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2014-2016, Regents of the University of California,
/*
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -24,7 +24,6 @@
*/
#include "retx-suppression-fixed.hpp"
#include "algorithm.hpp"
namespace nfd {
namespace fw {
@@ -37,20 +36,19 @@ RetxSuppressionFixed::RetxSuppressionFixed(const time::milliseconds& minRetxInte
BOOST_ASSERT(minRetxInterval > time::milliseconds::zero());
}
RetxSuppression::Result
RetxSuppressionFixed::decide(const Face& inFace, const Interest& interest,
pit::Entry& pitEntry) const
RetxSuppressionResult
RetxSuppressionFixed::decidePerPitEntry(pit::Entry& pitEntry) const
{
bool isNewPitEntry = !hasPendingOutRecords(pitEntry);
if (isNewPitEntry) {
return NEW;
return RetxSuppressionResult::NEW;
}
time::steady_clock::TimePoint lastOutgoing = this->getLastOutgoing(pitEntry);
time::steady_clock::TimePoint lastOutgoing = getLastOutgoing(pitEntry);
time::steady_clock::TimePoint now = time::steady_clock::now();
time::steady_clock::Duration sinceLastOutgoing = now - lastOutgoing;
bool shouldSuppress = sinceLastOutgoing < m_minRetxInterval;
return shouldSuppress ? SUPPRESS : FORWARD;
return shouldSuppress ? RetxSuppressionResult::SUPPRESS : RetxSuppressionResult::FORWARD;
}
} // namespace fw
Loaded 100 of 335 files, more files were not shown because too many files have changed in this diff. Show more