Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
84dd4ca348 | ||
|
|
b8d1fc7562 | ||
|
|
6d1c1c320b | ||
|
|
4b89a6e943 | ||
|
|
afb2059377 | ||
|
|
4e837f8d12 | ||
|
|
b5eee2066b | ||
|
|
0804f5dd1f | ||
|
|
847de408cb | ||
|
|
b5220706c6 | ||
|
|
38fb251c44 | ||
|
|
60b7d51063 | ||
|
|
085b9e3a6f | ||
|
|
cfe81d5098 | ||
|
|
042a331582 | ||
|
|
ac238f29be | ||
|
|
f940ae70f2 | ||
|
|
f54d0301c6 | ||
|
|
88043fcbe8 | ||
|
|
06a1eab063 | ||
|
|
a907980840 | ||
|
|
c25737cd9a | ||
|
|
84d3adcd6c | ||
|
|
a997d29622 | ||
|
|
c8f17e51ed | ||
|
|
c0a5a39a4f | ||
|
|
01bbd09ed8 | ||
|
|
15902efdef | ||
|
|
00a3c9dc86 | ||
|
|
8ca138de13 | ||
|
|
a5765d647b | ||
|
|
2642cd250b | ||
|
|
b15276f90e | ||
|
|
6126547e53 | ||
|
|
b8eebcb4aa | ||
|
|
79a92089e7 | ||
|
|
27b3854eb7 | ||
|
|
bbace1de47 | ||
|
|
4e1b6eee32 | ||
|
|
849e12a068 | ||
|
|
90015991bc | ||
|
|
bb6146ef74 | ||
|
|
6028619f71 | ||
|
|
fc2e13d58d | ||
|
|
944f38bb49 | ||
|
|
b347b7f189 | ||
|
|
6837d4f732 | ||
|
|
0ba6d64e5a | ||
|
|
2d49175d6e | ||
|
|
84d62cbbc6 | ||
|
|
7b0071e6a9 | ||
|
|
a5a3a87ae0 | ||
|
|
cb76686ecc | ||
|
|
0d82d0459c | ||
|
|
2760adcb80 | ||
|
|
f748a4e9e2 | ||
|
|
84c65c0546 | ||
|
|
9ac95d3c1c | ||
|
|
68ab43d8b7 | ||
|
|
0b0a71d801 | ||
|
|
1816d4b190 | ||
|
|
3b0815486e | ||
|
|
dbb6b3e119 | ||
|
|
8a1f170ea6 | ||
|
|
83be1dae4b | ||
|
|
08e96316be | ||
|
|
8eb5f8f3ed | ||
|
|
46afec48ec | ||
|
|
43ff2a9254 | ||
|
|
7744a4a298 | ||
|
|
f32ca897c5 | ||
|
|
221f53bb6f | ||
|
|
9f3973e7b7 | ||
|
|
7d8695d9e7 | ||
|
|
3f21e58a58 | ||
|
|
6eb0271fae | ||
|
|
33a24e8e8f | ||
|
|
16a3adfafc | ||
|
|
f4056d0242 | ||
|
|
9ae8857bce | ||
|
|
a94f3676c3 | ||
|
|
fe0580cb55 | ||
|
|
6f6ef012cb | ||
|
|
5c1bb36ae9 | ||
|
|
8d61c037a7 | ||
|
|
1cce2a3014 | ||
|
|
73bcad33b9 | ||
|
|
22db53973d | ||
|
|
38c7d9e6d7 | ||
|
|
8eda682a18 | ||
|
|
a3c9ddb21c | ||
|
|
acca18ae12 | ||
|
|
77911ccc73 | ||
|
|
c19408ddf2 | ||
|
|
e5eebad6d2 | ||
|
|
8fd15e628a | ||
|
|
b283f52c78 | ||
|
|
f03d4796d7 | ||
|
|
6fc57a69fa | ||
|
|
5d3e481956 | ||
|
|
f995f382ec | ||
|
|
cad76b6242 | ||
|
|
0d97692b40 | ||
|
|
78e32b0ee2 | ||
|
|
6bd6d0b50a | ||
|
|
bfea575095 | ||
|
|
fe4fce3a3f | ||
|
|
185ab29325 | ||
|
|
21f22e692f | ||
|
|
da89fa5a9f | ||
|
|
d2ea2350ad | ||
|
|
4f5cdcf421 | ||
|
|
d396b61ba1 | ||
|
|
1586aff97b | ||
|
|
9fcf123b19 | ||
|
|
635bf20ef5 | ||
|
|
f6af4f8913 | ||
|
|
1d62e62bc0 | ||
|
|
89c0ea0621 | ||
|
|
2fb3e8e3fe | ||
|
|
63b67e2b23 | ||
|
|
1a25a6edd0 | ||
|
|
2fe3af0577 | ||
|
|
ecdbe5f2a6 | ||
|
|
f7bf4093be | ||
|
|
084b79553f | ||
|
|
e38e261ae6 | ||
|
|
918e5d451e | ||
|
|
7a294d49ab | ||
|
|
d695801754 | ||
|
|
26cbdbd904 | ||
|
|
6fd1e7e5ef | ||
|
|
36e5429622 | ||
|
|
58ba3f9429 | ||
|
|
970f648843 | ||
|
|
8f803f292e | ||
|
|
05dd4441b0 | ||
|
|
1fbdb54f85 | ||
|
|
f9858fdd1a | ||
|
|
fab9e0de46 | ||
|
|
1d7fef5810 | ||
|
|
f0dfb33292 | ||
|
|
04262d9c84 | ||
|
|
056815ea8b | ||
|
|
215fd0b6d9 | ||
|
|
9954007722 | ||
|
|
ea47bde02e | ||
|
|
c344304026 | ||
|
|
1f481fa1a3 | ||
|
|
88a062de78 |
No files matched your search
+18
-5
@@ -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
|
||||
@@ -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
@@ -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
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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.
|
||||

|
||||
|
||||
In that case, add it under contact information panel.
|
||||
.
|
||||
|
||||
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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
@@ -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
@@ -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()) {
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
@@ -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
@@ -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
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
@@ -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,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;
|
||||
|
||||
@@ -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>()) {
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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://"),
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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");
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
@@ -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
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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));
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
Reference in new issue
Block a user