Compare commits
409
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3f94ec31f3 | ||
|
|
e0bae0f64b | ||
|
|
fdcbb4d0ef | ||
|
|
acd00876f9 | ||
|
|
d7b44c817d | ||
|
|
4d82692800 | ||
|
|
56a741f29a | ||
|
|
17d1849dec | ||
|
|
003357899d | ||
|
|
de33245d9f | ||
|
|
26667e16df | ||
|
|
d5e89e8c7d | ||
|
|
c9b5e018ed | ||
|
|
02a0e0fbe3 | ||
|
|
92e4ea53ba | ||
|
|
494a955b56 | ||
|
|
332783b3e5 | ||
|
|
d96744d968 | ||
|
|
85244373f9 | ||
|
|
75306351ff | ||
|
|
3d2049fbc1 | ||
|
|
0c3e57b399 | ||
|
|
15b5505e01 | ||
|
|
97a010178c | ||
|
|
5cc706c8cb | ||
|
|
0e13e1e2e2 | ||
|
|
0c84164fd0 | ||
|
|
a6f2d245d7 | ||
|
|
8ce09032ff | ||
|
|
b49313db62 | ||
|
|
167040a677 | ||
|
|
3160a3f820 | ||
|
|
3257af2823 | ||
|
|
f2bfb44a4a | ||
|
|
8215a3ada6 | ||
|
|
4738f15594 | ||
|
|
00d39fda3c | ||
|
|
78ef901f24 | ||
|
|
3f9234b2cf | ||
|
|
0c63c63ed7 | ||
|
|
69b63dcdd4 | ||
|
|
41aba10d3d | ||
|
|
4253885f23 | ||
|
|
59769b1864 | ||
|
|
52ae3296bd | ||
|
|
bb734df9f6 | ||
|
|
bc79bd63c7 | ||
|
|
6daf2a39f5 | ||
|
|
22fba35327 | ||
|
|
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 | ||
|
|
19e2e6d858 | ||
|
|
c31080d470 | ||
|
|
b47247dad9 | ||
|
|
8a84773fdb | ||
|
|
55e21b9e17 | ||
|
|
7d30d8556a | ||
|
|
64d99f21eb | ||
|
|
1b65ca1900 | ||
|
|
3409cd399d | ||
|
|
3206565bac | ||
|
|
32dab97321 | ||
|
|
7003c609b8 | ||
|
|
16320cd28b | ||
|
|
406deb5971 | ||
|
|
38b24c7b94 | ||
|
|
530cf00d68 | ||
|
|
465056e9c6 | ||
|
|
91f6ee0c72 | ||
|
|
b859031855 | ||
|
|
81a9a86305 | ||
|
|
7f566dd0e5 | ||
|
|
65800d22e7 | ||
|
|
4cb74319f9 | ||
|
|
3aba7548f4 | ||
|
|
593887c8d0 | ||
|
|
0e4a1f112d | ||
|
|
18739c4ac1 | ||
|
|
947192570d | ||
|
|
2a00194316 | ||
|
|
9137e9e2ae | ||
|
|
890afe9d85 | ||
|
|
d0e276f66a | ||
|
|
5e8755c774 | ||
|
|
037f4ab4b6 | ||
|
|
f15058ab06 | ||
|
|
c34d167b91 | ||
|
|
b4a5acd890 | ||
|
|
a88911105c | ||
|
|
7ee00fb680 | ||
|
|
c29c0b01a7 | ||
|
|
9db735958c | ||
|
|
57cda13f7b | ||
|
|
2a73f35ce6 | ||
|
|
aaa58a6c10 | ||
|
|
52fa45c6ee | ||
|
|
934f5055ab | ||
|
|
26c82ca131 | ||
|
|
52555b06d4 | ||
|
|
e21b3f3dc4 | ||
|
|
ee400b5235 | ||
|
|
e273333fba | ||
|
|
6d932e83d2 | ||
|
|
ae889380da | ||
|
|
a7f9a29e74 | ||
|
|
a6350d9395 | ||
|
|
6c13562d23 | ||
|
|
00dc91423e | ||
|
|
ac767aad0f | ||
|
|
89567d3b31 | ||
|
|
dcdf6212cc | ||
|
|
c5f651f014 | ||
|
|
a274292b93 | ||
|
|
d3ac777f7b | ||
|
|
ecc45cbc7e | ||
|
|
1b4ba050cc | ||
|
|
5f47aa6140 | ||
|
|
89c4cca866 | ||
|
|
27bdd1a325 | ||
|
|
6db5ce1912 | ||
|
|
91e1d7ce48 | ||
|
|
97210d5b15 | ||
|
|
436f3bd9fe | ||
|
|
7de0444d06 | ||
|
|
7c7951391c | ||
|
|
6e21cdb13f | ||
|
|
67019110d7 | ||
|
|
9a00fabed7 | ||
|
|
c05918c860 | ||
|
|
f40551a061 | ||
|
|
d8205106cc | ||
|
|
cffb4e5bce | ||
|
|
3495b8009c | ||
|
|
c143afea10 | ||
|
|
c69f9210d9 | ||
|
|
043c50147c | ||
|
|
e94804bd48 | ||
|
|
e342e8de70 | ||
|
|
c06746182b | ||
|
|
fc6515d2f7 | ||
|
|
3d1eb091c0 | ||
|
|
bf6acd5cc9 | ||
|
|
737c43cdad | ||
|
|
0fc97486ff | ||
|
|
e0edc4c06d | ||
|
|
0a5e5e2fa5 | ||
|
|
8527f86de4 | ||
|
|
6459e64f36 | ||
|
|
25c6ce41d5 | ||
|
|
cbc8e947bf | ||
|
|
0dba01018c | ||
|
|
b5aa7f58e8 | ||
|
|
64567bb12a | ||
|
|
58320f7156 | ||
|
|
5ad19febad | ||
|
|
88f5f7392b | ||
|
|
4260241659 | ||
|
|
231ddd7508 | ||
|
|
cf0f3ce319 | ||
|
|
aaa5dd3a00 | ||
|
|
a269f47bc7 | ||
|
|
9685cc5682 | ||
|
|
2f741327e6 | ||
|
|
8c39bf0b9f | ||
|
|
e0fa33b2a0 | ||
|
|
fc02186460 | ||
|
|
2af9ca368b | ||
|
|
0cc125c365 | ||
|
|
36b22256c1 | ||
|
|
fbe8efea55 | ||
|
|
2e526d70d2 | ||
|
|
06d6ba666d | ||
|
|
37175489ca | ||
|
|
9ddf1b5c26 | ||
|
|
217384025e | ||
|
|
29b4128397 | ||
|
|
331ade75da | ||
|
|
d243d71bf5 | ||
|
|
5b3feb6008 | ||
|
|
d7631279f7 | ||
|
|
f5409d434a | ||
|
|
1c2c6c45e2 | ||
|
|
b30863e0e2 | ||
|
|
13ad4b7a42 | ||
|
|
9f6ca60f74 | ||
|
|
dbef6dc8e2 | ||
|
|
3248205bb7 | ||
|
|
09cf13cd9f | ||
|
|
8ff0a86e18 | ||
|
|
b9420cf0c1 | ||
|
|
340d5533ea | ||
|
|
e7258ffa88 | ||
|
|
dd8f66140b | ||
|
|
15e98b00f5 | ||
|
|
759993b922 | ||
|
|
7f35843d58 | ||
|
|
f2420fc7a3 | ||
|
|
811c010098 | ||
|
|
67f30273dd | ||
|
|
352fc82185 | ||
|
|
9ede5bd312 | ||
|
|
e3cf285ee7 | ||
|
|
f9f7cf35ae | ||
|
|
b660b4c9c0 | ||
|
|
029401f6cc | ||
|
|
9f5b01dfd8 | ||
|
|
38f4ce9f25 | ||
|
|
9cff779038 | ||
|
|
02b73f5705 | ||
|
|
2570f3ec4f | ||
|
|
dcffdaafb5 | ||
|
|
80f9fcdaa1 | ||
|
|
71d12148c8 | ||
|
|
d23de8b9ef | ||
|
|
4a4ccfe7d6 | ||
|
|
5149320e5b | ||
|
|
cb27ea8185 | ||
|
|
ae04d34c17 | ||
|
|
b84e6743be | ||
|
|
5b43f9a74d | ||
|
|
581cf00ee8 | ||
|
|
a49a1abf12 | ||
|
|
3978c92af1 | ||
|
|
221b6fec69 | ||
|
|
ff10da6575 | ||
|
|
af9aa366fd | ||
|
|
a6de4290c8 | ||
|
|
8d843148d2 | ||
|
|
05cc50ae37 | ||
|
|
b260017fec | ||
|
|
8a4c29e8db | ||
|
|
fde3f54d67 | ||
|
|
cf0db02ced | ||
|
|
25a84074ab | ||
|
|
891f47bcfa | ||
|
|
d41d6077ce | ||
|
|
3d1874af59 | ||
|
|
6ca6cf7474 | ||
|
|
67079664ae | ||
|
|
f5aee94f15 | ||
|
|
2879779804 | ||
|
|
b184e5324d | ||
|
|
ff920303df | ||
|
|
e0d71b0bc0 | ||
|
|
ab49745afe | ||
|
|
ace83ac938 | ||
|
|
75142a10be | ||
|
|
b84bd3a8c4 | ||
|
|
cafae24473 | ||
|
|
cc7bee7d80 | ||
|
|
6602b3b179 | ||
|
|
4f1afaca16 | ||
|
|
d72f092052 | ||
|
|
8744c97a4d | ||
|
|
df846e5904 | ||
|
|
eee53aa5d5 | ||
|
|
f3410d8c59 | ||
|
|
4846f37117 | ||
|
|
39a79be5ae | ||
|
|
fef73e4b86 | ||
|
|
2c5a01a67d |
No files matched your search
+2
-1
@@ -2,7 +2,8 @@
|
||||
.waf-1*
|
||||
.waf3-*
|
||||
.lock*
|
||||
**/*.pyc
|
||||
*.pyc
|
||||
*.pyo
|
||||
build/
|
||||
VERSION
|
||||
unit-tests.conf
|
||||
+20
-15
@@ -4,26 +4,31 @@ set -e
|
||||
JDIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
|
||||
source "$JDIR"/util.sh
|
||||
|
||||
if has OSX $NODE_LABELS; then
|
||||
if has OSX-10.8 $NODE_LABELS; then
|
||||
EXTRA_FLAGS=--c++11
|
||||
fi
|
||||
set -x
|
||||
|
||||
set -x
|
||||
if has OSX $NODE_LABELS; then
|
||||
FORMULAE=(boost openssl pkg-config)
|
||||
brew update
|
||||
brew upgrade
|
||||
brew install boost pkg-config cryptopp $EXTRA_FLAGS
|
||||
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
|
||||
BOOST_PKG=libboost-all-dev
|
||||
if has Ubuntu-12.04 $NODE_LABELS; then
|
||||
BOOST_PKG=libboost1.48-all-dev
|
||||
fi
|
||||
sudo apt-get -qq update
|
||||
sudo apt-get -qy install build-essential pkg-config libboost-all-dev \
|
||||
libsqlite3-dev libssl-dev libpcap-dev
|
||||
|
||||
set -x
|
||||
sudo apt-get update -qq -y
|
||||
sudo apt-get -qq -y install build-essential pkg-config $BOOST_PKG \
|
||||
libcrypto++-dev libsqlite3-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
|
||||
+25
-11
@@ -1,19 +1,32 @@
|
||||
#!/usr/bin/env bash
|
||||
set -x
|
||||
set -e
|
||||
|
||||
JDIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
|
||||
source "$JDIR"/util.sh
|
||||
|
||||
pushd ${CACHE_DIR:-/tmp} >/dev/null
|
||||
set -x
|
||||
|
||||
INSTALLED_VERSION=$((cd ndn-cxx && git rev-parse HEAD) 2>/dev/null || echo NONE)
|
||||
pushd "${CACHE_DIR:-/tmp}" >/dev/null
|
||||
|
||||
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
|
||||
@@ -22,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 configure -j1 --color=yes --enable-shared --disable-static --without-osx-keychain
|
||||
./waf -j1 --color=yes
|
||||
sudo ./waf install -j1 --color=yes
|
||||
./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
|
||||
+32
-23
@@ -1,36 +1,45 @@
|
||||
#!/usr/bin/env bash
|
||||
set -x
|
||||
set -e
|
||||
|
||||
JDIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
|
||||
source "$JDIR"/util.sh
|
||||
|
||||
set -x
|
||||
|
||||
git submodule init
|
||||
git submodule sync
|
||||
git submodule update
|
||||
|
||||
# Cleanup
|
||||
sudo ./waf -j1 --color=yes distclean
|
||||
|
||||
# Configure/build in optimized mode with tests and precompiled headers
|
||||
./waf -j1 --color=yes configure --with-tests
|
||||
./waf -j1 --color=yes build
|
||||
|
||||
# Cleanup
|
||||
sudo ./waf -j1 --color=yes distclean
|
||||
|
||||
# Configure/build in optimized mode without tests and with precompiled headers
|
||||
./waf -j1 --color=yes configure
|
||||
./waf -j1 --color=yes build
|
||||
|
||||
# Cleanup
|
||||
sudo ./waf -j1 --color=yes distclean
|
||||
|
||||
# Configure/build in debug mode
|
||||
if [[ "$JOB_NAME" == *"code-coverage" ]]; then
|
||||
if [[ $JOB_NAME == *"code-coverage" ]]; then
|
||||
COVERAGE="--with-coverage"
|
||||
elif [[ -z $DISABLE_ASAN ]]; then
|
||||
ASAN="--with-sanitizer=address"
|
||||
fi
|
||||
./waf -j1 --color=yes configure --debug --with-tests --without-pch $COVERAGE
|
||||
./waf -j1 --color=yes build
|
||||
|
||||
# Cleanup
|
||||
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 --color=yes configure --with-tests
|
||||
./waf --color=yes build -j${WAF_JOBS:-1}
|
||||
|
||||
# Cleanup
|
||||
sudo env "PATH=$PATH" ./waf --color=yes distclean
|
||||
|
||||
# Configure/build in optimized mode without tests, but with "other tests"
|
||||
./waf --color=yes configure --with-other-tests
|
||||
./waf --color=yes build -j${WAF_JOBS:-1}
|
||||
|
||||
# Cleanup
|
||||
sudo env "PATH=$PATH" ./waf --color=yes distclean
|
||||
fi
|
||||
|
||||
# Configure/build in debug mode with tests and without precompiled headers
|
||||
./waf --color=yes configure --debug --with-tests --without-pch $ASAN $COVERAGE
|
||||
./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
|
||||
+37
-24
@@ -1,41 +1,54 @@
|
||||
#!/usr/bin/env bash
|
||||
set -x
|
||||
set -e
|
||||
|
||||
JDIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
|
||||
source "$JDIR"/util.sh
|
||||
|
||||
set -x
|
||||
|
||||
# Prepare environment
|
||||
rm -Rf ~/.ndnx ~/.ndn
|
||||
rm -Rf ~/.ndn
|
||||
|
||||
if has OSX $NODE_LABELS; then
|
||||
security unlock-keychain -p "named-data"
|
||||
sudo chgrp admin /dev/bpf*
|
||||
sudo chmod g+rw /dev/bpf*
|
||||
fi
|
||||
|
||||
if has Linux $NODE_LABELS; then
|
||||
sudo setcap cap_net_raw,cap_net_admin=eip `pwd`/build/unit-tests-core || true
|
||||
sudo setcap cap_net_raw,cap_net_admin=eip `pwd`/build/unit-tests-daemon || true
|
||||
sudo setcap cap_net_raw,cap_net_admin=eip `pwd`/build/unit-tests-rib || true
|
||||
security unlock-keychain -p named-data
|
||||
fi
|
||||
|
||||
ndnsec-keygen "/tmp/jenkins/$NODE_NAME" | ndnsec-install-cert -
|
||||
|
||||
# Run unit tests
|
||||
# Core
|
||||
if [[ -n $XUNIT ]]; then
|
||||
./build/unit-tests-core -l all -- --log_format2=XML --log_sink2=build/xunit-core-report.xml
|
||||
sudo ./build/unit-tests-core -t TestPrivilegeHelper -l all -- --log_format2=XML --log_sink2=build/xunit-core-sudo-report.xml
|
||||
BOOST_VERSION=$(python -c "import sys; sys.path.append('build/c4che'); import _cache; print(_cache.BOOST_VERSION_NUMBER);")
|
||||
|
||||
./build/unit-tests-daemon -l all -- --log_format2=XML --log_sink2=build/xunit-daemon-report.xml
|
||||
ut_log_args() {
|
||||
if (( BOOST_VERSION >= 106200 )); then
|
||||
echo --logger=HRF,test_suite,stdout:XML,all,build/xunit-${1:-report}.xml
|
||||
else
|
||||
if [[ -n $XUNIT ]]; then
|
||||
echo --log_level=all $( (( BOOST_VERSION >= 106000 )) && echo -- ) \
|
||||
--log_format2=XML --log_sink2=build/xunit-${1:-report}.xml
|
||||
else
|
||||
echo --log_level=test_suite
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
./build/unit-tests-rib -l all -- --log_format2=XML --log_sink2=build/xunit-rib-report.xml
|
||||
else
|
||||
./build/unit-tests-core -l test_suite
|
||||
sudo ./build/unit-tests-core -t TestPrivilegeHelper -l test_suite
|
||||
ASAN_OPTIONS="color=always"
|
||||
ASAN_OPTIONS+=":detect_leaks=false"
|
||||
ASAN_OPTIONS+=":detect_stack_use_after_return=true"
|
||||
ASAN_OPTIONS+=":check_initialization_order=true"
|
||||
ASAN_OPTIONS+=":strict_init_order=true"
|
||||
ASAN_OPTIONS+=":detect_invalid_pointer_pairs=1"
|
||||
ASAN_OPTIONS+=":detect_container_overflow=false"
|
||||
ASAN_OPTIONS+=":strict_string_checks=true"
|
||||
ASAN_OPTIONS+=":strip_path_prefix=${PWD}/"
|
||||
export ASAN_OPTIONS
|
||||
|
||||
./build/unit-tests-daemon -l test_suite
|
||||
# First run all tests as unprivileged user
|
||||
./build/unit-tests-core $(ut_log_args core)
|
||||
./build/unit-tests-daemon $(ut_log_args daemon)
|
||||
./build/unit-tests-rib $(ut_log_args rib)
|
||||
./build/unit-tests-tools $(ut_log_args tools)
|
||||
|
||||
./build/unit-tests-rib -l test_suite
|
||||
fi
|
||||
# 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/*Ethernet* $(ut_log_args daemon-ethernet)
|
||||
sudo -E ./build/unit-tests-daemon -t Face/TestUdpFactory $(ut_log_args daemon-udp-factory)
|
||||
sudo -E ./build/unit-tests-daemon -t Mgmt/TestGeneralConfigSection/UserAndGroupConfig,NoUserConfig $(ut_log_args daemon-user-config)
|
||||
@@ -1,11 +1,41 @@
|
||||
#!/usr/bin/env bash
|
||||
set -x
|
||||
set -e
|
||||
|
||||
JDIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
|
||||
source "$JDIR"/util.sh
|
||||
|
||||
if [[ "$JOB_NAME" == *"code-coverage" ]]; then
|
||||
BASE="`pwd | sed -e 's|/|\\\/|g'`\\"
|
||||
(cd build && gcovr -x -f $BASE/core -f $BASE/daemon -f $BASE/rib -r ../ -o coverage.xml ./)
|
||||
set -x
|
||||
|
||||
if [[ $JOB_NAME == *"code-coverage" ]]; then
|
||||
gcovr --object-directory=build \
|
||||
--output=build/coverage.xml \
|
||||
--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
|
||||
@@ -9,17 +9,17 @@ Environment Variables Used in Build Scripts
|
||||
|
||||
The list should include at least `[OS_TYPE]`, `[DISTRO_TYPE]`, and `[DISTRO_VERSION]`.
|
||||
|
||||
Possible values for Linux OS:
|
||||
Possible values for Linux:
|
||||
|
||||
* `[OS_TYPE]`: `Linux`
|
||||
* `[DISTRO_TYPE]`: `Ubuntu`
|
||||
* `[DISTRO_VERSION]`: `Ubuntu-12.04`, `Ubuntu-14.04`, `Ubuntu-15.04`
|
||||
* `[DISTRO_VERSION]`: `Ubuntu-14.04`, `Ubuntu-16.04`
|
||||
|
||||
Possible values of OSX OS:
|
||||
Possible values for OS X / macOS:
|
||||
|
||||
* `[OS_TYPE]`: `OSX`
|
||||
* `[DISTRO_TYPE]`: `OSX` (can be absent)
|
||||
* `[DISTRO_VERSION]`: `OSX-10.10`, `OSX-10.9`, `OSX-10.8`, `OSX-10.7`
|
||||
* `[DISTRO_VERSION]`: `OSX-10.10`, `OSX-10.11`, `OSX-10.12`
|
||||
|
||||
- `JOB_NAME`: optional variable to define type of the job. Depending on the defined job type,
|
||||
the build scripts can perform different tasks.
|
||||
@@ -27,7 +27,10 @@ Environment Variables Used in Build Scripts
|
||||
Possible values:
|
||||
|
||||
* empty: default build process
|
||||
* `code-coverage` (Linux OS is assumed): build process with code coverage analysis
|
||||
* `code-coverage` (Linux OS is assumed): debug build with tests and code coverage analysis
|
||||
* `limited-build`: only a single debug build with tests
|
||||
|
||||
- `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.
|
||||
+13
-4
@@ -1,9 +1,18 @@
|
||||
has() {
|
||||
local saved_xtrace
|
||||
[[ $- == *x* ]] && saved_xtrace=-x || saved_xtrace=+x
|
||||
set +x
|
||||
|
||||
local p=$1
|
||||
shift
|
||||
local x
|
||||
for x in "$@"; do
|
||||
[[ "${x}" == "${p}" ]] && return 0
|
||||
local i ret=1
|
||||
for i in "$@"; do
|
||||
if [[ "${i}" == "${p}" ]]; then
|
||||
ret=0
|
||||
break
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
|
||||
set ${saved_xtrace}
|
||||
return ${ret}
|
||||
}
|
||||
+111
-12
@@ -1,15 +1,114 @@
|
||||
sudo: true
|
||||
language: cpp
|
||||
os:
|
||||
- linux
|
||||
compiler:
|
||||
- gcc
|
||||
notifications:
|
||||
email:
|
||||
on_success: always
|
||||
on_failure: always
|
||||
dist: trusty
|
||||
sudo: required
|
||||
env:
|
||||
global:
|
||||
- JOB_NAME=limited-build
|
||||
- WAF_JOBS=2
|
||||
|
||||
matrix:
|
||||
include:
|
||||
# Linux/gcc
|
||||
# https://launchpad.net/~ubuntu-toolchain-r/+archive/ubuntu/test/+packages
|
||||
- os: linux
|
||||
env: COMPILER=g++-4.8
|
||||
- os: linux
|
||||
env: COMPILER=g++-4.9
|
||||
addons:
|
||||
apt:
|
||||
sources: ubuntu-toolchain-r-test
|
||||
packages: g++-4.9
|
||||
- os: linux
|
||||
env: COMPILER=g++-5
|
||||
addons:
|
||||
apt:
|
||||
sources: ubuntu-toolchain-r-test
|
||||
packages: g++-5
|
||||
- os: linux
|
||||
env: COMPILER=g++-6
|
||||
addons:
|
||||
apt:
|
||||
sources: ubuntu-toolchain-r-test
|
||||
packages: g++-6
|
||||
- os: linux
|
||||
env: COMPILER=g++-7
|
||||
addons:
|
||||
apt:
|
||||
sources: ubuntu-toolchain-r-test
|
||||
packages: g++-7
|
||||
|
||||
# Linux/clang
|
||||
# https://apt.llvm.org/
|
||||
- os: linux
|
||||
env: COMPILER=clang++-3.5
|
||||
addons:
|
||||
apt:
|
||||
packages: clang-3.5
|
||||
- os: linux
|
||||
env: COMPILER=clang++-3.6
|
||||
addons:
|
||||
apt:
|
||||
packages: clang-3.6
|
||||
- os: linux
|
||||
env: COMPILER=clang++-3.7 DISABLE_ASAN=yes
|
||||
addons:
|
||||
apt:
|
||||
sources: ["llvm-toolchain-precise-3.7", "ubuntu-toolchain-r-test"]
|
||||
packages: clang-3.7
|
||||
- os: linux
|
||||
env: COMPILER=clang++-3.8
|
||||
addons:
|
||||
apt:
|
||||
sources: ["llvm-toolchain-precise-3.8", "ubuntu-toolchain-r-test"]
|
||||
packages: clang-3.8
|
||||
- os: linux
|
||||
env: COMPILER=clang++-3.9 DISABLE_ASAN=yes
|
||||
addons:
|
||||
apt:
|
||||
sources: ["llvm-toolchain-trusty-3.9", "ubuntu-toolchain-r-test"]
|
||||
packages: clang-3.9
|
||||
- os: linux
|
||||
env: COMPILER=clang++-4.0
|
||||
addons:
|
||||
apt:
|
||||
sources: ["llvm-toolchain-trusty-4.0", "ubuntu-toolchain-r-test"]
|
||||
packages: clang-4.0
|
||||
- os: linux
|
||||
env: COMPILER=clang++-5.0
|
||||
addons:
|
||||
apt:
|
||||
sources: ["llvm-toolchain-trusty-5.0", "ubuntu-toolchain-r-test"]
|
||||
packages: clang-5.0
|
||||
- os: linux
|
||||
env: COMPILER=clang++-6.0
|
||||
addons:
|
||||
apt:
|
||||
sources: ["llvm-toolchain-trusty", "ubuntu-toolchain-r-test"]
|
||||
packages: clang-6.0
|
||||
|
||||
# macOS/clang
|
||||
# https://docs.travis-ci.com/user/osx-ci-environment/#OS-X-Version
|
||||
- os: osx
|
||||
osx_image: xcode7.3
|
||||
env: OSX_VERSION=10.11
|
||||
- os: osx
|
||||
osx_image: xcode8.3
|
||||
env: OSX_VERSION=10.12
|
||||
- os: osx
|
||||
osx_image: xcode9.2
|
||||
env: OSX_VERSION=10.12
|
||||
|
||||
allow_failures:
|
||||
- env: COMPILER=clang++-6.0
|
||||
|
||||
fast_finish: true
|
||||
|
||||
before_script:
|
||||
- if [[ ${TRAVIS_OS_NAME} == linux ]]; then export NODE_LABELS="Linux Ubuntu Ubuntu-14.04"; fi
|
||||
- if [[ ${TRAVIS_OS_NAME} == osx ]]; then export NODE_LABELS="OSX OSX-${OSX_VERSION}"; fi
|
||||
- if [[ -n ${COMPILER} ]]; then export CXX=${COMPILER}; fi
|
||||
- ${CXX:-c++} --version
|
||||
- python --version
|
||||
|
||||
script:
|
||||
- if [[ $TRAVIS_OS_NAME == linux ]]; then export NODE_LABELS="Linux Ubuntu Ubuntu-12.04"; fi
|
||||
- if [[ $TRAVIS_OS_NAME == osx ]]; then export NODE_LABELS="OSX OSX-10.10"; fi
|
||||
- echo $NODE_LABELS
|
||||
- ./.jenkins
|
||||
@@ -1,63 +1,30 @@
|
||||
# -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
|
||||
|
||||
"""
|
||||
Copyright (c) 2014-2016, 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/>.
|
||||
"""
|
||||
|
||||
from waflib.Configure import conf
|
||||
|
||||
OVERRIDE = '''
|
||||
class Base
|
||||
{
|
||||
virtual void
|
||||
f(int a);
|
||||
};
|
||||
|
||||
class Derived : public Base
|
||||
{
|
||||
virtual void
|
||||
f(int a) override;
|
||||
};
|
||||
'''
|
||||
|
||||
@conf
|
||||
def check_override(self):
|
||||
self.check_cxx(msg='Checking for override specifier',
|
||||
fragment=OVERRIDE,
|
||||
define_name='HAVE_CXX_OVERRIDE',
|
||||
features='cxx', mandatory=False)
|
||||
|
||||
FINAL = '''
|
||||
class Base
|
||||
{
|
||||
virtual void
|
||||
f(int a);
|
||||
};
|
||||
|
||||
class Derived : public Base
|
||||
{
|
||||
virtual void
|
||||
f(int a) final;
|
||||
};
|
||||
'''
|
||||
|
||||
@conf
|
||||
def check_final(self):
|
||||
self.check_cxx(msg='Checking for final specifier on method',
|
||||
fragment=FINAL,
|
||||
define_name='HAVE_CXX_FINAL',
|
||||
features='cxx', mandatory=False)
|
||||
|
||||
CLASS_FINAL = '''
|
||||
class A final
|
||||
{
|
||||
};
|
||||
'''
|
||||
|
||||
@conf
|
||||
def check_class_final(self):
|
||||
self.check_cxx(msg='Checking for final specifier on class',
|
||||
fragment=CLASS_FINAL,
|
||||
define_name='HAVE_CXX_CLASS_FINAL',
|
||||
features='cxx', mandatory=False)
|
||||
|
||||
def configure(conf):
|
||||
conf.check_override()
|
||||
conf.check_final()
|
||||
conf.check_class_final()
|
||||
pass
|
||||
@@ -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,12 +77,13 @@ 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.CXXFLAGS = supportedFlags + self.env.CXXFLAGS
|
||||
self.env.prepend_value('CXXFLAGS', supportedFlags)
|
||||
|
||||
@Configure.conf
|
||||
def add_supported_linkflags(self, linkflags):
|
||||
@@ -73,12 +96,13 @@ 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.LINKFLAGS = supportedFlags + self.env.LINKFLAGS
|
||||
self.env.prepend_value('LINKFLAGS', supportedFlags)
|
||||
|
||||
|
||||
class CompilerFlags(object):
|
||||
@@ -98,17 +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',
|
||||
'-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):
|
||||
@@ -118,34 +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, 6, 0):
|
||||
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.6.0.')
|
||||
elif version < (4, 7, 0):
|
||||
flags['CXXFLAGS'] += ['-std=c++0x']
|
||||
else:
|
||||
flags['CXXFLAGS'] += ['-std=c++11']
|
||||
if version < (4, 8, 0):
|
||||
flags['DEFINES'] += ['_GLIBCXX_USE_NANOSLEEP'] # Bug #2499
|
||||
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):
|
||||
@@ -159,25 +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=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,22 @@
|
||||
# -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
|
||||
|
||||
def options(opt):
|
||||
opt.add_option('--with-sanitizer', action='store', default='', dest='sanitizers',
|
||||
help='Comma-separated list of compiler sanitizers to enable [default=none]')
|
||||
|
||||
def configure(conf):
|
||||
for san in conf.options.sanitizers.split(','):
|
||||
if not san:
|
||||
continue
|
||||
|
||||
sanflag = '-fsanitize=%s' % san
|
||||
conf.start_msg('Checking if compiler supports %s' % sanflag)
|
||||
|
||||
if conf.check_cxx(cxxflags=['-Werror', sanflag, '-fno-omit-frame-pointer'],
|
||||
linkflags=[sanflag], mandatory=False):
|
||||
conf.end_msg('yes')
|
||||
conf.env.append_unique('CXXFLAGS', [sanflag, '-fno-omit-frame-pointer'])
|
||||
conf.env.append_unique('LINKFLAGS', [sanflag])
|
||||
else:
|
||||
conf.end_msg('no', color='RED')
|
||||
conf.fatal('%s sanitizer is not supported by the current compiler' % san)
|
||||
@@ -6,23 +6,12 @@
|
||||
|
||||
from waflib import Configure
|
||||
|
||||
IS_DEFAULT_CONSTRUCTIBLE_CHECK = '''
|
||||
#include <type_traits>
|
||||
static_assert(std::is_default_constructible<int>::value, "");
|
||||
'''
|
||||
|
||||
IS_MOVE_CONSTRUCTIBLE_CHECK = '''
|
||||
#include <type_traits>
|
||||
static_assert(std::is_move_constructible<int>::value, "");
|
||||
'''
|
||||
|
||||
def configure(conf):
|
||||
if conf.check_cxx(msg='Checking for std::is_default_constructible',
|
||||
fragment=IS_DEFAULT_CONSTRUCTIBLE_CHECK,
|
||||
features='cxx', mandatory=False):
|
||||
conf.define('HAVE_IS_DEFAULT_CONSTRUCTIBLE', 1)
|
||||
conf.env['HAVE_IS_DEFAULT_CONSTRUCTIBLE'] = True
|
||||
|
||||
if conf.check_cxx(msg='Checking for std::is_move_constructible',
|
||||
fragment=IS_MOVE_CONSTRUCTIBLE_CHECK,
|
||||
features='cxx', mandatory=False):
|
||||
|
||||
+7
-10
@@ -1,17 +1,14 @@
|
||||
# encoding: utf-8
|
||||
|
||||
from waflib import Options, Logs, Errors
|
||||
from waflib.Configure import conf
|
||||
# -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
|
||||
|
||||
from waflib import Options, Logs, Errors, Configure
|
||||
import re
|
||||
|
||||
def addWebsocketOptions(self, opt):
|
||||
opt.add_option('--without-websocket', action='store_false', default=True,
|
||||
dest='with_websocket',
|
||||
help='Disable WebSocket face support')
|
||||
dest='with_websocket', help='Disable WebSocket face support')
|
||||
setattr(Options.OptionsContext, "addWebsocketOptions", addWebsocketOptions)
|
||||
|
||||
@conf
|
||||
@Configure.conf
|
||||
def checkWebsocket(self, **kw):
|
||||
if not self.options.with_websocket:
|
||||
return
|
||||
@@ -36,8 +33,8 @@ def checkWebsocket(self, **kw):
|
||||
|
||||
# Looking for the following:
|
||||
# static int const major_version = 0;
|
||||
# static int const minor_version = 5;
|
||||
# static int const patch_version = 1;
|
||||
# static int const minor_version = 7;
|
||||
# static int const patch_version = 0;
|
||||
|
||||
version = [None, None, None]
|
||||
|
||||
@@ -71,7 +68,7 @@ def checkWebsocket(self, **kw):
|
||||
Logs.warn(' git submodule init && git submodule update')
|
||||
Logs.warn('Otherwise, manually download and extract websocketpp library:')
|
||||
Logs.warn(' mkdir websocketpp')
|
||||
Logs.warn(' curl -L https://github.com/zaphoyd/websocketpp/archive/0.5.1.tar.gz > websocket.tar.gz')
|
||||
Logs.warn(' curl -L https://github.com/zaphoyd/websocketpp/archive/0.7.0.tar.gz > websocket.tar.gz')
|
||||
Logs.warn(' tar zxf websocket.tar.gz -C websocketpp/ --strip 1')
|
||||
Logs.warn('Alternatively, WebSocket support can be disabled with --without-websocket')
|
||||
self.fatal("The configuration failed")
|
||||
|
||||
+2
-2
@@ -43,7 +43,7 @@ volunteers.
|
||||
|
||||
* University Pierre & Marie Curie, Sorbonne University
|
||||
|
||||
* Davide Pesavento <http://www.lip6.fr/actualite/personnes-fiche.php?ident=D1469>
|
||||
* Davide Pesavento <https://www.linkedin.com/in/davidepesavento>
|
||||
* Giulio Grassi <http://www.lip6.fr/actualite/personnes-fiche.php?ident=D1461>
|
||||
* Giovanni Pau <http://www.cs.ucla.edu/~gpau/Giovanni_Paus_Home_Page/Home.html>
|
||||
|
||||
@@ -73,7 +73,7 @@ in the library:
|
||||
|
||||
* Tai-Lin Chu <https://www.linkedin.com/pub/tai-lin-chu/55/5b2/669>
|
||||
* Spyridon Mastorakis <http://cs.ucla.edu/~mastorakis/>
|
||||
* Eric Newberry <http://ericnewberry.com>
|
||||
* Eric Newberry <http://cs.arizona.edu/~enewberry/>
|
||||
* Yukai Tu <tuyukai1994@gmail.com>
|
||||
* João Pereira <http://website.jpereira.co.uk>
|
||||
* Mathias Gibbens <gibmat@cs.arizona.edu>
|
||||
|
||||
@@ -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/ac5d7ea5af9734de965688b54a7860259887b537/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/
|
||||
|
||||
+8
-5
@@ -1,6 +1,9 @@
|
||||
Notes for NFD developers
|
||||
========================
|
||||
|
||||
If you are new to the NDN software community, please read the
|
||||
[Contributor's Guide](https://github.com/named-data/NFD/blob/master/CONTRIBUTING.md)
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
@@ -11,7 +14,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).
|
||||
@@ -30,12 +33,12 @@ Include the following license boilerplate into all `.hpp` and `.cpp` files:
|
||||
*/
|
||||
|
||||
If you are affiliated to an NSF-supported NDN project institution, please use the [NDN Team License
|
||||
Boilerplate](http://redmine.named-data.net/projects/nfd/wiki/NDN_Team_License_Boilerplate_(NFD)).
|
||||
Boilerplate](https://redmine.named-data.net/projects/nfd/wiki/NDN_Team_License_Boilerplate_(NFD)).
|
||||
|
||||
Recommendations
|
||||
---------------
|
||||
|
||||
NFD code is subject to NFD [code style](http://redmine.named-data.net/projects/nfd/wiki/CodeStyle).
|
||||
NFD code is subject to NFD [code style](https://redmine.named-data.net/projects/nfd/wiki/CodeStyle).
|
||||
|
||||
|
||||
Running unit-tests
|
||||
@@ -57,7 +60,7 @@ The simplest way to run tests, is just to run the compiled binary without any pa
|
||||
# Run NFD RIB management tests
|
||||
./build/unit-tests-rib
|
||||
|
||||
However, [Boost.Test framework](http://www.boost.org/doc/libs/1_48_0/libs/test/doc/html/)
|
||||
However, [Boost.Test framework](http://www.boost.org/doc/libs/1_54_0/libs/test/doc/html/index.html)
|
||||
is very flexible and allows a number of run-time customization of what tests should be run.
|
||||
For example, it is possible to choose to run only a specific test suite, only a specific
|
||||
test case within a suite, or specific test cases within specific test suites:
|
||||
@@ -90,5 +93,5 @@ or `-p` to show progress bar:
|
||||
|
||||
There are many more command line options available, information about
|
||||
which can be obtained either from the command line using `--help`
|
||||
switch, or online on [Boost.Test library](http://www.boost.org/doc/libs/1_48_0/libs/test/doc/html/)
|
||||
switch, or online on [Boost.Test library](http://www.boost.org/doc/libs/1_54_0/libs/test/doc/html/index.html)
|
||||
website.
|
||||
@@ -1,15 +1,20 @@
|
||||
NFD - Named Data Networking Forwarding Daemon
|
||||
=============================================
|
||||
|
||||
[](https://travis-ci.org/named-data/NFD)
|
||||
|
||||
For complete documentation, including step-by-step installation instructions and
|
||||
tutorials, please visit the [NFD homepage](http://named-data.net/doc/NFD/).
|
||||
tutorials, please visit the [NFD homepage](https://named-data.net/doc/NFD/).
|
||||
|
||||
If you are new to the NDN software community, please read README-dev.md and the
|
||||
[Contributor's Guide](https://github.com/named-data/NFD/blob/master/CONTRIBUTING.md).
|
||||
|
||||
## Overview
|
||||
|
||||
NFD is a network forwarder that implements and evolves together with the Named Data
|
||||
Networking (NDN) [protocol](http://named-data.net/doc/ndn-tlv/). After the initial
|
||||
Networking (NDN) [protocol](https://named-data.net/doc/ndn-tlv/). After the initial
|
||||
release, NFD will become a core component of the
|
||||
[NDN Platform](http://named-data.net/codebase/platform/) and will follow the same release
|
||||
[NDN Platform](https://named-data.net/codebase/platform/) and will follow the same release
|
||||
cycle.
|
||||
|
||||
NFD is an open and free software package licensed under GPL 3.0 license and is the
|
||||
@@ -19,14 +24,14 @@ limitation, refer to
|
||||
[`COPYING.md`](https://github.com/named-data/NFD/blob/master/COPYING.md).
|
||||
|
||||
NFD is developed by a community effort. Although the first release was mostly done by the
|
||||
members of [NSF-sponsored NDN project team](http://named-data.net/project/participants/),
|
||||
members of [NSF-sponsored NDN project team](https://named-data.net/project/participants/),
|
||||
it already contains significant contributions from people outside the project team (for
|
||||
more details, refer to
|
||||
[`AUTHORS.md`](https://github.com/named-data/NFD/blob/master/AUTHORS.md)). We strongly
|
||||
encourage participation from all interested parties, since broader community support is
|
||||
key for NDN to succeed as a new Internet architecture. Bug reports and feedback are
|
||||
highly appreciated and can be made through
|
||||
[Redmine site](http://redmine.named-data.net/projects/nfd) and the
|
||||
[Redmine site](https://redmine.named-data.net/projects/nfd) and the
|
||||
[ndn-interest mailing list](http://www.lists.cs.ucla.edu/mailman/listinfo/ndn-interest).
|
||||
|
||||
The main design goal of NFD is to support diverse experimentation of NDN technology. The
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# Copyright (c) 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.
|
||||
# Copyright (c) 2015-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.
|
||||
@@ -21,10 +21,11 @@
|
||||
# NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# Author: Eric Newberry <enewberry@email.arizona.edu>
|
||||
# Author: Davide Pesavento <davide.pesavento@lip6.fr>
|
||||
|
||||
[Unit]
|
||||
Description=NDN Forwarding Daemon
|
||||
Documentation=man:nfd man:nfdc man:nfd-status
|
||||
Documentation=man:nfd(1) man:nfdc(1)
|
||||
Wants=network-online.target
|
||||
After=network-online.target
|
||||
|
||||
@@ -32,11 +33,20 @@ After=network-online.target
|
||||
Environment=HOME=/usr/local/var/lib/ndn/nfd
|
||||
ExecStart=/usr/local/bin/nfd --config /usr/local/etc/ndn/nfd.conf
|
||||
ExecStartPost=/bin/sh -ec 'sleep 2; if [ -f /usr/local/etc/ndn/nfd-init.sh ]; then . /usr/local/etc/ndn/nfd-init.sh; fi'
|
||||
ExecReload=/bin/kill -HUP $MAINPID
|
||||
Restart=on-failure
|
||||
ProtectSystem=full
|
||||
RestartPreventExitStatus=2 4
|
||||
PrivateTmp=yes
|
||||
PrivateDevices=yes
|
||||
ProtectSystem=full
|
||||
ProtectHome=yes
|
||||
ProtectKernelTunables=yes
|
||||
ProtectKernelModules=yes
|
||||
ProtectControlGroups=yes
|
||||
SystemCallFilter=~@clock @cpu-emulation @debug @keyring @module @mount @obsolete @raw-io @reboot @swap
|
||||
SystemCallArchitectures=native
|
||||
MemoryDenyWriteExecute=yes
|
||||
RestrictRealtime=yes
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -0,0 +1,67 @@
|
||||
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
|
||||
/**
|
||||
* Copyright (c) 2014-2016, 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_ASSERTS_HPP
|
||||
#define NFD_CORE_ASSERTS_HPP
|
||||
|
||||
#include "common.hpp"
|
||||
#include <boost/concept/assert.hpp>
|
||||
#include <boost/concept_check.hpp>
|
||||
#include <type_traits>
|
||||
|
||||
namespace nfd {
|
||||
namespace detail {
|
||||
|
||||
// As of Boost 1.54.0, the internal implementation of BOOST_CONCEPT_ASSERT does not allow
|
||||
// multiple assertions on the same line, so we have to combine multiple concepts together.
|
||||
|
||||
template<typename T>
|
||||
class StlForwardIteratorConcept : public boost::ForwardIterator<T>
|
||||
, public boost::DefaultConstructible<T>
|
||||
{
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace nfd
|
||||
|
||||
/** \brief assert T is default constructible
|
||||
* \sa http://en.cppreference.com/w/cpp/concept/DefaultConstructible
|
||||
*/
|
||||
#define NFD_ASSERT_DEFAULT_CONSTRUCTIBLE(T) \
|
||||
static_assert(std::is_default_constructible<T>::value, \
|
||||
#T " must be default-constructible"); \
|
||||
BOOST_CONCEPT_ASSERT((boost::DefaultConstructible<T>))
|
||||
|
||||
/** \brief assert T is a forward iterator
|
||||
* \sa http://en.cppreference.com/w/cpp/concept/ForwardIterator
|
||||
* \note A forward iterator should be default constructible, but boost::ForwardIterator follows
|
||||
* SGI standard which doesn't require DefaultConstructible, so a separate check is needed.
|
||||
*/
|
||||
#define NFD_ASSERT_FORWARD_ITERATOR(T) \
|
||||
BOOST_CONCEPT_ASSERT((::nfd::detail::StlForwardIteratorConcept<T>)); \
|
||||
static_assert(std::is_default_constructible<T>::value, \
|
||||
#T " must be default-constructible")
|
||||
|
||||
#endif // NFD_CORE_ASSERTS_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-2018, Regents of the University of California,
|
||||
* Arizona Board of Regents,
|
||||
* Colorado State University,
|
||||
* University Pierre & Marie Curie, Sorbonne University,
|
||||
@@ -23,51 +23,23 @@
|
||||
* NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef NFD_COMMON_HPP
|
||||
#define NFD_COMMON_HPP
|
||||
#ifndef NFD_CORE_COMMON_HPP
|
||||
#define NFD_CORE_COMMON_HPP
|
||||
|
||||
#include "config.hpp"
|
||||
#include "core/config.hpp"
|
||||
|
||||
#ifdef WITH_TESTS
|
||||
#define VIRTUAL_WITH_TESTS virtual
|
||||
#define PUBLIC_WITH_TESTS_ELSE_PROTECTED public
|
||||
#define PUBLIC_WITH_TESTS_ELSE_PRIVATE public
|
||||
#define PROTECTED_WITH_TESTS_ELSE_PRIVATE protected
|
||||
#define FINAL_UNLESS_WITH_TESTS
|
||||
#else
|
||||
#define VIRTUAL_WITH_TESTS
|
||||
#define PUBLIC_WITH_TESTS_ELSE_PROTECTED protected
|
||||
#define PUBLIC_WITH_TESTS_ELSE_PRIVATE private
|
||||
#define PROTECTED_WITH_TESTS_ELSE_PRIVATE private
|
||||
#endif
|
||||
|
||||
/** \def DECL_OVERRIDE
|
||||
* \brief expands to 'override' if compiler supports 'override' specifier,
|
||||
* otherwise expands to nothing
|
||||
*/
|
||||
#ifdef HAVE_CXX_OVERRIDE
|
||||
#define DECL_OVERRIDE override
|
||||
#else
|
||||
#define DECL_OVERRIDE
|
||||
#endif
|
||||
|
||||
/** \def DECL_FINAL
|
||||
* \brief expands to 'final' if compiler supports 'final' specifier on method,
|
||||
* otherwise expands to nothing
|
||||
*/
|
||||
#ifdef HAVE_CXX_FINAL
|
||||
#define DECL_FINAL final
|
||||
#else
|
||||
#define DECL_FINAL
|
||||
#endif
|
||||
|
||||
/** \def DECL_CLASS_FINAL
|
||||
* \brief expands to 'final' if compiler supports 'final' specifier on class,
|
||||
* otherwise expands to nothing
|
||||
*/
|
||||
#ifdef HAVE_CXX_CLASS_FINAL
|
||||
#define DECL_CLASS_FINAL final
|
||||
#else
|
||||
#define DECL_CLASS_FINAL
|
||||
#define FINAL_UNLESS_WITH_TESTS final
|
||||
#endif
|
||||
|
||||
#include <cstddef>
|
||||
@@ -84,16 +56,18 @@
|
||||
#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/algorithm/string.hpp>
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
@@ -125,25 +99,28 @@ 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;
|
||||
|
||||
// Not using a namespace alias (namespace tlv = ndn::tlv), because
|
||||
// it doesn't allow NFD to add other members to the namespace
|
||||
namespace tlv {
|
||||
// Don't write "namespace tlv = ndn::tlv", because NFD can add other members into this namespace.
|
||||
using namespace ndn::tlv;
|
||||
} // namespace tlv
|
||||
|
||||
namespace lp = ndn::lp;
|
||||
namespace name = ndn::name;
|
||||
namespace time = ndn::time;
|
||||
namespace signal = ndn::util::signal;
|
||||
namespace time = ndn::time;
|
||||
using namespace ndn::time_literals;
|
||||
|
||||
} // namespace nfd
|
||||
|
||||
#endif // NFD_COMMON_HPP
|
||||
#endif // NFD_CORE_COMMON_HPP
|
||||
+21
-40
@@ -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-2018, 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
|
||||
@@ -59,10 +56,10 @@ ConfigFile::ignoreUnknownSection(const std::string& filename,
|
||||
}
|
||||
|
||||
bool
|
||||
ConfigFile::parseYesNo(const ConfigSection::value_type& option,
|
||||
ConfigFile::parseYesNo(const ConfigSection& node, const std::string& key,
|
||||
const std::string& sectionName)
|
||||
{
|
||||
auto value = option.second.get_value<std::string>();
|
||||
auto value = node.get_value<std::string>();
|
||||
|
||||
if (value == "yes") {
|
||||
return true;
|
||||
@@ -72,7 +69,7 @@ ConfigFile::parseYesNo(const ConfigSection::value_type& option,
|
||||
}
|
||||
|
||||
BOOST_THROW_EXCEPTION(Error("Invalid value \"" + value + "\" for option \"" +
|
||||
option.first + "\" in \"" + sectionName + "\" section"));
|
||||
key + "\" in \"" + sectionName + "\" section"));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -85,14 +82,10 @@ ConfigFile::addSectionHandler(const std::string& sectionName,
|
||||
void
|
||||
ConfigFile::parse(const std::string& filename, bool isDryRun)
|
||||
{
|
||||
std::ifstream inputFile;
|
||||
inputFile.open(filename.c_str());
|
||||
if (!inputFile.good() || !inputFile.is_open())
|
||||
{
|
||||
std::string msg = "Failed to read configuration file: ";
|
||||
msg += filename;
|
||||
BOOST_THROW_EXCEPTION(Error(msg));
|
||||
}
|
||||
std::ifstream inputFile(filename);
|
||||
if (!inputFile.good() || !inputFile.is_open()) {
|
||||
BOOST_THROW_EXCEPTION(Error("Failed to read configuration file " + filename));
|
||||
}
|
||||
parse(inputFile, isDryRun, filename);
|
||||
inputFile.close();
|
||||
}
|
||||
@@ -107,18 +100,13 @@ ConfigFile::parse(const std::string& input, bool isDryRun, const std::string& fi
|
||||
void
|
||||
ConfigFile::parse(std::istream& input, bool isDryRun, const std::string& filename)
|
||||
{
|
||||
try
|
||||
{
|
||||
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()));
|
||||
}
|
||||
try {
|
||||
boost::property_tree::read_info(input, m_global);
|
||||
}
|
||||
catch (const boost::property_tree::info_parser_error& error) {
|
||||
BOOST_THROW_EXCEPTION(Error("Failed to parse configuration file " + filename +
|
||||
": " + error.message() + " on line " + to_string(error.line())));
|
||||
}
|
||||
|
||||
process(isDryRun, filename);
|
||||
}
|
||||
@@ -135,16 +123,9 @@ 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));
|
||||
}
|
||||
|
||||
for (const auto& i : m_global) {
|
||||
try {
|
||||
ConfigSectionHandler subscriber = m_subscriptions.at(i.first);
|
||||
const ConfigSectionHandler& subscriber = m_subscriptions.at(i.first);
|
||||
subscriber(i.second, isDryRun, filename);
|
||||
}
|
||||
catch (const std::out_of_range&) {
|
||||
@@ -153,4 +134,4 @@ ConfigFile::process(bool isDryRun, const std::string& filename) const
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nfd
|
||||
+55
-37
@@ -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,
|
||||
@@ -32,19 +32,29 @@
|
||||
|
||||
namespace nfd {
|
||||
|
||||
/** \brief a config file section
|
||||
*/
|
||||
typedef boost::property_tree::ptree ConfigSection;
|
||||
|
||||
/// \brief callback for config file sections
|
||||
typedef function<void(const ConfigSection& /*section*/,
|
||||
bool /*isDryRun*/,
|
||||
const std::string& /*filename*/)> ConfigSectionHandler;
|
||||
/** \brief an optional config file section
|
||||
*/
|
||||
typedef boost::optional<const ConfigSection&> OptionalConfigSection;
|
||||
|
||||
/// \brief callback for config file sections without a subscribed handler
|
||||
typedef function<void(const std::string& /*filename*/,
|
||||
const std::string& /*sectionName*/,
|
||||
const ConfigSection& /*section*/,
|
||||
bool /*isDryRun*/)> UnknownConfigSectionHandler;
|
||||
/** \brief callback to process a config file section
|
||||
*/
|
||||
typedef function<void(const ConfigSection& section,
|
||||
bool isDryRun,
|
||||
const std::string& filename)> ConfigSectionHandler;
|
||||
|
||||
/** \brief callback to process a config file section without a \p ConfigSectionHandler
|
||||
*/
|
||||
typedef function<void(const std::string& filename,
|
||||
const std::string& sectionName,
|
||||
const ConfigSection& section,
|
||||
bool isDryRun)> UnknownConfigSectionHandler;
|
||||
|
||||
/** \brief configuration file parsing utility
|
||||
*/
|
||||
class ConfigFile : noncopyable
|
||||
{
|
||||
public:
|
||||
@@ -61,6 +71,7 @@ public:
|
||||
explicit
|
||||
ConfigFile(UnknownConfigSectionHandler unknownSectionCallback = throwErrorOnUnknownSection);
|
||||
|
||||
public: // unknown section handlers
|
||||
static void
|
||||
throwErrorOnUnknownSection(const std::string& filename,
|
||||
const std::string& sectionName,
|
||||
@@ -73,27 +84,50 @@ public:
|
||||
const ConfigSection& section,
|
||||
bool isDryRun);
|
||||
|
||||
/**
|
||||
* \brief parse a config option that can be either "yes" or "no"
|
||||
*
|
||||
* \return true if "yes", false if "no"
|
||||
* \throw Error the value is neither "yes" nor "no"
|
||||
public: // parse helpers
|
||||
/** \brief parse a config option that can be either "yes" or "no"
|
||||
* \retval true "yes"
|
||||
* \retval false "no"
|
||||
* \throw Error the value is neither "yes" nor "no"
|
||||
*/
|
||||
static bool
|
||||
parseYesNo(const ConfigSection::value_type& option,
|
||||
const std::string& sectionName);
|
||||
parseYesNo(const ConfigSection& node, const std::string& key, const std::string& sectionName);
|
||||
|
||||
static bool
|
||||
parseYesNo(const ConfigSection::value_type& option, const std::string& sectionName)
|
||||
{
|
||||
return parseYesNo(option.second, option.first, sectionName);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief parse a numeric (integral or floating point) config option
|
||||
* \tparam T an arithmetic type
|
||||
*
|
||||
* \return the numeric value of the parsed option
|
||||
* \throw Error the value cannot be converted to the specified type
|
||||
*/
|
||||
template <typename T>
|
||||
static typename std::enable_if<std::is_arithmetic<T>::value, T>::type
|
||||
parseNumber(const ConfigSection::value_type& option,
|
||||
const std::string& sectionName);
|
||||
template<typename T>
|
||||
static T
|
||||
parseNumber(const ConfigSection& node, const std::string& key, const std::string& sectionName)
|
||||
{
|
||||
static_assert(std::is_arithmetic<T>::value, "T must be an arithmetic type");
|
||||
|
||||
boost::optional<T> value = node.get_value_optional<T>();
|
||||
if (value) {
|
||||
return *value;
|
||||
}
|
||||
BOOST_THROW_EXCEPTION(Error("Invalid value \"" + node.get_value<std::string>() +
|
||||
"\" for option \"" + key + "\" in \"" + sectionName + "\" section"));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static T
|
||||
parseNumber(const ConfigSection::value_type& option, const std::string& sectionName)
|
||||
{
|
||||
return parseNumber<T>(option.second, option.first, sectionName);
|
||||
}
|
||||
|
||||
public: // setup and parsing
|
||||
/// \brief setup notification of configuration file sections
|
||||
void
|
||||
addSectionHandler(const std::string& sectionName,
|
||||
@@ -146,22 +180,6 @@ private:
|
||||
ConfigSection m_global;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
inline typename std::enable_if<std::is_arithmetic<T>::value, T>::type
|
||||
ConfigFile::parseNumber(const ConfigSection::value_type& option,
|
||||
const std::string& sectionName)
|
||||
{
|
||||
auto value = option.second.get_value<std::string>();
|
||||
|
||||
try {
|
||||
return boost::lexical_cast<T>(value);
|
||||
}
|
||||
catch (const boost::bad_lexical_cast&) {
|
||||
BOOST_THROW_EXCEPTION(Error("Invalid value \"" + value + "\" for option \"" +
|
||||
option.first + "\" in \"" + sectionName + "\" section"));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace nfd
|
||||
|
||||
#endif // NFD_CORE_CONFIG_FILE_HPP
|
||||
+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-2015, Regents of the University of California,
|
||||
* Copyright (c) 2014-2016, 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_CORE_EXTENDED_ERROR_MESSAGE_HPP
|
||||
|
||||
#include <boost/exception/get_error_info.hpp>
|
||||
#include <sstream>
|
||||
|
||||
namespace nfd {
|
||||
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
|
||||
/*
|
||||
* Copyright (c) 2014-2018, 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_FIB_MAX_DEPTH_HPP
|
||||
#define NFD_CORE_FIB_MAX_DEPTH_HPP
|
||||
|
||||
namespace nfd {
|
||||
|
||||
/** \brief Maximum number of components in a FIB entry prefix.
|
||||
*/
|
||||
static const int FIB_MAX_DEPTH = 32;
|
||||
|
||||
} // namespace nfd
|
||||
|
||||
#endif // NFD_CORE_FIB_MAX_DEPTH_HPP
|
||||
+36
-17
@@ -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,
|
||||
@@ -25,8 +25,13 @@
|
||||
|
||||
#include "logger-factory.hpp"
|
||||
|
||||
#include <ndn-cxx/util/logging.hpp>
|
||||
|
||||
#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
|
||||
@@ -53,6 +58,15 @@ LoggerFactory::LoggerFactory()
|
||||
m_levelNames["DEBUG"] = LOG_DEBUG;
|
||||
m_levelNames["TRACE"] = LOG_TRACE;
|
||||
m_levelNames["ALL"] = LOG_ALL;
|
||||
|
||||
// Let ndn-cxx logging facility initialize Boost.Log backend,
|
||||
// so that only one sink is attached to Boost.Log core.
|
||||
ndn::util::Logging::setDestination(std::clog);
|
||||
}
|
||||
|
||||
LoggerFactory::~LoggerFactory()
|
||||
{
|
||||
ndn::util::Logging::flush();
|
||||
}
|
||||
|
||||
void
|
||||
@@ -64,8 +78,7 @@ LoggerFactory::setConfigFile(ConfigFile& config)
|
||||
LogLevel
|
||||
LoggerFactory::parseLevel(const std::string& level)
|
||||
{
|
||||
std::string upperLevel = level;
|
||||
boost::to_upper(upperLevel);
|
||||
std::string upperLevel = boost::to_upper_copy(level);
|
||||
|
||||
// std::cerr << "parsing level: " << upperLevel << std::endl;;
|
||||
// std::cerr << "# levels: " << m_levelNames.size() << std::endl;
|
||||
@@ -112,19 +125,19 @@ LoggerFactory::onConfig(const ConfigSection& section,
|
||||
bool isDryRun,
|
||||
const std::string& filename)
|
||||
{
|
||||
// log
|
||||
// {
|
||||
// ; default_level specifies the logging level for modules
|
||||
// ; that are not explicitly named. All debugging levels
|
||||
// ; listed above the selected value are enabled.
|
||||
//
|
||||
// default_level INFO
|
||||
//
|
||||
// ; You may also override the default for specific modules:
|
||||
//
|
||||
// FibManager DEBUG
|
||||
// Forwarder WARN
|
||||
// }
|
||||
// log
|
||||
// {
|
||||
// ; default_level specifies the logging level for modules
|
||||
// ; that are not explicitly named. All debugging levels
|
||||
// ; listed above the selected value are enabled.
|
||||
//
|
||||
// default_level INFO
|
||||
//
|
||||
// ; You may also override the default for specific modules:
|
||||
//
|
||||
// FibManager DEBUG
|
||||
// Forwarder WARN
|
||||
// }
|
||||
|
||||
if (!isDryRun) {
|
||||
ConfigSection::const_assoc_iterator item = section.find("default_level");
|
||||
@@ -173,6 +186,12 @@ LoggerFactory::setDefaultLevel(LogLevel level)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
LoggerFactory::flushBackend()
|
||||
{
|
||||
ndn::util::Logging::flush();
|
||||
}
|
||||
|
||||
Logger&
|
||||
LoggerFactory::create(const std::string& moduleName)
|
||||
{
|
||||
|
||||
@@ -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-2016, Regents of the University of California,
|
||||
* Arizona Board of Regents,
|
||||
* Colorado State University,
|
||||
* University Pierre & Marie Curie, Sorbonne University,
|
||||
@@ -35,6 +35,8 @@
|
||||
#include "config-file.hpp"
|
||||
#include "logger.hpp"
|
||||
|
||||
#include <mutex>
|
||||
|
||||
namespace nfd {
|
||||
|
||||
class LoggerFactory : noncopyable
|
||||
@@ -77,10 +79,15 @@ PUBLIC_WITH_TESTS_ELSE_PRIVATE:
|
||||
void
|
||||
setDefaultLevel(LogLevel level);
|
||||
|
||||
void
|
||||
flushBackend();
|
||||
|
||||
private:
|
||||
|
||||
LoggerFactory();
|
||||
|
||||
~LoggerFactory();
|
||||
|
||||
Logger&
|
||||
createLogger(const std::string& moduleName);
|
||||
|
||||
|
||||
+6
-10
@@ -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-2016, Regents of the University of California,
|
||||
* Arizona Board of Regents,
|
||||
* Colorado State University,
|
||||
* University Pierre & Marie Curie, Sorbonne University,
|
||||
@@ -36,25 +36,23 @@
|
||||
|
||||
namespace nfd {
|
||||
|
||||
std::mutex g_logMutex;
|
||||
|
||||
Logger::Logger(const std::string& name, LogLevel level)
|
||||
: m_moduleName(name)
|
||||
, m_enabledLogLevel(level)
|
||||
{
|
||||
}
|
||||
|
||||
const char*
|
||||
Logger::now()
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os, const LoggerTimestamp&)
|
||||
{
|
||||
using namespace ndn::time;
|
||||
|
||||
static const microseconds::rep ONE_SECOND = 1000000;
|
||||
microseconds::rep microsecondsSinceEpoch = duration_cast<microseconds>(
|
||||
system_clock::now().time_since_epoch()).count();
|
||||
system_clock::now().time_since_epoch()).count();
|
||||
|
||||
// 10 (whole seconds) + '.' + 6 (fraction) + '\0'
|
||||
static char buffer[10 + 1 + 6 + 1];
|
||||
char buffer[10 + 1 + 6 + 1];
|
||||
BOOST_ASSERT_MSG(microsecondsSinceEpoch / ONE_SECOND <= 9999999999L,
|
||||
"whole seconds cannot fit in 10 characters");
|
||||
|
||||
@@ -66,9 +64,7 @@ Logger::now()
|
||||
microsecondsSinceEpoch / ONE_SECOND,
|
||||
microsecondsSinceEpoch % ONE_SECOND);
|
||||
|
||||
// It's okay to return a statically allocated buffer, because Logger::now() is invoked
|
||||
// only once per log macro.
|
||||
return buffer;
|
||||
return os << buffer;
|
||||
}
|
||||
|
||||
} // namespace nfd
|
||||
+40
-24
@@ -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-2016, Regents of the University of California,
|
||||
* Arizona Board of Regents,
|
||||
* Colorado State University,
|
||||
* University Pierre & Marie Curie, Sorbonne University,
|
||||
@@ -31,8 +31,8 @@
|
||||
#ifdef HAVE_CUSTOM_LOGGER
|
||||
#include "custom-logger.hpp"
|
||||
#else
|
||||
|
||||
#include <mutex>
|
||||
#include <boost/log/common.hpp>
|
||||
#include <boost/log/sources/logger.hpp>
|
||||
|
||||
namespace nfd {
|
||||
|
||||
@@ -84,12 +84,8 @@ public:
|
||||
m_moduleName = name;
|
||||
}
|
||||
|
||||
/** \return string representation of time since epoch: seconds.microseconds
|
||||
* \warning Return value is in a statically allocated buffer,
|
||||
* which subsequent calls will overwrite.
|
||||
*/
|
||||
static const char*
|
||||
now();
|
||||
public:
|
||||
boost::log::sources::logger boostLogger;
|
||||
|
||||
private:
|
||||
std::string m_moduleName;
|
||||
@@ -103,6 +99,20 @@ operator<<(std::ostream& output, const Logger& logger)
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief a tag that writes a timestamp upon stream output
|
||||
*/
|
||||
struct LoggerTimestamp
|
||||
{
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief write a timestamp to \p os
|
||||
* \note This function is thread-safe.
|
||||
*/
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os, const LoggerTimestamp&);
|
||||
|
||||
} // namespace nfd
|
||||
|
||||
#include "core/logger-factory.hpp"
|
||||
@@ -110,36 +120,42 @@ operator<<(std::ostream& output, const Logger& logger)
|
||||
namespace nfd {
|
||||
|
||||
#define NFD_LOG_INIT(name) \
|
||||
static nfd::Logger& g_logger = nfd::LoggerFactory::create(name)
|
||||
static ::nfd::Logger& g_logger = ::nfd::LoggerFactory::create(name)
|
||||
|
||||
#define NFD_LOG_INCLASS_DECLARE() \
|
||||
static nfd::Logger& g_logger
|
||||
static ::nfd::Logger& g_logger
|
||||
|
||||
#define NFD_LOG_INCLASS_DEFINE(cls, name) \
|
||||
nfd::Logger& cls::g_logger = nfd::LoggerFactory::create(name)
|
||||
::nfd::Logger& cls::g_logger = ::nfd::LoggerFactory::create(name)
|
||||
|
||||
#define NFD_LOG_INCLASS_TEMPLATE_DEFINE(cls, name) \
|
||||
template<class T> \
|
||||
nfd::Logger& cls<T>::g_logger = nfd::LoggerFactory::create(name)
|
||||
::nfd::Logger& cls<T>::g_logger = ::nfd::LoggerFactory::create(name)
|
||||
|
||||
#define NFD_LOG_INCLASS_TEMPLATE_SPECIALIZATION_DEFINE(cls, specialization, name) \
|
||||
template<> \
|
||||
nfd::Logger& cls<specialization>::g_logger = nfd::LoggerFactory::create(name)
|
||||
::nfd::Logger& cls<specialization>::g_logger = ::nfd::LoggerFactory::create(name)
|
||||
|
||||
#define NFD_LOG_INCLASS_2TEMPLATE_SPECIALIZATION_DEFINE(cls, s1, s2, name) \
|
||||
template<> \
|
||||
nfd::Logger& cls<s1, s2>::g_logger = nfd::LoggerFactory::create(name)
|
||||
::nfd::Logger& cls<s1, s2>::g_logger = ::nfd::LoggerFactory::create(name)
|
||||
|
||||
extern std::mutex g_logMutex;
|
||||
#if (BOOST_VERSION >= 105900) && (BOOST_VERSION < 106000)
|
||||
// workaround Boost bug 11549
|
||||
#define NFD_BOOST_LOG(x) BOOST_LOG(x) << ""
|
||||
#else
|
||||
#define NFD_BOOST_LOG(x) BOOST_LOG(x)
|
||||
#endif
|
||||
|
||||
#define NFD_LOG(level, msg, expression) \
|
||||
do { \
|
||||
if (g_logger.isEnabled(::nfd::LOG_##level)) { \
|
||||
std::lock_guard<std::mutex> lock(::nfd::g_logMutex); \
|
||||
std::clog << ::nfd::Logger::now() << " "#msg": " \
|
||||
<< "[" << g_logger << "] " << expression << "\n"; \
|
||||
} \
|
||||
} while (false)
|
||||
#define NFD_LOG_LINE(msg, expression) \
|
||||
::nfd::LoggerTimestamp{} << " "#msg": " << "[" << g_logger << "] " << expression
|
||||
|
||||
#define NFD_LOG(level, msg, expression) \
|
||||
do { \
|
||||
if (g_logger.isEnabled(::nfd::LOG_##level)) { \
|
||||
NFD_BOOST_LOG(g_logger.boostLogger) << NFD_LOG_LINE(msg, expression); \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
#define NFD_LOG_TRACE(expression) NFD_LOG(TRACE, TRACE, expression)
|
||||
#define NFD_LOG_DEBUG(expression) NFD_LOG(DEBUG, DEBUG, expression)
|
||||
|
||||
@@ -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,
|
||||
@@ -30,16 +30,14 @@ namespace nfd {
|
||||
using ndn::mgmt::ValidateParameters;
|
||||
using ndn::mgmt::Authorization;
|
||||
|
||||
ManagerBase::ManagerBase(Dispatcher& dispatcher,
|
||||
CommandValidator& validator,
|
||||
const std::string& module)
|
||||
ManagerBase::ManagerBase(Dispatcher& dispatcher, const std::string& module)
|
||||
: m_dispatcher(dispatcher)
|
||||
, m_validator(validator)
|
||||
, m_mgmtModuleName(module)
|
||||
, m_module(module)
|
||||
{
|
||||
m_validator.addSupportedPrivilege(module);
|
||||
}
|
||||
|
||||
ManagerBase::~ManagerBase() = default;
|
||||
|
||||
void
|
||||
ManagerBase::registerStatusDatasetHandler(const std::string& verb,
|
||||
const ndn::mgmt::StatusDatasetHandler& handler)
|
||||
@@ -55,20 +53,6 @@ ManagerBase::registerNotificationStream(const std::string& verb)
|
||||
return m_dispatcher.addNotificationStream(makeRelPrefix(verb));
|
||||
}
|
||||
|
||||
void
|
||||
ManagerBase::authorize(const Name& prefix, const Interest& interest,
|
||||
const ndn::mgmt::ControlParameters* params,
|
||||
ndn::mgmt::AcceptContinuation accept,
|
||||
ndn::mgmt::RejectContinuation reject)
|
||||
{
|
||||
BOOST_ASSERT(params != nullptr);
|
||||
BOOST_ASSERT(typeid(*params) == typeid(ndn::nfd::ControlParameters));
|
||||
|
||||
m_validator.validate(interest,
|
||||
bind(&ManagerBase::extractRequester, this, interest, accept),
|
||||
bind([&] { reject(ndn::mgmt::RejectReply::STATUS403); }));
|
||||
}
|
||||
|
||||
void
|
||||
ManagerBase::extractRequester(const Interest& interest,
|
||||
ndn::mgmt::AcceptContinuation accept)
|
||||
@@ -116,5 +100,4 @@ ManagerBase::handleCommand(shared_ptr<nfd::ControlCommand> command,
|
||||
handler(*command, prefix, interest, parameters, done);
|
||||
}
|
||||
|
||||
|
||||
} // 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,
|
||||
@@ -23,16 +23,15 @@
|
||||
* NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef NFD_DAEMON_MGMT_MANAGER_BASE_HPP
|
||||
#define NFD_DAEMON_MGMT_MANAGER_BASE_HPP
|
||||
#ifndef NFD_CORE_MANAGER_BASE_HPP
|
||||
#define NFD_CORE_MANAGER_BASE_HPP
|
||||
|
||||
#include "common.hpp"
|
||||
#include "mgmt/command-validator.hpp"
|
||||
|
||||
#include <ndn-cxx/mgmt/dispatcher.hpp>
|
||||
#include <ndn-cxx/management/nfd-control-command.hpp>
|
||||
#include <ndn-cxx/management/nfd-control-response.hpp>
|
||||
#include <ndn-cxx/management/nfd-control-parameters.hpp>
|
||||
#include <ndn-cxx/mgmt/nfd/control-command.hpp>
|
||||
#include <ndn-cxx/mgmt/nfd/control-response.hpp>
|
||||
#include <ndn-cxx/mgmt/nfd/control-parameters.hpp>
|
||||
|
||||
namespace nfd {
|
||||
|
||||
@@ -43,10 +42,10 @@ using ndn::nfd::ControlResponse;
|
||||
using ndn::nfd::ControlParameters;
|
||||
|
||||
/**
|
||||
* @brief a collection of common functions shared by all NFD managers,
|
||||
* @brief a collection of common functions shared by all NFD managers and RIB manager,
|
||||
* such as communicating with the dispatcher and command validator.
|
||||
*/
|
||||
class ManagerBase : public noncopyable
|
||||
class ManagerBase : noncopyable
|
||||
{
|
||||
public:
|
||||
class Error : public std::runtime_error
|
||||
@@ -60,12 +59,18 @@ public:
|
||||
};
|
||||
|
||||
public:
|
||||
ManagerBase(Dispatcher& dispatcher,
|
||||
CommandValidator& validator,
|
||||
const std::string& module);
|
||||
ManagerBase(Dispatcher& dispatcher, const std::string& module);
|
||||
|
||||
virtual
|
||||
~ManagerBase();
|
||||
|
||||
const std::string&
|
||||
getModule() const
|
||||
{
|
||||
return m_module;
|
||||
}
|
||||
|
||||
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,
|
||||
@@ -84,27 +89,7 @@ PUBLIC_WITH_TESTS_ELSE_PROTECTED: // registrations to the dispatcher
|
||||
ndn::mgmt::PostNotification
|
||||
registerNotificationStream(const std::string& verb);
|
||||
|
||||
PUBLIC_WITH_TESTS_ELSE_PRIVATE: // command validation
|
||||
/**
|
||||
* @brief validate a request for ControlCommand.
|
||||
*
|
||||
* This is called by the dispatcher.
|
||||
*
|
||||
* @pre params != null
|
||||
* @pre typeid(*params) == typeid(ndn::nfd::ControlParameters)
|
||||
*
|
||||
* @param prefix the top prefix
|
||||
* @param interest a request for ControlCommand
|
||||
* @param params the parameters for ControlCommand
|
||||
* @param accept callback of successful validation, take the requester string as a argument
|
||||
* @param reject callback of failure in validation, take the action code as a argument
|
||||
*/
|
||||
void
|
||||
authorize(const Name& prefix, const Interest& interest,
|
||||
const ndn::mgmt::ControlParameters* params,
|
||||
ndn::mgmt::AcceptContinuation accept,
|
||||
ndn::mgmt::RejectContinuation reject);
|
||||
|
||||
PUBLIC_WITH_TESTS_ELSE_PROTECTED:
|
||||
/**
|
||||
* @brief extract a requester from a ControlCommand request
|
||||
*
|
||||
@@ -117,7 +102,13 @@ PUBLIC_WITH_TESTS_ELSE_PRIVATE: // command validation
|
||||
extractRequester(const Interest& interest,
|
||||
ndn::mgmt::AcceptContinuation accept);
|
||||
|
||||
PUBLIC_WITH_TESTS_ELSE_PRIVATE: // helpers
|
||||
PUBLIC_WITH_TESTS_ELSE_PRIVATE:
|
||||
/**
|
||||
* @return an authorization function for specified management module and verb
|
||||
*/
|
||||
virtual ndn::mgmt::Authorization
|
||||
makeAuthorization(const std::string& verb) = 0;
|
||||
|
||||
/**
|
||||
* @brief validate the @p parameters for a given @p command
|
||||
*
|
||||
@@ -150,15 +141,14 @@ PUBLIC_WITH_TESTS_ELSE_PRIVATE: // helpers
|
||||
makeRelPrefix(const std::string& verb);
|
||||
|
||||
private:
|
||||
Dispatcher& m_dispatcher;
|
||||
CommandValidator& m_validator;
|
||||
std::string m_mgmtModuleName;
|
||||
Dispatcher& m_dispatcher;
|
||||
std::string m_module;
|
||||
};
|
||||
|
||||
inline PartialName
|
||||
ManagerBase::makeRelPrefix(const std::string& verb)
|
||||
{
|
||||
return PartialName(m_mgmtModuleName).append(verb);
|
||||
return PartialName(m_module).append(verb);
|
||||
}
|
||||
|
||||
template<typename Command>
|
||||
@@ -170,11 +160,11 @@ ManagerBase::registerCommandHandler(const std::string& verb,
|
||||
|
||||
m_dispatcher.addControlCommand<ControlParameters>(
|
||||
makeRelPrefix(verb),
|
||||
bind(&ManagerBase::authorize, this, _1, _2, _3, _4, _5),
|
||||
makeAuthorization(verb),
|
||||
bind(&ManagerBase::validateParameters, cref(*command), _1),
|
||||
bind(&ManagerBase::handleCommand, command, handler, _1, _2, _3, _4));
|
||||
}
|
||||
|
||||
} // namespace nfd
|
||||
|
||||
#endif // NFD_DAEMON_MGMT_MANAGER_BASE_HPP
|
||||
#endif // NFD_CORE_MANAGER_BASE_HPP
|
||||
@@ -0,0 +1,138 @@
|
||||
/* -*- 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 "network-interface-predicate.hpp"
|
||||
|
||||
#include "config-file.hpp"
|
||||
#include "network.hpp"
|
||||
|
||||
#include <fnmatch.h>
|
||||
|
||||
namespace nfd {
|
||||
|
||||
NetworkInterfacePredicate::NetworkInterfacePredicate()
|
||||
{
|
||||
this->clear();
|
||||
}
|
||||
|
||||
void
|
||||
NetworkInterfacePredicate::clear()
|
||||
{
|
||||
m_whitelist = std::set<std::string>{"*"};
|
||||
m_blacklist.clear();
|
||||
}
|
||||
|
||||
static void
|
||||
parseList(std::set<std::string>& set, const boost::property_tree::ptree& list, const std::string& section)
|
||||
{
|
||||
set.clear();
|
||||
|
||||
for (const auto& item : list) {
|
||||
if (item.first == "*") {
|
||||
// insert wildcard
|
||||
set.insert(item.first);
|
||||
}
|
||||
else if (item.first == "ifname") {
|
||||
// very basic sanity check for interface names
|
||||
auto name = item.second.get_value<std::string>();
|
||||
if (name.empty()) {
|
||||
BOOST_THROW_EXCEPTION(ConfigFile::Error("Empty interface name in \"" + section + "\" section"));
|
||||
}
|
||||
set.insert(name);
|
||||
}
|
||||
else if (item.first == "ether") {
|
||||
// validate ethernet address
|
||||
auto addr = item.second.get_value<std::string>();
|
||||
if (ndn::ethernet::Address::fromString(addr).isNull()) {
|
||||
BOOST_THROW_EXCEPTION(ConfigFile::Error("Malformed ether address \"" + addr +
|
||||
"\" in \"" + section + "\" section"));
|
||||
}
|
||||
set.insert(addr);
|
||||
}
|
||||
else if (item.first == "subnet") {
|
||||
// example subnet: 10.0.0.0/8
|
||||
auto cidr = item.second.get_value<std::string>();
|
||||
if (!Network::isValidCidr(cidr)) {
|
||||
BOOST_THROW_EXCEPTION(ConfigFile::Error("Malformed subnet declaration \"" + cidr +
|
||||
"\" in \"" + section + "\" section"));
|
||||
}
|
||||
set.insert(cidr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
NetworkInterfacePredicate::parseWhitelist(const boost::property_tree::ptree& list)
|
||||
{
|
||||
parseList(m_whitelist, list, "whitelist");
|
||||
}
|
||||
|
||||
void
|
||||
NetworkInterfacePredicate::parseBlacklist(const boost::property_tree::ptree& list)
|
||||
{
|
||||
parseList(m_blacklist, list, "blacklist");
|
||||
}
|
||||
|
||||
static bool
|
||||
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.getNetworkAddresses()) {
|
||||
if (n.doesContain(addr.getIp())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rule == "*" ||
|
||||
doesMatchPattern(netif.getName(), rule) ||
|
||||
netif.getEthernetAddress().toString() == rule;
|
||||
}
|
||||
|
||||
bool
|
||||
NetworkInterfacePredicate::operator()(const ndn::net::NetworkInterface& netif) const
|
||||
{
|
||||
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
|
||||
NetworkInterfacePredicate::operator==(const NetworkInterfacePredicate& other) const
|
||||
{
|
||||
return this->m_whitelist == other.m_whitelist &&
|
||||
this->m_blacklist == other.m_blacklist;
|
||||
}
|
||||
|
||||
} // namespace nfd
|
||||
@@ -0,0 +1,79 @@
|
||||
/* -*- 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_CORE_NETWORK_INTERFACE_PREDICATE_HPP
|
||||
#define NFD_CORE_NETWORK_INTERFACE_PREDICATE_HPP
|
||||
|
||||
#include "common.hpp"
|
||||
#include <ndn-cxx/net/network-interface.hpp>
|
||||
|
||||
namespace nfd {
|
||||
|
||||
/**
|
||||
* \brief Represents a predicate to accept or reject a NetworkInterfaceInfo.
|
||||
*
|
||||
* The predicate consists of a whitelist and a blacklist. Whitelist and blacklist can contain,
|
||||
* in no particular order, interface names (e.g., ifname eth0), mac addresses
|
||||
* (e.g., ether 85:3b:4d:d3:5f:c2), subnets (e.g., subnet 192.0.2.0/24) or a wildcard (*) that matches
|
||||
* all interfaces. A NetworkInterfaceInfo is accepted if it matches any entry in the whitelist and none
|
||||
* of the entries in the blacklist.
|
||||
*/
|
||||
class NetworkInterfacePredicate
|
||||
{
|
||||
public:
|
||||
NetworkInterfacePredicate();
|
||||
|
||||
/**
|
||||
* \brief Set the whitelist to "*" and clear the blacklist
|
||||
*/
|
||||
void
|
||||
clear();
|
||||
|
||||
void
|
||||
parseWhitelist(const boost::property_tree::ptree& list);
|
||||
|
||||
void
|
||||
parseBlacklist(const boost::property_tree::ptree& list);
|
||||
|
||||
bool
|
||||
operator()(const ndn::net::NetworkInterface& netif) const;
|
||||
|
||||
bool
|
||||
operator==(const NetworkInterfacePredicate& other) const;
|
||||
|
||||
bool
|
||||
operator!=(const NetworkInterfacePredicate& other) const
|
||||
{
|
||||
return !this->operator==(other);
|
||||
}
|
||||
|
||||
private:
|
||||
std::set<std::string> m_whitelist;
|
||||
std::set<std::string> m_blacklist;
|
||||
};
|
||||
|
||||
} // namespace nfd
|
||||
|
||||
#endif // NFD_CORE_NETWORK_INTERFACE_PREDICATE_HPP
|
||||
@@ -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
|
||||
+99
-71
@@ -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.
|
||||
@@ -21,23 +21,30 @@
|
||||
*
|
||||
* 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>
|
||||
|
||||
namespace nfd {
|
||||
|
||||
void
|
||||
Network::print(std::ostream& os) const
|
||||
Network::Network() = default;
|
||||
|
||||
Network::Network(const boost::asio::ip::address& minAddress,
|
||||
const boost::asio::ip::address& maxAddress)
|
||||
: m_minAddress(minAddress)
|
||||
, m_maxAddress(maxAddress)
|
||||
{
|
||||
os << m_minAddress << " <-> " << m_maxAddress;
|
||||
}
|
||||
|
||||
const Network&
|
||||
Network::getMaxRangeV4()
|
||||
{
|
||||
using boost::asio::ip::address_v4;
|
||||
static Network range = Network(address_v4(0), address_v4(0xFFFFFFFF));
|
||||
static Network range{address_v4{}, address_v4{0xffffffff}};
|
||||
return range;
|
||||
}
|
||||
|
||||
@@ -47,86 +54,107 @@ Network::getMaxRangeV6()
|
||||
using boost::asio::ip::address_v6;
|
||||
static address_v6::bytes_type maxV6 = {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}};
|
||||
static Network range = Network(address_v6(), address_v6(maxV6));
|
||||
static Network range{address_v6{}, address_v6{maxV6}};
|
||||
return range;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
bool
|
||||
Network::isValidCidr(const std::string& cidr)
|
||||
{
|
||||
auto pos = cidr.find('/');
|
||||
if (pos == std::string::npos) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boost::system::error_code invalidIp;
|
||||
boost::asio::ip::address_v4::from_string(cidr.substr(0, pos), invalidIp);
|
||||
if (invalidIp) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto prefixLenStr = cidr.substr(pos + 1);
|
||||
if (!std::all_of(prefixLenStr.begin(), prefixLenStr.end(), ::isdigit)) {
|
||||
return false;
|
||||
}
|
||||
int prefixLen = -1;
|
||||
try {
|
||||
prefixLen = boost::lexical_cast<int>(prefixLenStr);
|
||||
}
|
||||
catch (const boost::bad_lexical_cast&) {
|
||||
return false;
|
||||
}
|
||||
if (prefixLen < 0 || prefixLen > 32) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os, const Network& network)
|
||||
{
|
||||
network.print(os);
|
||||
return os;
|
||||
return os << network.m_minAddress << " <-> " << network.m_maxAddress;
|
||||
}
|
||||
|
||||
std::istream&
|
||||
operator>>(std::istream& is, Network& network)
|
||||
{
|
||||
using namespace boost::asio;
|
||||
namespace ip = boost::asio::ip;
|
||||
|
||||
std::string networkStr;
|
||||
is >> networkStr;
|
||||
|
||||
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);
|
||||
if (position == std::string::npos) {
|
||||
network.m_minAddress = ndn::ip::addressFromString(networkStr);
|
||||
network.m_maxAddress = ndn::ip::addressFromString(networkStr);
|
||||
}
|
||||
else {
|
||||
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()) {
|
||||
ip::address_v4::bytes_type maskBytes = boost::initialized_value;
|
||||
for (size_t i = 0; i < mask; i++) {
|
||||
size_t byteId = i / 8;
|
||||
size_t bitIndex = 7 - i % 8;
|
||||
maskBytes[byteId] |= (1 << bitIndex);
|
||||
}
|
||||
|
||||
ip::address_v4::bytes_type addressBytes = address.to_v4().to_bytes();
|
||||
ip::address_v4::bytes_type min;
|
||||
ip::address_v4::bytes_type max;
|
||||
|
||||
for (size_t i = 0; i < addressBytes.size(); i++) {
|
||||
min[i] = addressBytes[i] & maskBytes[i];
|
||||
max[i] = addressBytes[i] | ~(maskBytes[i]);
|
||||
}
|
||||
|
||||
network.m_minAddress = ip::address_v4(min);
|
||||
network.m_maxAddress = ip::address_v4(max);
|
||||
}
|
||||
else
|
||||
{
|
||||
ip::address address = ip::address::from_string(networkStr.substr(0, position));
|
||||
size_t mask = boost::lexical_cast<size_t>(networkStr.substr(position+1));
|
||||
else {
|
||||
ip::address_v6::bytes_type maskBytes = boost::initialized_value;
|
||||
for (size_t i = 0; i < mask; i++) {
|
||||
size_t byteId = i / 8;
|
||||
size_t bitIndex = 7 - i % 8;
|
||||
maskBytes[byteId] |= (1 << bitIndex);
|
||||
}
|
||||
|
||||
if (address.is_v4())
|
||||
{
|
||||
ip::address_v4::bytes_type maskBytes = boost::initialized_value;
|
||||
for (size_t i = 0; i < mask; i++)
|
||||
{
|
||||
size_t byteId = i / 8;
|
||||
size_t bitIndex = 7 - i % 8;
|
||||
maskBytes[byteId] |= (1 << bitIndex);
|
||||
}
|
||||
ip::address_v6::bytes_type addressBytes = address.to_v6().to_bytes();
|
||||
ip::address_v6::bytes_type min;
|
||||
ip::address_v6::bytes_type max;
|
||||
|
||||
ip::address_v4::bytes_type addressBytes = address.to_v4().to_bytes();
|
||||
ip::address_v4::bytes_type min;
|
||||
ip::address_v4::bytes_type max;
|
||||
for (size_t i = 0; i < addressBytes.size(); i++) {
|
||||
min[i] = addressBytes[i] & maskBytes[i];
|
||||
max[i] = addressBytes[i] | ~(maskBytes[i]);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < addressBytes.size(); i++)
|
||||
{
|
||||
min[i] = addressBytes[i] & maskBytes[i];
|
||||
max[i] = addressBytes[i] | ~(maskBytes[i]);
|
||||
}
|
||||
|
||||
network.m_minAddress = ip::address_v4(min);
|
||||
network.m_maxAddress = ip::address_v4(max);
|
||||
}
|
||||
else
|
||||
{
|
||||
ip::address_v6::bytes_type maskBytes = boost::initialized_value;
|
||||
for (size_t i = 0; i < mask; i++)
|
||||
{
|
||||
size_t byteId = i / 8;
|
||||
size_t bitIndex = 7 - i % 8;
|
||||
maskBytes[byteId] |= (1 << bitIndex);
|
||||
}
|
||||
|
||||
ip::address_v6::bytes_type addressBytes = address.to_v6().to_bytes();
|
||||
ip::address_v6::bytes_type min;
|
||||
ip::address_v6::bytes_type max;
|
||||
|
||||
for (size_t i = 0; i < addressBytes.size(); i++)
|
||||
{
|
||||
min[i] = addressBytes[i] & maskBytes[i];
|
||||
max[i] = addressBytes[i] | ~(maskBytes[i]);
|
||||
}
|
||||
|
||||
network.m_minAddress = ip::address_v6(min);
|
||||
network.m_maxAddress = ip::address_v6(max);
|
||||
}
|
||||
network.m_minAddress = ip::address_v6(min);
|
||||
network.m_maxAddress = ip::address_v6(max);
|
||||
}
|
||||
}
|
||||
|
||||
return is;
|
||||
}
|
||||
|
||||
|
||||
+17
-25
@@ -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-2016, 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.
|
||||
@@ -26,33 +26,22 @@
|
||||
#ifndef NFD_CORE_NETWORK_HPP
|
||||
#define NFD_CORE_NETWORK_HPP
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/utility/value_init.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include "common.hpp"
|
||||
|
||||
namespace nfd {
|
||||
|
||||
class Network
|
||||
{
|
||||
public:
|
||||
Network()
|
||||
{
|
||||
}
|
||||
Network();
|
||||
|
||||
Network(const boost::asio::ip::address& minAddress,
|
||||
const boost::asio::ip::address& maxAddress)
|
||||
: m_minAddress(minAddress)
|
||||
, m_maxAddress(maxAddress)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
print(std::ostream& os) const;
|
||||
const boost::asio::ip::address& maxAddress);
|
||||
|
||||
bool
|
||||
doesContain(const boost::asio::ip::address& address) const
|
||||
{
|
||||
return (m_minAddress <= address && address <= m_maxAddress);
|
||||
return m_minAddress <= address && address <= m_maxAddress;
|
||||
}
|
||||
|
||||
static const Network&
|
||||
@@ -61,6 +50,9 @@ public:
|
||||
static const Network&
|
||||
getMaxRangeV6();
|
||||
|
||||
static bool
|
||||
isValidCidr(const std::string& cidr);
|
||||
|
||||
bool
|
||||
operator==(const Network& rhs) const
|
||||
{
|
||||
@@ -77,11 +69,11 @@ private:
|
||||
boost::asio::ip::address m_minAddress;
|
||||
boost::asio::ip::address m_maxAddress;
|
||||
|
||||
friend std::istream&
|
||||
operator>>(std::istream& is, Network& network);
|
||||
|
||||
friend std::ostream&
|
||||
operator<<(std::ostream& os, const Network& network);
|
||||
|
||||
friend std::istream&
|
||||
operator>>(std::istream& is, Network& network);
|
||||
};
|
||||
|
||||
std::ostream&
|
||||
|
||||
+46
-99
@@ -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-2018, Regents of the University of California,
|
||||
* Arizona Board of Regents,
|
||||
* Colorado State University,
|
||||
* University Pierre & Marie Curie, Sorbonne University,
|
||||
@@ -56,80 +56,63 @@ PrivilegeHelper::initialize(const std::string& userName, const std::string& grou
|
||||
|
||||
// workflow from man getpwnam_r
|
||||
|
||||
if (!groupName.empty())
|
||||
{
|
||||
static int groupSize = ::sysconf(_SC_GETGR_R_SIZE_MAX);
|
||||
if (!groupName.empty()) {
|
||||
static long groupSize = ::sysconf(_SC_GETGR_R_SIZE_MAX);
|
||||
|
||||
if (groupSize == -1)
|
||||
{
|
||||
groupSize = FALLBACK_GROUP_BUFFER_SIZE;
|
||||
}
|
||||
if (groupSize == -1)
|
||||
groupSize = FALLBACK_GROUP_BUFFER_SIZE;
|
||||
|
||||
std::vector<char> groupBuffer(groupSize);
|
||||
struct group group;
|
||||
struct group* groupResult = 0;
|
||||
std::vector<char> groupBuffer(groupSize);
|
||||
struct group group;
|
||||
struct group* groupResult = nullptr;
|
||||
|
||||
int errorCode = getgrnam_r(groupName.c_str(), &group,
|
||||
&groupBuffer[0], groupSize, &groupResult);
|
||||
int errorCode = getgrnam_r(groupName.data(), &group,
|
||||
&groupBuffer[0], groupBuffer.size(), &groupResult);
|
||||
|
||||
while (errorCode == ERANGE)
|
||||
{
|
||||
if (groupBuffer.size() * 2 > MAX_GROUP_BUFFER_SIZE)
|
||||
{
|
||||
throw PrivilegeHelper::Error("Cannot allocate large enough buffer for struct group");
|
||||
}
|
||||
while (errorCode == ERANGE) {
|
||||
if (groupBuffer.size() * 2 > MAX_GROUP_BUFFER_SIZE)
|
||||
throw Error("Cannot allocate large enough buffer for struct group");
|
||||
|
||||
groupBuffer.resize(groupBuffer.size() * 2);
|
||||
groupBuffer.resize(groupBuffer.size() * 2);
|
||||
|
||||
errorCode = getgrnam_r(groupName.c_str(), &group,
|
||||
&groupBuffer[0], groupBuffer.size(), &groupResult);
|
||||
}
|
||||
|
||||
if (errorCode != 0 || !groupResult)
|
||||
{
|
||||
throw PrivilegeHelper::Error("Failed to get gid for \"" + groupName + "\"");
|
||||
}
|
||||
|
||||
s_normalGid = group.gr_gid;
|
||||
errorCode = getgrnam_r(groupName.data(), &group,
|
||||
&groupBuffer[0], groupBuffer.size(), &groupResult);
|
||||
}
|
||||
|
||||
if (!userName.empty())
|
||||
{
|
||||
static int passwdSize = ::sysconf(_SC_GETPW_R_SIZE_MAX);
|
||||
if (errorCode != 0 || !groupResult)
|
||||
throw Error("Failed to get gid for \"" + groupName + "\"");
|
||||
|
||||
if (passwdSize == -1)
|
||||
{
|
||||
passwdSize = FALLBACK_PASSWD_BUFFER_SIZE;
|
||||
}
|
||||
s_normalGid = group.gr_gid;
|
||||
}
|
||||
|
||||
std::vector<char> passwdBuffer(passwdSize);
|
||||
struct passwd passwd;
|
||||
struct passwd* passwdResult = 0;
|
||||
if (!userName.empty()) {
|
||||
static long passwdSize = ::sysconf(_SC_GETPW_R_SIZE_MAX);
|
||||
|
||||
int errorCode =
|
||||
getpwnam_r(userName.c_str(), &passwd,
|
||||
&passwdBuffer[0], passwdBuffer.size(), &passwdResult);
|
||||
if (passwdSize == -1)
|
||||
passwdSize = FALLBACK_PASSWD_BUFFER_SIZE;
|
||||
|
||||
while (errorCode == ERANGE)
|
||||
{
|
||||
if (passwdBuffer.size() * 2 > MAX_PASSWD_BUFFER_SIZE)
|
||||
{
|
||||
throw PrivilegeHelper::Error("Cannot allocate large enough buffer for struct passwd");
|
||||
}
|
||||
std::vector<char> passwdBuffer(passwdSize);
|
||||
struct passwd passwd;
|
||||
struct passwd* passwdResult = nullptr;
|
||||
|
||||
passwdBuffer.resize(passwdBuffer.size() * 2);
|
||||
int errorCode = getpwnam_r(userName.data(), &passwd,
|
||||
&passwdBuffer[0], passwdBuffer.size(), &passwdResult);
|
||||
|
||||
errorCode = getpwnam_r(userName.c_str(), &passwd,
|
||||
&passwdBuffer[0], passwdBuffer.size(), &passwdResult);
|
||||
}
|
||||
while (errorCode == ERANGE) {
|
||||
if (passwdBuffer.size() * 2 > MAX_PASSWD_BUFFER_SIZE)
|
||||
throw Error("Cannot allocate large enough buffer for struct passwd");
|
||||
|
||||
if (errorCode != 0 || !passwdResult)
|
||||
{
|
||||
throw PrivilegeHelper::Error("Failed to get uid for \"" + userName + "\"");
|
||||
}
|
||||
passwdBuffer.resize(passwdBuffer.size() * 2);
|
||||
|
||||
s_normalUid = passwd.pw_uid;
|
||||
errorCode = getpwnam_r(userName.data(), &passwd,
|
||||
&passwdBuffer[0], passwdBuffer.size(), &passwdResult);
|
||||
}
|
||||
|
||||
if (errorCode != 0 || !passwdResult)
|
||||
throw Error("Failed to get uid for \"" + userName + "\"");
|
||||
|
||||
s_normalUid = passwd.pw_uid;
|
||||
}
|
||||
#else
|
||||
if (!userName.empty() || !groupName.empty()) {
|
||||
throw Error("Dropping and raising privileges is not supported on this platform");
|
||||
@@ -143,21 +126,11 @@ PrivilegeHelper::drop()
|
||||
#ifdef HAVE_PRIVILEGE_DROP_AND_ELEVATE
|
||||
NFD_LOG_TRACE("dropping to effective gid=" << s_normalGid);
|
||||
if (::setegid(s_normalGid) != 0)
|
||||
{
|
||||
std::stringstream error;
|
||||
error << "Failed to drop to effective gid=" << s_normalGid;
|
||||
|
||||
throw PrivilegeHelper::Error(error.str());
|
||||
}
|
||||
throw Error("Failed to drop to effective gid=" + to_string(s_normalGid));
|
||||
|
||||
NFD_LOG_TRACE("dropping to effective uid=" << s_normalUid);
|
||||
if (::seteuid(s_normalUid) != 0)
|
||||
{
|
||||
std::stringstream error;
|
||||
error << "Failed to drop to effective uid=" << s_normalUid;
|
||||
|
||||
throw PrivilegeHelper::Error(error.str());
|
||||
}
|
||||
throw Error("Failed to drop to effective uid=" + to_string(s_normalUid));
|
||||
|
||||
NFD_LOG_INFO("dropped to effective uid=" << ::geteuid() << " gid=" << ::getegid());
|
||||
#else
|
||||
@@ -171,42 +144,16 @@ PrivilegeHelper::raise()
|
||||
#ifdef HAVE_PRIVILEGE_DROP_AND_ELEVATE
|
||||
NFD_LOG_TRACE("elevating to effective uid=" << s_privilegedUid);
|
||||
if (::seteuid(s_privilegedUid) != 0)
|
||||
{
|
||||
std::stringstream error;
|
||||
error << "Failed to elevate to effective uid=" << s_privilegedUid;
|
||||
|
||||
throw PrivilegeHelper::Error(error.str());
|
||||
}
|
||||
throw Error("Failed to elevate to effective uid=" + to_string(s_privilegedUid));
|
||||
|
||||
NFD_LOG_TRACE("elevating to effective gid=" << s_privilegedGid);
|
||||
if (::setegid(s_privilegedGid) != 0)
|
||||
{
|
||||
std::stringstream error;
|
||||
error << "Failed to elevate to effective gid=" << s_privilegedGid;
|
||||
throw Error("Failed to elevate to effective gid=" + to_string(s_privilegedGid));
|
||||
|
||||
throw PrivilegeHelper::Error(error.str());
|
||||
}
|
||||
NFD_LOG_INFO("elevated to effective uid=" << ::geteuid() << " gid=" << ::getegid());
|
||||
#else
|
||||
NFD_LOG_WARN("Elevating privileges is not supported on this platform");
|
||||
#endif // HAVE_PRIVILEGE_DROP_AND_ELEVATE
|
||||
}
|
||||
|
||||
void
|
||||
PrivilegeHelper::runElevated(function<void()> f)
|
||||
{
|
||||
raise();
|
||||
|
||||
try
|
||||
{
|
||||
f();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
drop();
|
||||
throw;
|
||||
}
|
||||
drop();
|
||||
}
|
||||
|
||||
} // 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-2018, Regents of the University of California,
|
||||
* Arizona Board of Regents,
|
||||
* Colorado State University,
|
||||
* University Pierre & Marie Curie, Sorbonne University,
|
||||
@@ -35,7 +35,6 @@ namespace nfd {
|
||||
class PrivilegeHelper
|
||||
{
|
||||
public:
|
||||
|
||||
/** \brief represents a serious seteuid/gid failure
|
||||
*
|
||||
* This should only be caught by main as part of a graceful program termination.
|
||||
@@ -53,7 +52,7 @@ public:
|
||||
const char*
|
||||
what() const
|
||||
{
|
||||
return m_whatMessage.c_str();
|
||||
return m_whatMessage.data();
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -66,11 +65,22 @@ public:
|
||||
static void
|
||||
drop();
|
||||
|
||||
template<class F>
|
||||
static void
|
||||
runElevated(function<void()> f);
|
||||
runElevated(F&& f)
|
||||
{
|
||||
raise();
|
||||
try {
|
||||
f();
|
||||
}
|
||||
catch (...) {
|
||||
drop();
|
||||
throw;
|
||||
}
|
||||
drop();
|
||||
}
|
||||
|
||||
PUBLIC_WITH_TESTS_ELSE_PRIVATE:
|
||||
|
||||
static void
|
||||
raise();
|
||||
|
||||
|
||||
+7
-13
@@ -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-2016, Regents of the University of California,
|
||||
* Arizona Board of Regents,
|
||||
* Colorado State University,
|
||||
* University Pierre & Marie Curie, Sorbonne University,
|
||||
@@ -28,21 +28,15 @@
|
||||
|
||||
namespace nfd {
|
||||
|
||||
static boost::thread_specific_ptr<boost::random::mt19937> g_rng;
|
||||
|
||||
boost::random::mt19937&
|
||||
std::mt19937&
|
||||
getGlobalRng()
|
||||
{
|
||||
if (g_rng.get() == nullptr) {
|
||||
g_rng.reset(new boost::random::mt19937());
|
||||
static boost::thread_specific_ptr<std::mt19937> rng;
|
||||
if (rng.get() == nullptr) {
|
||||
std::random_device rd;
|
||||
rng.reset(new std::mt19937(rd()));
|
||||
}
|
||||
return *g_rng;
|
||||
}
|
||||
|
||||
void
|
||||
resetGlobalRng()
|
||||
{
|
||||
g_rng.reset();
|
||||
return *rng;
|
||||
}
|
||||
|
||||
} // namespace nfd
|
||||
+18
-15
@@ -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.
|
||||
@@ -24,24 +24,27 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* This file declares the global random number generator
|
||||
* All random numbers in NFD should use this global random number generator,
|
||||
* so that the generator can be properly seeded when necessary.
|
||||
* \file
|
||||
* \brief Declares the global pseudorandom number generator (PRNG) for NFD
|
||||
*
|
||||
* This random number generator is not suitable for security purposes;
|
||||
* security-critical code should use CryptoPP random number generator instead.
|
||||
* All random numbers generated by NFD should use this global generator,
|
||||
* so that it can be properly seeded when necessary.
|
||||
*
|
||||
* \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
|
||||
#define NFD_CORE_RANDOM_HPP
|
||||
|
||||
#include <boost/random/mersenne_twister.hpp>
|
||||
#include <random>
|
||||
|
||||
namespace nfd {
|
||||
|
||||
/** \return the global random number generator instance
|
||||
*/
|
||||
boost::random::mt19937&
|
||||
std::mt19937&
|
||||
getGlobalRng();
|
||||
|
||||
} // namespace nfd
|
||||
|
||||
@@ -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,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.
|
||||
@@ -21,12 +21,12 @@
|
||||
*
|
||||
* 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_RTT_ESTIMATOR_HPP
|
||||
#define NFD_DAEMON_FW_RTT_ESTIMATOR_HPP
|
||||
#ifndef NFD_CORE_RTT_ESTIMATOR_HPP
|
||||
#define NFD_CORE_RTT_ESTIMATOR_HPP
|
||||
|
||||
#include "common.hpp"
|
||||
#include "core/common.hpp"
|
||||
|
||||
namespace nfd {
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -1,132 +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/>.
|
||||
*/
|
||||
|
||||
#ifndef NFD_CORE_SEGMENT_PUBLISHER_HPP
|
||||
#define NFD_CORE_SEGMENT_PUBLISHER_HPP
|
||||
|
||||
#include "common.hpp"
|
||||
|
||||
#include <ndn-cxx/encoding/encoding-buffer.hpp>
|
||||
#include <ndn-cxx/security/key-chain.hpp>
|
||||
|
||||
namespace nfd {
|
||||
|
||||
/** \brief provides a publisher of Status Dataset or other segmented octet stream
|
||||
* \sa http://redmine.named-data.net/projects/nfd/wiki/StatusDataset
|
||||
*/
|
||||
template <class FaceBase>
|
||||
class SegmentPublisher : noncopyable
|
||||
{
|
||||
public:
|
||||
SegmentPublisher(FaceBase& face,
|
||||
const Name& prefix,
|
||||
ndn::KeyChain& keyChain,
|
||||
const time::milliseconds& freshnessPeriod = getDefaultFreshness())
|
||||
: m_face(face)
|
||||
, m_prefix(prefix)
|
||||
, m_keyChain(keyChain)
|
||||
, m_freshnessPeriod(freshnessPeriod)
|
||||
{
|
||||
}
|
||||
|
||||
virtual
|
||||
~SegmentPublisher()
|
||||
{
|
||||
}
|
||||
|
||||
static size_t
|
||||
getMaxSegmentSize()
|
||||
{
|
||||
static const size_t MAX_SEGMENT_SIZE = ndn::MAX_NDN_PACKET_SIZE >> 1;
|
||||
return MAX_SEGMENT_SIZE;
|
||||
}
|
||||
|
||||
static constexpr time::milliseconds
|
||||
getDefaultFreshness()
|
||||
{
|
||||
return time::milliseconds(1000);
|
||||
}
|
||||
|
||||
void
|
||||
publish()
|
||||
{
|
||||
ndn::EncodingBuffer buffer;
|
||||
generate(buffer);
|
||||
|
||||
const uint8_t* rawBuffer = buffer.buf();
|
||||
const uint8_t* segmentBegin = rawBuffer;
|
||||
const uint8_t* end = rawBuffer + buffer.size();
|
||||
|
||||
Name segmentPrefix(m_prefix);
|
||||
segmentPrefix.appendVersion();
|
||||
|
||||
uint64_t segmentNo = 0;
|
||||
do {
|
||||
const uint8_t* segmentEnd = segmentBegin + getMaxSegmentSize();
|
||||
if (segmentEnd > end) {
|
||||
segmentEnd = end;
|
||||
}
|
||||
|
||||
Name segmentName(segmentPrefix);
|
||||
segmentName.appendSegment(segmentNo);
|
||||
|
||||
shared_ptr<Data> data = make_shared<Data>(segmentName);
|
||||
data->setContent(segmentBegin, segmentEnd - segmentBegin);
|
||||
data->setFreshnessPeriod(m_freshnessPeriod);
|
||||
|
||||
segmentBegin = segmentEnd;
|
||||
if (segmentBegin >= end) {
|
||||
data->setFinalBlockId(segmentName[-1]);
|
||||
}
|
||||
|
||||
publishSegment(data);
|
||||
++segmentNo;
|
||||
} while (segmentBegin < end);
|
||||
}
|
||||
|
||||
protected:
|
||||
/** \brief In a derived class, write the octets into outBuffer.
|
||||
*/
|
||||
virtual size_t
|
||||
generate(ndn::EncodingBuffer& outBuffer) = 0;
|
||||
|
||||
private:
|
||||
void
|
||||
publishSegment(shared_ptr<Data>& data)
|
||||
{
|
||||
m_keyChain.sign(*data);
|
||||
m_face.put(*data);
|
||||
}
|
||||
|
||||
private:
|
||||
FaceBase& m_face;
|
||||
const Name m_prefix;
|
||||
ndn::KeyChain& m_keyChain;
|
||||
const time::milliseconds m_freshnessPeriod;
|
||||
};
|
||||
|
||||
} // namespace nfd
|
||||
|
||||
#endif // NFD_CORE_SEGMENT_PUBLISHER_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-2016, 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.
|
||||
@@ -23,8 +23,8 @@
|
||||
* NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef NFD_VERSION_HPP
|
||||
#define NFD_VERSION_HPP
|
||||
#ifndef NFD_CORE_VERSION_HPP
|
||||
#define NFD_CORE_VERSION_HPP
|
||||
|
||||
namespace nfd {
|
||||
|
||||
@@ -71,4 +71,4 @@ namespace nfd {
|
||||
|
||||
} // namespace nfd
|
||||
|
||||
#endif // NFD_VERSION_HPP
|
||||
#endif // NFD_CORE_VERSION_HPP
|
||||
@@ -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
|
||||
+56
-17
@@ -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-2018, Regents of the University of California,
|
||||
* Arizona Board of Regents,
|
||||
* Colorado State University,
|
||||
* University Pierre & Marie Curie, Sorbonne University,
|
||||
@@ -26,24 +26,29 @@
|
||||
#ifndef NFD_DAEMON_FACE_CHANNEL_HPP
|
||||
#define NFD_DAEMON_FACE_CHANNEL_HPP
|
||||
|
||||
#include "channel-log.hpp"
|
||||
#include "face.hpp"
|
||||
|
||||
#include <boost/logic/tribool.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)
|
||||
/** \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;
|
||||
using FaceCreatedCallback = function<void(const shared_ptr<Face>& face)>;
|
||||
|
||||
/**
|
||||
* \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(const std::string& reason)> FaceCreationFailedCallback;
|
||||
|
||||
using FaceCreationFailedCallback = function<void(uint32_t status, const std::string& reason)>;
|
||||
|
||||
/** \brief represent a channel that communicates on a local endpoint
|
||||
* \sa FaceSystem
|
||||
*
|
||||
* A channel can listen on a local endpoint and initiate outgoing connection from a local endpoint.
|
||||
* A channel creates Face objects and retains shared ownership of them.
|
||||
*/
|
||||
class Channel : noncopyable
|
||||
{
|
||||
public:
|
||||
@@ -51,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
|
||||
@@ -61,11 +79,31 @@ private:
|
||||
FaceUri m_uri;
|
||||
};
|
||||
|
||||
inline const FaceUri&
|
||||
Channel::getUri() const
|
||||
/** \brief Parameters used to set Transport properties or LinkService options on a newly created 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.
|
||||
*/
|
||||
class FaceParams
|
||||
{
|
||||
return m_uri;
|
||||
}
|
||||
public:
|
||||
// get rid of this constructor and use aggregate init + NSDMIs when we switch to C++14
|
||||
FaceParams() noexcept
|
||||
: persistency(ndn::nfd::FACE_PERSISTENCY_PERSISTENT)
|
||||
, wantLocalFields(false)
|
||||
, wantLpReliability(false)
|
||||
, wantCongestionMarking(boost::logic::indeterminate)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
ndn::nfd::FacePersistency persistency;
|
||||
ndn::optional<time::nanoseconds> baseCongestionMarkingInterval;
|
||||
ndn::optional<uint64_t> defaultCongestionThreshold;
|
||||
bool wantLocalFields;
|
||||
bool wantLpReliability;
|
||||
boost::logic::tribool wantCongestionMarking;
|
||||
};
|
||||
|
||||
/** \brief invokes a callback when the face is closed
|
||||
* \param face the face
|
||||
@@ -77,6 +115,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-2015, Regents of the University of California,
|
||||
/*
|
||||
* Copyright (c) 2014-2018, 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_FACE_DATAGRAM_TRANSPORT_HPP
|
||||
|
||||
#include "transport.hpp"
|
||||
#include "socket-utils.hpp"
|
||||
#include "core/global-io.hpp"
|
||||
|
||||
#include <array>
|
||||
@@ -54,6 +55,9 @@ public:
|
||||
explicit
|
||||
DatagramTransport(typename protocol::socket&& socket);
|
||||
|
||||
ssize_t
|
||||
getSendQueueLength() override;
|
||||
|
||||
/** \brief Receive datagram, translate buffer into packet, deliver to parent class.
|
||||
*/
|
||||
void
|
||||
@@ -61,11 +65,11 @@ public:
|
||||
const boost::system::error_code& error);
|
||||
|
||||
protected:
|
||||
virtual void
|
||||
doClose() DECL_OVERRIDE;
|
||||
void
|
||||
doClose() override;
|
||||
|
||||
virtual void
|
||||
doSend(Transport::Packet&& packet) DECL_OVERRIDE;
|
||||
void
|
||||
doSend(Transport::Packet&& packet) override;
|
||||
|
||||
void
|
||||
handleSend(const boost::system::error_code& error,
|
||||
@@ -79,10 +83,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,21 +99,43 @@ 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)
|
||||
{
|
||||
boost::asio::socket_base::send_buffer_size sendBufferSizeOption;
|
||||
boost::system::error_code error;
|
||||
m_socket.get_option(sendBufferSizeOption, error);
|
||||
if (error) {
|
||||
NFD_LOG_FACE_WARN("Failed to obtain send queue capacity from socket: " << error.message());
|
||||
this->setSendQueueCapacity(QUEUE_ERROR);
|
||||
}
|
||||
else {
|
||||
this->setSendQueueCapacity(sendBufferSizeOption.value());
|
||||
}
|
||||
|
||||
m_socket.async_receive_from(boost::asio::buffer(m_receiveBuffer), m_sender,
|
||||
bind(&DatagramTransport<T, U>::handleReceive, this,
|
||||
boost::asio::placeholders::error,
|
||||
boost::asio::placeholders::bytes_transferred));
|
||||
}
|
||||
|
||||
template<class T, class U>
|
||||
ssize_t
|
||||
DatagramTransport<T, U>::getSendQueueLength()
|
||||
{
|
||||
ssize_t queueLength = getTxQueueLength(m_socket.native_handle());
|
||||
if (queueLength == QUEUE_ERROR) {
|
||||
NFD_LOG_FACE_WARN("Failed to obtain send queue length from socket: " << std::strerror(errno));
|
||||
}
|
||||
return queueLength;
|
||||
}
|
||||
|
||||
template<class T, class U>
|
||||
void
|
||||
DatagramTransport<T, U>::doClose()
|
||||
@@ -152,13 +178,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 +193,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 +235,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-2018, 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,
|
||||
const FaceParams& params,
|
||||
const FaceCreatedCallback& onFaceCreated,
|
||||
const FaceCreationFailedCallback& onConnectFailed)
|
||||
{
|
||||
shared_ptr<Face> face;
|
||||
try {
|
||||
face = createFace(remoteEndpoint, params).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 {
|
||||
FaceParams params;
|
||||
params.persistency = ndn::nfd::FACE_PERSISTENCY_ON_DEMAND;
|
||||
std::tie(isCreated, face) = createFace(sender, params);
|
||||
}
|
||||
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,
|
||||
const FaceParams& params)
|
||||
{
|
||||
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 = params.wantLpReliability;
|
||||
|
||||
auto linkService = make_unique<GenericLinkService>(options);
|
||||
auto transport = make_unique<UnicastEthernetTransport>(*m_localEndpoint, remoteEndpoint,
|
||||
params.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,142 @@
|
||||
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
|
||||
/*
|
||||
* Copyright (c) 2014-2018, 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
|
||||
*/
|
||||
void
|
||||
connect(const ethernet::Address& remoteEndpoint,
|
||||
const FaceParams& params,
|
||||
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,
|
||||
const FaceParams& params);
|
||||
|
||||
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,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-2018, Regents of the University of California,
|
||||
* Arizona Board of Regents,
|
||||
* Colorado State University,
|
||||
* University Pierre & Marie Curie, Sorbonne University,
|
||||
@@ -24,61 +24,353 @@
|
||||
*/
|
||||
|
||||
#include "ethernet-factory.hpp"
|
||||
#include "ethernet-transport.hpp"
|
||||
#include "generic-link-service.hpp"
|
||||
#include "multicast-ethernet-transport.hpp"
|
||||
|
||||
#include <boost/range/adaptors.hpp>
|
||||
#include <boost/range/algorithm/copy.hpp>
|
||||
|
||||
namespace nfd {
|
||||
namespace face {
|
||||
|
||||
shared_ptr<Face>
|
||||
EthernetFactory::createMulticastFace(const NetworkInterfaceInfo& interface,
|
||||
const ethernet::Address& address)
|
||||
NFD_LOG_INIT("EthernetFactory");
|
||||
NFD_REGISTER_PROTOCOL_FACTORY(EthernetFactory);
|
||||
|
||||
const std::string&
|
||||
EthernetFactory::getId()
|
||||
{
|
||||
if (!address.isMulticast())
|
||||
BOOST_THROW_EXCEPTION(Error(address.toString() + " is not a multicast address"));
|
||||
static std::string id("ether");
|
||||
return id;
|
||||
}
|
||||
|
||||
auto face = findMulticastFace(interface.name, address);
|
||||
if (face)
|
||||
return face;
|
||||
|
||||
face::GenericLinkService::Options opts;
|
||||
opts.allowFragmentation = true;
|
||||
opts.allowReassembly = true;
|
||||
|
||||
auto linkService = make_unique<face::GenericLinkService>(opts);
|
||||
auto transport = make_unique<face::EthernetTransport>(interface, address);
|
||||
face = make_shared<Face>(std::move(linkService), std::move(transport));
|
||||
|
||||
auto key = std::make_pair(interface.name, address);
|
||||
m_multicastFaces[key] = face;
|
||||
connectFaceClosedSignal(*face, [this, key] { m_multicastFaces.erase(key); });
|
||||
|
||||
return face;
|
||||
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::createFace(const FaceUri& uri,
|
||||
ndn::nfd::FacePersistency persistency,
|
||||
const FaceCreatedCallback& onCreated,
|
||||
const FaceCreationFailedCallback& onConnectFailed)
|
||||
EthernetFactory::processConfig(OptionalConfigSection configSection,
|
||||
FaceSystem::ConfigContext& context)
|
||||
{
|
||||
BOOST_THROW_EXCEPTION(Error("EthernetFactory does not support 'createFace' operation"));
|
||||
// ether
|
||||
// {
|
||||
// listen yes
|
||||
// idle_timeout 600
|
||||
// mcast yes
|
||||
// mcast_group 01:00:5E:00:17:AA
|
||||
// mcast_ad_hoc no
|
||||
// whitelist
|
||||
// {
|
||||
// *
|
||||
// }
|
||||
// blacklist
|
||||
// {
|
||||
// }
|
||||
// }
|
||||
|
||||
UnicastConfig unicastConfig;
|
||||
MulticastConfig mcastConfig;
|
||||
|
||||
if (configSection) {
|
||||
// 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 == "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>();
|
||||
mcastConfig.group = ethernet::Address::fromString(valueStr);
|
||||
if (mcastConfig.group.isNull()) {
|
||||
BOOST_THROW_EXCEPTION(ConfigFile::Error("face_system.ether.mcast_group: '" +
|
||||
valueStr + "' cannot be parsed as an Ethernet address"));
|
||||
}
|
||||
else if (!mcastConfig.group.isMulticast()) {
|
||||
BOOST_THROW_EXCEPTION(ConfigFile::Error("face_system.ether.mcast_group: '" +
|
||||
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);
|
||||
}
|
||||
else if (key == "blacklist") {
|
||||
mcastConfig.netifPredicate.parseBlacklist(value);
|
||||
}
|
||||
else {
|
||||
BOOST_THROW_EXCEPTION(ConfigFile::Error("Unrecognized option face_system.ether." + key));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
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);
|
||||
}
|
||||
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_unicastConfig = unicastConfig;
|
||||
m_mcastConfig = mcastConfig;
|
||||
this->applyConfig(context);
|
||||
}
|
||||
|
||||
void
|
||||
EthernetFactory::createFace(const CreateFaceRequest& req,
|
||||
const FaceCreatedCallback& onCreated,
|
||||
const FaceCreationFailedCallback& onFailure)
|
||||
{
|
||||
BOOST_ASSERT(req.remoteUri.isCanonical());
|
||||
|
||||
if (!req.localUri || req.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(req.localUri->isCanonical());
|
||||
|
||||
if (req.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(req.remoteUri.getHost()));
|
||||
std::string localEndpoint(req.localUri->getHost());
|
||||
|
||||
if (remoteEndpoint.isMulticast()) {
|
||||
NFD_LOG_TRACE("createFace does not support multicast faces");
|
||||
onFailure(406, "Cannot create multicast Ethernet faces");
|
||||
return;
|
||||
}
|
||||
|
||||
if (req.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, req.params, 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::findMulticastFace(const std::string& interfaceName,
|
||||
const ethernet::Address& address) const
|
||||
EthernetFactory::createMulticastFace(const ndn::net::NetworkInterface& netif,
|
||||
const ethernet::Address& address)
|
||||
{
|
||||
auto i = m_multicastFaces.find({interfaceName, address});
|
||||
if (i != m_multicastFaces.end())
|
||||
return i->second;
|
||||
else
|
||||
return nullptr;
|
||||
BOOST_ASSERT(address.isMulticast());
|
||||
|
||||
auto key = std::make_pair(netif.getName(), address);
|
||||
auto found = m_mcastFaces.find(key);
|
||||
if (found != m_mcastFaces.end()) {
|
||||
return found->second;
|
||||
}
|
||||
|
||||
GenericLinkService::Options opts;
|
||||
opts.allowFragmentation = true;
|
||||
opts.allowReassembly = true;
|
||||
|
||||
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;
|
||||
connectFaceClosedSignal(*face, [this, key] { m_mcastFaces.erase(key); });
|
||||
|
||||
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)
|
||||
{
|
||||
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()));
|
||||
|
||||
// create channels and multicast faces if requested by config
|
||||
for (const auto& netif : netmon->listNetworkInterfaces()) {
|
||||
this->applyUnicastConfigToNetif(netif);
|
||||
|
||||
auto face = this->applyMcastConfigToNetif(*netif);
|
||||
if (face != nullptr) {
|
||||
// don't destroy face
|
||||
oldFaces.erase(face);
|
||||
}
|
||||
}
|
||||
|
||||
// destroy old multicast faces that are not needed in new configuration
|
||||
for (const auto& face : oldFaces) {
|
||||
face->close();
|
||||
}
|
||||
}
|
||||
|
||||
} // 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-2018, Regents of the University of California,
|
||||
* Arizona Board of Regents,
|
||||
* Colorado State University,
|
||||
* University Pierre & Marie Curie, Sorbonne University,
|
||||
@@ -27,85 +27,108 @@
|
||||
#define NFD_DAEMON_FACE_ETHERNET_FACTORY_HPP
|
||||
|
||||
#include "protocol-factory.hpp"
|
||||
#include "core/network-interface.hpp"
|
||||
#include "ethernet-channel.hpp"
|
||||
|
||||
namespace nfd {
|
||||
namespace face {
|
||||
|
||||
/** \brief protocol factory for Ethernet
|
||||
*/
|
||||
class EthernetFactory : public ProtocolFactory
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* \brief Exception of EthernetFactory
|
||||
static const std::string&
|
||||
getId();
|
||||
|
||||
explicit
|
||||
EthernetFactory(const CtorParams& params);
|
||||
|
||||
/** \brief process face_system.ether config section
|
||||
*/
|
||||
class Error : public ProtocolFactory::Error
|
||||
{
|
||||
public:
|
||||
explicit
|
||||
Error(const std::string& what)
|
||||
: ProtocolFactory::Error(what)
|
||||
{
|
||||
}
|
||||
};
|
||||
void
|
||||
processConfig(OptionalConfigSection configSection,
|
||||
FaceSystem::ConfigContext& context) override;
|
||||
|
||||
typedef std::map<std::pair<std::string, ethernet::Address>,
|
||||
shared_ptr<Face>> MulticastFaceMap;
|
||||
|
||||
/**
|
||||
* \brief Create an EthernetFace to communicate with the given multicast group
|
||||
*
|
||||
* If this method is called twice with the same interface and group, only
|
||||
* one face will be created. Instead, the second call will just retrieve
|
||||
* the existing face.
|
||||
*
|
||||
* \param interface Local network interface
|
||||
* \param address Ethernet broadcast/multicast destination address
|
||||
*
|
||||
* \returns always a valid shared pointer to an EthernetFace object,
|
||||
* an exception will be thrown if the creation fails
|
||||
*
|
||||
* \throws EthernetFactory::Error or EthernetTransport::Error
|
||||
*/
|
||||
shared_ptr<Face>
|
||||
createMulticastFace(const NetworkInterfaceInfo& interface,
|
||||
const ethernet::Address& address);
|
||||
|
||||
/**
|
||||
* \brief Get map of configured multicast faces
|
||||
*/
|
||||
const MulticastFaceMap&
|
||||
getMulticastFaces() const;
|
||||
|
||||
public: // from ProtocolFactory
|
||||
virtual void
|
||||
createFace(const FaceUri& uri,
|
||||
ndn::nfd::FacePersistency persistency,
|
||||
void
|
||||
createFace(const CreateFaceRequest& req,
|
||||
const FaceCreatedCallback& onCreated,
|
||||
const FaceCreationFailedCallback& onConnectFailed) DECL_OVERRIDE;
|
||||
const FaceCreationFailedCallback& onFailure) override;
|
||||
|
||||
virtual std::vector<shared_ptr<const Channel>>
|
||||
getChannels() const DECL_OVERRIDE;
|
||||
|
||||
private:
|
||||
/**
|
||||
* \brief Look up EthernetFace using specified interface and address
|
||||
* \brief Create Ethernet-based channel on the specified network interface
|
||||
*
|
||||
* \returns shared pointer to the existing EthernetFace object
|
||||
* or nullptr when such face does not exist
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* \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>
|
||||
findMulticastFace(const std::string& interfaceName,
|
||||
const ethernet::Address& address) const;
|
||||
createMulticastFace(const ndn::net::NetworkInterface& localEndpoint,
|
||||
const ethernet::Address& group);
|
||||
|
||||
private:
|
||||
MulticastFaceMap m_multicastFaces;
|
||||
/** \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 = 10_min;
|
||||
};
|
||||
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;
|
||||
};
|
||||
|
||||
inline const EthernetFactory::MulticastFaceMap&
|
||||
EthernetFactory::getMulticastFaces() const
|
||||
{
|
||||
return m_multicastFaces;
|
||||
}
|
||||
|
||||
} // namespace face
|
||||
} // namespace nfd
|
||||
|
||||
#endif // NFD_DAEMON_FACE_ETHERNET_FACTORY_HPP
|
||||
@@ -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
|
||||
@@ -0,0 +1,45 @@
|
||||
/* -*- 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_PROTOCOL_HPP
|
||||
#define NFD_DAEMON_FACE_ETHERNET_PROTOCOL_HPP
|
||||
|
||||
#include "core/common.hpp"
|
||||
#include <ndn-cxx/net/ethernet.hpp>
|
||||
#include <net/ethernet.h>
|
||||
|
||||
namespace nfd {
|
||||
namespace ethernet {
|
||||
|
||||
using namespace ndn::ethernet;
|
||||
|
||||
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_DAEMON_FACE_ETHERNET_PROTOCOL_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,
|
||||
@@ -24,118 +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::beforeChangePersistency(ndn::nfd::FacePersistency newPersistency)
|
||||
{
|
||||
if (newPersistency != ndn::nfd::FACE_PERSISTENCY_PERMANENT) {
|
||||
BOOST_THROW_EXCEPTION(
|
||||
std::invalid_argument("EthernetTransport supports only FACE_PERSISTENCY_PERMANENT"));
|
||||
}
|
||||
}
|
||||
|
||||
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__);
|
||||
|
||||
@@ -146,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
|
||||
@@ -156,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
|
||||
@@ -280,138 +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)
|
||||
{
|
||||
NFD_LOG_FACE_TRACE(__func__);
|
||||
|
||||
if (getState() == TransportState::CLOSING ||
|
||||
getState() == TransportState::FAILED ||
|
||||
getState() == TransportState::CLOSED ||
|
||||
error == boost::asio::error::operation_aborted)
|
||||
// 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,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,26 +26,18 @@
|
||||
#ifndef NFD_DAEMON_FACE_ETHERNET_TRANSPORT_HPP
|
||||
#define NFD_DAEMON_FACE_ETHERNET_TRANSPORT_HPP
|
||||
|
||||
#include "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 DECL_CLASS_FINAL : public Transport
|
||||
class EthernetTransport : public Transport
|
||||
{
|
||||
public:
|
||||
class Error : public std::runtime_error
|
||||
@@ -59,43 +51,37 @@ 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:
|
||||
virtual void
|
||||
beforeChangePersistency(ndn::nfd::FacePersistency newPersistency) DECL_FINAL;
|
||||
EthernetTransport(const ndn::net::NetworkInterface& localEndpoint,
|
||||
const ethernet::Address& remoteEndpoint);
|
||||
|
||||
virtual void
|
||||
doClose() DECL_FINAL;
|
||||
void
|
||||
doClose() final;
|
||||
|
||||
bool
|
||||
hasRecentlyReceived() const
|
||||
{
|
||||
return m_hasRecentlyReceived;
|
||||
}
|
||||
|
||||
void
|
||||
resetRecentlyReceived()
|
||||
{
|
||||
m_hasRecentlyReceived = false;
|
||||
}
|
||||
|
||||
private:
|
||||
virtual void
|
||||
doSend(Transport::Packet&& packet) DECL_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();
|
||||
doSend(Transport::Packet&& packet) final;
|
||||
|
||||
/**
|
||||
* @brief Sends the specified TLV block on the network wrapped in an Ethernet frame
|
||||
@@ -103,49 +89,27 @@ private:
|
||||
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,
|
||||
@@ -32,6 +32,7 @@ FaceCounters::FaceCounters(const LinkService::Counters& linkServiceCounters,
|
||||
const Transport::Counters& transportCounters)
|
||||
: nInInterests(linkServiceCounters.nInInterests)
|
||||
, nOutInterests(linkServiceCounters.nOutInterests)
|
||||
, nDroppedInterests(linkServiceCounters.nDroppedInterests)
|
||||
, nInData(linkServiceCounters.nInData)
|
||||
, nOutData(linkServiceCounters.nOutData)
|
||||
, nInNacks(linkServiceCounters.nInNacks)
|
||||
|
||||
@@ -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,
|
||||
@@ -70,6 +70,7 @@ public:
|
||||
public:
|
||||
const PacketCounter& nInInterests;
|
||||
const PacketCounter& nOutInterests;
|
||||
const PacketCounter& nDroppedInterests;
|
||||
const PacketCounter& nInData;
|
||||
const PacketCounter& nOutData;
|
||||
const PacketCounter& nInNacks;
|
||||
|
||||
@@ -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
|
||||
@@ -0,0 +1,151 @@
|
||||
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
|
||||
/*
|
||||
* Copyright (c) 2014-2018, 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 "face-system.hpp"
|
||||
#include "protocol-factory.hpp"
|
||||
#include "core/global-io.hpp"
|
||||
#include "fw/face-table.hpp"
|
||||
|
||||
namespace nfd {
|
||||
namespace face {
|
||||
|
||||
NFD_LOG_INIT("FaceSystem");
|
||||
|
||||
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, pfCtorParams);
|
||||
}
|
||||
}
|
||||
|
||||
ProtocolFactoryCtorParams
|
||||
FaceSystem::makePFCtorParams()
|
||||
{
|
||||
auto addFace = bind(&FaceTable::add, &m_faceTable, _1);
|
||||
return {addFace, m_netmon};
|
||||
}
|
||||
|
||||
FaceSystem::~FaceSystem() = default;
|
||||
|
||||
std::set<const ProtocolFactory*>
|
||||
FaceSystem::listProtocolFactories() const
|
||||
{
|
||||
std::set<const ProtocolFactory*> factories;
|
||||
for (const auto& p : m_factories) {
|
||||
factories.insert(p.second.get());
|
||||
}
|
||||
return factories;
|
||||
}
|
||||
|
||||
ProtocolFactory*
|
||||
FaceSystem::getFactoryById(const std::string& id)
|
||||
{
|
||||
auto found = m_factories.find(id);
|
||||
return found == m_factories.end() ? nullptr : found->second.get();
|
||||
}
|
||||
|
||||
ProtocolFactory*
|
||||
FaceSystem::getFactoryByScheme(const std::string& scheme)
|
||||
{
|
||||
auto found = m_factoryByScheme.find(scheme);
|
||||
return found == m_factoryByScheme.end() ? nullptr : found->second;
|
||||
}
|
||||
|
||||
void
|
||||
FaceSystem::setConfigFile(ConfigFile& configFile)
|
||||
{
|
||||
configFile.addSectionHandler("face_system", bind(&FaceSystem::processConfig, this, _1, _2, _3));
|
||||
}
|
||||
|
||||
void
|
||||
FaceSystem::processConfig(const ConfigSection& configSection, bool isDryRun, const std::string& filename)
|
||||
{
|
||||
ConfigContext context;
|
||||
context.isDryRun = isDryRun;
|
||||
|
||||
// process general protocol factory config section
|
||||
auto generalSection = configSection.get_child_optional("general");
|
||||
if (generalSection) {
|
||||
for (const auto& pair : *generalSection) {
|
||||
const std::string& key = pair.first;
|
||||
if (key == "enable_congestion_marking") {
|
||||
context.generalConfig.wantCongestionMarking = ConfigFile::parseYesNo(pair, "face_system.general");
|
||||
}
|
||||
else {
|
||||
BOOST_THROW_EXCEPTION(ConfigFile::Error("Unrecognized option face_system.general." + key));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// process sections in protocol factories
|
||||
for (const auto& pair : m_factories) {
|
||||
const std::string& sectionName = pair.first;
|
||||
ProtocolFactory* factory = pair.second.get();
|
||||
|
||||
std::set<std::string> oldProvidedSchemes = factory->getProvidedSchemes();
|
||||
factory->processConfig(configSection.get_child_optional(sectionName), context);
|
||||
|
||||
if (!isDryRun) {
|
||||
for (const std::string& scheme : factory->getProvidedSchemes()) {
|
||||
m_factoryByScheme[scheme] = factory;
|
||||
if (oldProvidedSchemes.erase(scheme) == 0) {
|
||||
NFD_LOG_TRACE("factory " << sectionName <<
|
||||
" provides " << scheme << " FaceUri scheme");
|
||||
}
|
||||
}
|
||||
for (const std::string& scheme : oldProvidedSchemes) {
|
||||
m_factoryByScheme.erase(scheme);
|
||||
NFD_LOG_TRACE("factory " << sectionName <<
|
||||
" no longer provides " << scheme << " FaceUri scheme");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// process other sections
|
||||
std::set<std::string> seenSections;
|
||||
for (const auto& pair : configSection) {
|
||||
const std::string& sectionName = pair.first;
|
||||
// const ConfigSection& subSection = pair.second;
|
||||
|
||||
if (!seenSections.insert(sectionName).second) {
|
||||
BOOST_THROW_EXCEPTION(ConfigFile::Error("Duplicate section face_system." + sectionName));
|
||||
}
|
||||
|
||||
if (sectionName == "general" || m_factories.count(sectionName) > 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
///\todo #3521 nicfaces
|
||||
|
||||
BOOST_THROW_EXCEPTION(ConfigFile::Error("Unrecognized option face_system." + sectionName));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace face
|
||||
} // namespace nfd
|
||||
@@ -0,0 +1,131 @@
|
||||
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
|
||||
/*
|
||||
* Copyright (c) 2014-2018, 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_FACE_SYSTEM_HPP
|
||||
#define NFD_DAEMON_FACE_FACE_SYSTEM_HPP
|
||||
|
||||
#include "channel.hpp"
|
||||
#include "core/config-file.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 {
|
||||
|
||||
class FaceTable;
|
||||
|
||||
namespace face {
|
||||
|
||||
class ProtocolFactory;
|
||||
struct ProtocolFactoryCtorParams;
|
||||
|
||||
/** \brief entry point of the face system
|
||||
*
|
||||
* NFD's face system is organized as a FaceSystem-ProtocolFactory-Channel-Face hierarchy.
|
||||
* FaceSystem class is the entry point of NFD's face system and owns ProtocolFactory objects.
|
||||
*/
|
||||
class FaceSystem : noncopyable
|
||||
{
|
||||
public:
|
||||
FaceSystem(FaceTable& faceTable, shared_ptr<ndn::net::NetworkMonitor> netmon);
|
||||
|
||||
~FaceSystem();
|
||||
|
||||
/** \return ProtocolFactory objects owned by the FaceSystem
|
||||
*/
|
||||
std::set<const ProtocolFactory*>
|
||||
listProtocolFactories() const;
|
||||
|
||||
/** \return ProtocolFactory for the specified registered factory id or nullptr if not found
|
||||
*/
|
||||
ProtocolFactory*
|
||||
getFactoryById(const std::string& id);
|
||||
|
||||
/** \return ProtocolFactory for the specified FaceUri scheme or nullptr if not found
|
||||
*/
|
||||
ProtocolFactory*
|
||||
getFactoryByScheme(const std::string& scheme);
|
||||
|
||||
FaceTable&
|
||||
getFaceTable()
|
||||
{
|
||||
return m_faceTable;
|
||||
}
|
||||
|
||||
/** \brief register handler for face_system section of NFD configuration file
|
||||
*/
|
||||
void
|
||||
setConfigFile(ConfigFile& configFile);
|
||||
|
||||
/** \brief configuration options from "general" section
|
||||
*/
|
||||
struct GeneralConfig
|
||||
{
|
||||
bool wantCongestionMarking = true;
|
||||
};
|
||||
|
||||
/** \brief context for processing a config section in ProtocolFactory
|
||||
*/
|
||||
class ConfigContext : noncopyable
|
||||
{
|
||||
public:
|
||||
GeneralConfig generalConfig;
|
||||
bool isDryRun;
|
||||
};
|
||||
|
||||
PUBLIC_WITH_TESTS_ELSE_PRIVATE:
|
||||
ProtocolFactoryCtorParams
|
||||
makePFCtorParams();
|
||||
|
||||
private:
|
||||
void
|
||||
processConfig(const ConfigSection& configSection, bool isDryRun,
|
||||
const std::string& filename);
|
||||
|
||||
PUBLIC_WITH_TESTS_ELSE_PRIVATE:
|
||||
/** \brief config section name => protocol factory
|
||||
*/
|
||||
std::map<std::string, unique_ptr<ProtocolFactory>> m_factories;
|
||||
|
||||
private:
|
||||
/** \brief scheme => protocol factory
|
||||
*
|
||||
* The same protocol factory may be available under multiple schemes.
|
||||
*/
|
||||
std::map<std::string, ProtocolFactory*> m_factoryByScheme;
|
||||
|
||||
FaceTable& m_faceTable;
|
||||
|
||||
shared_ptr<ndn::net::NetworkMonitor> m_netmon;
|
||||
};
|
||||
|
||||
} // namespace face
|
||||
|
||||
using face::FaceSystem;
|
||||
|
||||
} // namespace nfd
|
||||
|
||||
#endif // NFD_DAEMON_FACE_FACE_SYSTEM_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,
|
||||
@@ -32,6 +32,7 @@ Face::Face(unique_ptr<LinkService> service, unique_ptr<Transport> transport)
|
||||
: afterReceiveInterest(service->afterReceiveInterest)
|
||||
, afterReceiveData(service->afterReceiveData)
|
||||
, afterReceiveNack(service->afterReceiveNack)
|
||||
, onDroppedInterest(service->onDroppedInterest)
|
||||
, afterStateChange(transport->afterStateChange)
|
||||
, m_id(INVALID_FACEID)
|
||||
, m_service(std::move(service))
|
||||
|
||||
+16
-19
@@ -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,
|
||||
@@ -23,13 +23,13 @@
|
||||
* NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef NFD_DAEMON_FACE_HPP
|
||||
#define NFD_DAEMON_FACE_HPP
|
||||
#ifndef NFD_DAEMON_FACE_FACE_HPP
|
||||
#define NFD_DAEMON_FACE_FACE_HPP
|
||||
|
||||
#include "transport.hpp"
|
||||
#include "link-service.hpp"
|
||||
#include "face-counters.hpp"
|
||||
#include "face-log.hpp"
|
||||
#include "link-service.hpp"
|
||||
#include "transport.hpp"
|
||||
|
||||
namespace nfd {
|
||||
namespace face {
|
||||
@@ -66,7 +66,7 @@ typedef TransportState FaceState;
|
||||
*/
|
||||
class Face
|
||||
#ifndef WITH_TESTS
|
||||
DECL_CLASS_FINAL
|
||||
final
|
||||
#endif
|
||||
: public enable_shared_from_this<Face>, noncopyable
|
||||
{
|
||||
@@ -74,10 +74,10 @@ public:
|
||||
Face(unique_ptr<LinkService> service, unique_ptr<Transport> transport);
|
||||
|
||||
LinkService*
|
||||
getLinkService();
|
||||
getLinkService() const;
|
||||
|
||||
Transport*
|
||||
getTransport();
|
||||
getTransport() const;
|
||||
|
||||
public: // upper interface connected to forwarding
|
||||
/** \brief sends Interest on Face
|
||||
@@ -107,6 +107,10 @@ public: // upper interface connected to forwarding
|
||||
*/
|
||||
signal::Signal<LinkService, lp::Nack>& afterReceiveNack;
|
||||
|
||||
/** \brief signals on Interest dropped by reliability system for exceeding allowed number of retx
|
||||
*/
|
||||
signal::Signal<LinkService, Interest>& onDroppedInterest;
|
||||
|
||||
public: // static properties
|
||||
/** \return face ID
|
||||
*/
|
||||
@@ -189,13 +193,13 @@ private:
|
||||
};
|
||||
|
||||
inline LinkService*
|
||||
Face::getLinkService()
|
||||
Face::getLinkService() const
|
||||
{
|
||||
return m_service.get();
|
||||
}
|
||||
|
||||
inline Transport*
|
||||
Face::getTransport()
|
||||
Face::getTransport() const
|
||||
{
|
||||
return m_transport.get();
|
||||
}
|
||||
@@ -293,13 +297,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;
|
||||
@@ -307,4 +304,4 @@ using face::Face;
|
||||
|
||||
} // namespace nfd
|
||||
|
||||
#endif // NFD_DAEMON_FACE_HPP
|
||||
#endif // NFD_DAEMON_FACE_FACE_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-2018, Regents of the University of California,
|
||||
* Arizona Board of Regents,
|
||||
* Colorado State University,
|
||||
* University Pierre & Marie Curie, Sorbonne University,
|
||||
@@ -25,31 +25,80 @@
|
||||
|
||||
#include "generic-link-service.hpp"
|
||||
|
||||
#include <ndn-cxx/lp/tags.hpp>
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace nfd {
|
||||
namespace face {
|
||||
|
||||
NFD_LOG_INIT("GenericLinkService");
|
||||
|
||||
GenericLinkServiceCounters::GenericLinkServiceCounters(const LpReassembler& reassembler)
|
||||
: nReassembling(reassembler)
|
||||
{
|
||||
}
|
||||
constexpr uint32_t DEFAULT_CONGESTION_THRESHOLD_DIVISOR = 2;
|
||||
|
||||
GenericLinkService::Options::Options()
|
||||
: allowLocalFields(false)
|
||||
, allowFragmentation(false)
|
||||
, allowReassembly(false)
|
||||
, allowCongestionMarking(false)
|
||||
, baseCongestionMarkingInterval(time::milliseconds(100)) // Interval from RFC 8289 (CoDel)
|
||||
, defaultCongestionThreshold(65536) // This default value works well for a queue capacity of 200KiB
|
||||
, allowSelfLearning(false)
|
||||
{
|
||||
}
|
||||
|
||||
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_nextMarkTime(time::steady_clock::TimePoint::max())
|
||||
, m_lastMarkTime(time::steady_clock::TimePoint::min())
|
||||
, m_nMarkedSinceInMarkingState(0)
|
||||
{
|
||||
m_reassembler.beforeTimeout.connect(bind([this] { ++this->nReassemblyTimeouts; }));
|
||||
m_reliability.onDroppedInterest.connect([this] (const Interest& i) { this->notifyDroppedInterest(i); });
|
||||
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);
|
||||
}
|
||||
|
||||
if (m_options.allowCongestionMarking) {
|
||||
checkCongestionLevel(pkt);
|
||||
}
|
||||
|
||||
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
|
||||
@@ -57,11 +106,9 @@ GenericLinkService::doSendInterest(const Interest& interest)
|
||||
{
|
||||
lp::Packet lpPacket(interest.wireEncode());
|
||||
|
||||
if (m_options.allowLocalFields) {
|
||||
encodeLocalFields(interest, lpPacket);
|
||||
}
|
||||
encodeLpFields(interest, lpPacket);
|
||||
|
||||
this->sendNetPacket(std::move(lpPacket));
|
||||
this->sendNetPacket(std::move(lpPacket), true);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -69,11 +116,9 @@ GenericLinkService::doSendData(const Data& data)
|
||||
{
|
||||
lp::Packet lpPacket(data.wireEncode());
|
||||
|
||||
if (m_options.allowLocalFields) {
|
||||
encodeLocalFields(data, lpPacket);
|
||||
}
|
||||
encodeLpFields(data, lpPacket);
|
||||
|
||||
this->sendNetPacket(std::move(lpPacket));
|
||||
this->sendNetPacket(std::move(lpPacket), false);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -82,27 +127,56 @@ GenericLinkService::doSendNack(const lp::Nack& nack)
|
||||
lp::Packet lpPacket(nack.getInterest().wireEncode());
|
||||
lpPacket.add<lp::NackField>(nack.getHeader());
|
||||
|
||||
if (m_options.allowLocalFields) {
|
||||
encodeLocalFields(nack, lpPacket);
|
||||
}
|
||||
encodeLpFields(nack, lpPacket);
|
||||
|
||||
this->sendNetPacket(std::move(lpPacket));
|
||||
this->sendNetPacket(std::move(lpPacket), false);
|
||||
}
|
||||
|
||||
void
|
||||
GenericLinkService::encodeLocalFields(const ndn::TagHost& netPkt, lp::Packet& lpPacket)
|
||||
GenericLinkService::encodeLpFields(const ndn::PacketBase& netPkt, lp::Packet& lpPacket)
|
||||
{
|
||||
shared_ptr<lp::IncomingFaceIdTag> incomingFaceIdTag = netPkt.getTag<lp::IncomingFaceIdTag>();
|
||||
if (incomingFaceIdTag != nullptr) {
|
||||
lpPacket.add<lp::IncomingFaceIdField>(*incomingFaceIdTag);
|
||||
if (m_options.allowLocalFields) {
|
||||
shared_ptr<lp::IncomingFaceIdTag> incomingFaceIdTag = netPkt.getTag<lp::IncomingFaceIdTag>();
|
||||
if (incomingFaceIdTag != nullptr) {
|
||||
lpPacket.add<lp::IncomingFaceIdField>(*incomingFaceIdTag);
|
||||
}
|
||||
}
|
||||
|
||||
shared_ptr<lp::CongestionMarkTag> congestionMarkTag = netPkt.getTag<lp::CongestionMarkTag>();
|
||||
if (congestionMarkTag != nullptr) {
|
||||
lpPacket.add<lp::CongestionMarkField>(*congestionMarkTag);
|
||||
}
|
||||
|
||||
if (m_options.allowSelfLearning) {
|
||||
shared_ptr<lp::NonDiscoveryTag> nonDiscoveryTag = netPkt.getTag<lp::NonDiscoveryTag>();
|
||||
if (nonDiscoveryTag != nullptr) {
|
||||
lpPacket.add<lp::NonDiscoveryField>(*nonDiscoveryTag);
|
||||
}
|
||||
|
||||
shared_ptr<lp::PrefixAnnouncementTag> prefixAnnouncementTag = netPkt.getTag<lp::PrefixAnnouncementTag>();
|
||||
if (prefixAnnouncementTag != nullptr) {
|
||||
lpPacket.add<lp::PrefixAnnouncementField>(*prefixAnnouncementTag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GenericLinkService::sendNetPacket(lp::Packet&& pkt)
|
||||
GenericLinkService::sendNetPacket(lp::Packet&& pkt, bool isInterest)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
if (m_options.allowCongestionMarking && mtu != MTU_UNLIMITED) {
|
||||
mtu -= CONGESTION_MARK_SIZE;
|
||||
}
|
||||
|
||||
BOOST_ASSERT(mtu == MTU_UNLIMITED || mtu > 0);
|
||||
|
||||
if (m_options.allowFragmentation && mtu != MTU_UNLIMITED) {
|
||||
bool isOk = false;
|
||||
std::tie(isOk, frags) = m_fragmenter.fragmentPacket(pkt, mtu);
|
||||
@@ -113,29 +187,33 @@ GenericLinkService::sendNetPacket(lp::Packet&& pkt)
|
||||
}
|
||||
}
|
||||
else {
|
||||
frags.push_back(pkt);
|
||||
if (m_options.reliabilityOptions.isEnabled) {
|
||||
frags.push_back(pkt);
|
||||
}
|
||||
else {
|
||||
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, std::move(pkt), isInterest);
|
||||
}
|
||||
|
||||
for (lp::Packet& frag : frags) {
|
||||
this->sendLpPacket(std::move(frag));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,12 +229,69 @@ GenericLinkService::assignSequences(std::vector<lp::Packet>& pkts)
|
||||
std::for_each(pkts.begin(), pkts.end(), bind(&GenericLinkService::assignSequence, this, _1));
|
||||
}
|
||||
|
||||
void
|
||||
GenericLinkService::checkCongestionLevel(lp::Packet& pkt)
|
||||
{
|
||||
ssize_t sendQueueLength = getTransport()->getSendQueueLength();
|
||||
// This operation requires that the transport supports retrieving current send queue length
|
||||
if (sendQueueLength < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// To avoid overflowing the queue, set the congestion threshold to at least half of the send
|
||||
// queue capacity.
|
||||
size_t congestionThreshold = m_options.defaultCongestionThreshold;
|
||||
if (getTransport()->getSendQueueCapacity() >= 0) {
|
||||
congestionThreshold = std::min(congestionThreshold,
|
||||
static_cast<size_t>(getTransport()->getSendQueueCapacity()) /
|
||||
DEFAULT_CONGESTION_THRESHOLD_DIVISOR);
|
||||
}
|
||||
|
||||
if (sendQueueLength > 0) {
|
||||
NFD_LOG_FACE_TRACE("txqlen=" << sendQueueLength << " threshold=" << congestionThreshold <<
|
||||
" capacity=" << getTransport()->getSendQueueCapacity());
|
||||
}
|
||||
|
||||
if (static_cast<size_t>(sendQueueLength) > congestionThreshold) { // Send queue is congested
|
||||
const auto now = time::steady_clock::now();
|
||||
if (now >= m_nextMarkTime || now >= m_lastMarkTime + m_options.baseCongestionMarkingInterval) {
|
||||
// Mark at most one initial packet per baseCongestionMarkingInterval
|
||||
if (m_nMarkedSinceInMarkingState == 0) {
|
||||
m_nextMarkTime = now;
|
||||
}
|
||||
|
||||
// Time to mark packet
|
||||
pkt.set<lp::CongestionMarkField>(1);
|
||||
++nCongestionMarked;
|
||||
NFD_LOG_FACE_DEBUG("LpPacket was marked as congested");
|
||||
|
||||
++m_nMarkedSinceInMarkingState;
|
||||
// Decrease the marking interval by the inverse of the square root of the number of packets
|
||||
// marked in this incident of congestion
|
||||
m_nextMarkTime += time::nanoseconds(static_cast<time::nanoseconds::rep>(
|
||||
m_options.baseCongestionMarkingInterval.count() /
|
||||
std::sqrt(m_nMarkedSinceInMarkingState)));
|
||||
m_lastMarkTime = now;
|
||||
}
|
||||
}
|
||||
else if (m_nextMarkTime != time::steady_clock::TimePoint::max()) {
|
||||
// Congestion incident has ended, so reset
|
||||
NFD_LOG_FACE_DEBUG("Send queue length dropped below congestion threshold");
|
||||
m_nextMarkTime = time::steady_clock::TimePoint::max();
|
||||
m_nMarkedSinceInMarkingState = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
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;
|
||||
@@ -240,6 +375,25 @@ GenericLinkService::decodeInterest(const Block& netPkt, const lp::Packet& firstP
|
||||
NFD_LOG_FACE_WARN("received IncomingFaceId: IGNORE");
|
||||
}
|
||||
|
||||
if (firstPkt.has<lp::CongestionMarkField>()) {
|
||||
interest->setTag(make_shared<lp::CongestionMarkTag>(firstPkt.get<lp::CongestionMarkField>()));
|
||||
}
|
||||
|
||||
if (firstPkt.has<lp::NonDiscoveryField>()) {
|
||||
if (m_options.allowSelfLearning) {
|
||||
interest->setTag(make_shared<lp::NonDiscoveryTag>(firstPkt.get<lp::NonDiscoveryField>()));
|
||||
}
|
||||
else {
|
||||
NFD_LOG_FACE_WARN("received NonDiscovery, but self-learning disabled: IGNORE");
|
||||
}
|
||||
}
|
||||
|
||||
if (firstPkt.has<lp::PrefixAnnouncementField>()) {
|
||||
++this->nInNetInvalid;
|
||||
NFD_LOG_FACE_WARN("received PrefixAnnouncement with Interest: DROP");
|
||||
return;
|
||||
}
|
||||
|
||||
this->receiveInterest(*interest);
|
||||
}
|
||||
|
||||
@@ -264,20 +418,35 @@ 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>()) {
|
||||
NFD_LOG_FACE_WARN("received IncomingFaceId: IGNORE");
|
||||
}
|
||||
|
||||
if (firstPkt.has<lp::CongestionMarkField>()) {
|
||||
data->setTag(make_shared<lp::CongestionMarkTag>(firstPkt.get<lp::CongestionMarkField>()));
|
||||
}
|
||||
|
||||
if (firstPkt.has<lp::NonDiscoveryField>()) {
|
||||
++this->nInNetInvalid;
|
||||
NFD_LOG_FACE_WARN("received NonDiscovery with Data: DROP");
|
||||
return;
|
||||
}
|
||||
|
||||
if (firstPkt.has<lp::PrefixAnnouncementField>()) {
|
||||
if (m_options.allowSelfLearning) {
|
||||
data->setTag(make_shared<lp::PrefixAnnouncementTag>(firstPkt.get<lp::PrefixAnnouncementField>()));
|
||||
}
|
||||
else {
|
||||
NFD_LOG_FACE_WARN("received PrefixAnnouncement, but self-learning disabled: IGNORE");
|
||||
}
|
||||
}
|
||||
|
||||
this->receiveData(*data);
|
||||
}
|
||||
|
||||
@@ -306,6 +475,22 @@ GenericLinkService::decodeNack(const Block& netPkt, const lp::Packet& firstPkt)
|
||||
NFD_LOG_FACE_WARN("received IncomingFaceId: IGNORE");
|
||||
}
|
||||
|
||||
if (firstPkt.has<lp::CongestionMarkField>()) {
|
||||
nack.setTag(make_shared<lp::CongestionMarkTag>(firstPkt.get<lp::CongestionMarkField>()));
|
||||
}
|
||||
|
||||
if (firstPkt.has<lp::NonDiscoveryField>()) {
|
||||
++this->nInNetInvalid;
|
||||
NFD_LOG_FACE_WARN("received NonDiscovery with Nack: DROP");
|
||||
return;
|
||||
}
|
||||
|
||||
if (firstPkt.has<lp::PrefixAnnouncementField>()) {
|
||||
++this->nInNetInvalid;
|
||||
NFD_LOG_FACE_WARN("received PrefixAnnouncement with Nack: DROP");
|
||||
return;
|
||||
}
|
||||
|
||||
this->receiveNack(nack);
|
||||
}
|
||||
|
||||
|
||||
@@ -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-2018, 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 "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,28 @@ 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 count of outgoing LpPackets that were marked with congestion marks
|
||||
*/
|
||||
PacketCounter nCongestionMarked;
|
||||
};
|
||||
|
||||
/** \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 +120,31 @@ public:
|
||||
/** \brief options for reassembly
|
||||
*/
|
||||
LpReassembler::Options reassemblerOptions;
|
||||
|
||||
/** \brief options for reliability
|
||||
*/
|
||||
LpReliability::Options reliabilityOptions;
|
||||
|
||||
/** \brief enables send queue congestion detection and marking
|
||||
*/
|
||||
bool allowCongestionMarking;
|
||||
|
||||
/** \brief starting value for congestion marking interval
|
||||
*/
|
||||
time::nanoseconds baseCongestionMarkingInterval;
|
||||
|
||||
/** \brief default congestion threshold in bytes
|
||||
*/
|
||||
size_t defaultCongestionThreshold;
|
||||
|
||||
/** \brief enables self-learning forwarding support
|
||||
*/
|
||||
bool allowSelfLearning;
|
||||
};
|
||||
|
||||
/** \brief counters provided by GenericLinkService
|
||||
*/
|
||||
typedef GenericLinkServiceCounters Counters;
|
||||
using Counters = GenericLinkServiceCounters;
|
||||
|
||||
explicit
|
||||
GenericLinkService(const Options& options = Options());
|
||||
@@ -126,36 +159,50 @@ public:
|
||||
void
|
||||
setOptions(const Options& options);
|
||||
|
||||
virtual const Counters&
|
||||
getCounters() const DECL_OVERRIDE;
|
||||
const Counters&
|
||||
getCounters() const override;
|
||||
|
||||
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);
|
||||
|
||||
private: // send path
|
||||
/** \brief send Interest
|
||||
*/
|
||||
void
|
||||
doSendInterest(const Interest& interest) DECL_OVERRIDE;
|
||||
doSendInterest(const Interest& interest) override;
|
||||
|
||||
/** \brief send Data
|
||||
*/
|
||||
void
|
||||
doSendData(const Data& data) DECL_OVERRIDE;
|
||||
doSendData(const Data& data) override;
|
||||
|
||||
/** \brief send Nack
|
||||
*/
|
||||
void
|
||||
doSendNack(const ndn::lp::Nack& nack) DECL_OVERRIDE;
|
||||
doSendNack(const ndn::lp::Nack& nack) override;
|
||||
|
||||
/** \brief encode local fields from tags onto outgoing LpPacket
|
||||
* \param pkt LpPacket containing a complete network layer packet
|
||||
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
|
||||
*/
|
||||
static void
|
||||
encodeLocalFields(const ndn::TagHost& netPkt, lp::Packet& lpPacket);
|
||||
void
|
||||
encodeLpFields(const ndn::PacketBase& netPkt, lp::Packet& lpPacket);
|
||||
|
||||
/** \brief send a complete network layer packet
|
||||
* \param pkt LpPacket containing a complete network layer packet
|
||||
* \param isInterest whether the network layer packet is an Interest
|
||||
*/
|
||||
void
|
||||
sendNetPacket(lp::Packet&& pkt);
|
||||
sendNetPacket(lp::Packet&& pkt, bool isInterest);
|
||||
|
||||
/** \brief assign a sequence number to an LpPacket
|
||||
*/
|
||||
@@ -167,11 +214,18 @@ private: // send path
|
||||
void
|
||||
assignSequences(std::vector<lp::Packet>& pkts);
|
||||
|
||||
/** \brief if the send queue is found to be congested, add a congestion mark to the packet
|
||||
* according to CoDel
|
||||
* \sa https://tools.ietf.org/html/rfc8289
|
||||
*/
|
||||
void
|
||||
checkCongestionLevel(lp::Packet& pkt);
|
||||
|
||||
private: // receive path
|
||||
/** \brief receive Packet from Transport
|
||||
*/
|
||||
void
|
||||
doReceivePacket(Transport::Packet&& packet) DECL_OVERRIDE;
|
||||
doReceivePacket(Transport::Packet&& packet) override;
|
||||
|
||||
/** \brief decode incoming network-layer packet
|
||||
* \param netPkt reassembled network-layer packet
|
||||
@@ -219,11 +273,24 @@ 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;
|
||||
|
||||
PUBLIC_WITH_TESTS_ELSE_PRIVATE:
|
||||
/// CongestionMark TLV-TYPE (3 octets) + CongestionMark TLV-LENGTH (1 octet) + sizeof(uint64_t)
|
||||
static constexpr size_t CONGESTION_MARK_SIZE = 3 + 1 + sizeof(uint64_t);
|
||||
/// Time to mark next packet due to send queue congestion
|
||||
time::steady_clock::TimePoint m_nextMarkTime;
|
||||
/// Time last packet was marked
|
||||
time::steady_clock::TimePoint m_lastMarkTime;
|
||||
/// number of marked packets in the current incident of congestion
|
||||
size_t m_nMarkedSinceInMarkingState;
|
||||
|
||||
friend class LpReliability;
|
||||
};
|
||||
|
||||
inline const GenericLinkService::Options&
|
||||
@@ -232,12 +299,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,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,
|
||||
@@ -45,15 +45,6 @@ InternalForwarderTransport::InternalForwarderTransport(
|
||||
NFD_LOG_FACE_INFO("Creating transport");
|
||||
}
|
||||
|
||||
void
|
||||
InternalForwarderTransport::beforeChangePersistency(ndn::nfd::FacePersistency newPersistency)
|
||||
{
|
||||
if (newPersistency != ndn::nfd::FACE_PERSISTENCY_PERMANENT) {
|
||||
BOOST_THROW_EXCEPTION(
|
||||
std::invalid_argument("InternalForwarderTransport supports only FACE_PERSISTENCY_PERMANENT"));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
InternalForwarderTransport::receiveFromLink(const Block& packet)
|
||||
{
|
||||
|
||||
@@ -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,
|
||||
@@ -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://"),
|
||||
@@ -58,19 +61,16 @@ public:
|
||||
ndn::nfd::FaceScope scope = ndn::nfd::FACE_SCOPE_LOCAL,
|
||||
ndn::nfd::LinkType linkType = ndn::nfd::LINK_TYPE_POINT_TO_POINT);
|
||||
|
||||
virtual void
|
||||
receiveFromLink(const Block& packet) DECL_OVERRIDE;
|
||||
void
|
||||
receiveFromLink(const Block& packet) override;
|
||||
|
||||
protected:
|
||||
virtual void
|
||||
beforeChangePersistency(ndn::nfd::FacePersistency newPersistency) DECL_OVERRIDE;
|
||||
|
||||
virtual void
|
||||
doClose() DECL_OVERRIDE;
|
||||
void
|
||||
doClose() override;
|
||||
|
||||
private:
|
||||
virtual void
|
||||
doSend(Packet&& packet) DECL_OVERRIDE;
|
||||
void
|
||||
doSend(Packet&& packet) override;
|
||||
|
||||
private:
|
||||
NFD_LOG_INCLASS_DECLARE();
|
||||
@@ -92,29 +92,29 @@ public:
|
||||
void
|
||||
connectToForwarder(InternalForwarderTransport* forwarderTransport);
|
||||
|
||||
virtual void
|
||||
receiveFromLink(const Block& packet) DECL_OVERRIDE;
|
||||
void
|
||||
receiveFromLink(const Block& packet) override;
|
||||
|
||||
virtual void
|
||||
close() DECL_OVERRIDE
|
||||
void
|
||||
close() override
|
||||
{
|
||||
}
|
||||
|
||||
virtual void
|
||||
pause() DECL_OVERRIDE
|
||||
void
|
||||
pause() override
|
||||
{
|
||||
}
|
||||
|
||||
virtual void
|
||||
resume() DECL_OVERRIDE
|
||||
void
|
||||
resume() override
|
||||
{
|
||||
}
|
||||
|
||||
virtual void
|
||||
send(const Block& wire) DECL_OVERRIDE;
|
||||
void
|
||||
send(const Block& wire) override;
|
||||
|
||||
virtual void
|
||||
send(const Block& header, const Block& payload) DECL_OVERRIDE;
|
||||
void
|
||||
send(const Block& header, const Block& payload) override;
|
||||
|
||||
private:
|
||||
NFD_LOG_INCLASS_DECLARE();
|
||||
|
||||
@@ -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,
|
||||
@@ -114,6 +114,13 @@ LinkService::receiveNack(const ndn::lp::Nack& nack)
|
||||
afterReceiveNack(nack);
|
||||
}
|
||||
|
||||
void
|
||||
LinkService::notifyDroppedInterest(const Interest& interest)
|
||||
{
|
||||
++this->nDroppedInterests;
|
||||
onDroppedInterest(interest);
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os, const FaceLogHelper<LinkService>& flh)
|
||||
{
|
||||
|
||||
@@ -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,
|
||||
@@ -27,8 +27,8 @@
|
||||
#define NFD_DAEMON_FACE_LINK_SERVICE_HPP
|
||||
|
||||
#include "core/counter.hpp"
|
||||
#include "transport.hpp"
|
||||
#include "face-log.hpp"
|
||||
#include "transport.hpp"
|
||||
|
||||
namespace nfd {
|
||||
namespace face {
|
||||
@@ -50,11 +50,15 @@ public:
|
||||
*/
|
||||
PacketCounter nOutInterests;
|
||||
|
||||
/** \brief count of incoming Data
|
||||
/** \brief count of Interests dropped by reliability system for exceeding allowed number of retx
|
||||
*/
|
||||
PacketCounter nDroppedInterests;
|
||||
|
||||
/** \brief count of incoming Data packets
|
||||
*/
|
||||
PacketCounter nInData;
|
||||
|
||||
/** \brief count of outgoing Data
|
||||
/** \brief count of outgoing Data packets
|
||||
*/
|
||||
PacketCounter nOutData;
|
||||
|
||||
@@ -138,6 +142,10 @@ public: // upper interface to be used by forwarding
|
||||
*/
|
||||
signal::Signal<LinkService, lp::Nack> afterReceiveNack;
|
||||
|
||||
/** \brief signals on Interest dropped by reliability system for exceeding allowed number of retx
|
||||
*/
|
||||
signal::Signal<LinkService, Interest> onDroppedInterest;
|
||||
|
||||
public: // lower interface to be invoked by Transport
|
||||
/** \brief performs LinkService specific operations to receive a lower-layer packet
|
||||
*/
|
||||
@@ -166,6 +174,10 @@ protected: // lower interface to be invoked in subclass (send path termination)
|
||||
void
|
||||
sendPacket(Transport::Packet&& packet);
|
||||
|
||||
protected:
|
||||
void
|
||||
notifyDroppedInterest(const Interest& packet);
|
||||
|
||||
private: // upper interface to be overridden in subclass (send path entrypoint)
|
||||
/** \brief performs LinkService specific operations to send an Interest
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,359 @@
|
||||
/* -*- 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, lp::Packet&& pkt, bool isInterest)
|
||||
{
|
||||
BOOST_ASSERT(m_options.isEnabled);
|
||||
|
||||
auto unackedFragsIt = m_unackedFrags.begin();
|
||||
auto sendTime = time::steady_clock::now();
|
||||
|
||||
auto netPkt = make_shared<NetPkt>(std::move(pkt), isInterest);
|
||||
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;
|
||||
|
||||
// Notify strategy of dropped Interest (if any)
|
||||
if (netPkt->isInterest) {
|
||||
BOOST_ASSERT(netPkt->pkt.has<lp::FragmentField>());
|
||||
ndn::Buffer::const_iterator fragBegin, fragEnd;
|
||||
std::tie(fragBegin, fragEnd) = netPkt->pkt.get<lp::FragmentField>();
|
||||
Block frag(&*fragBegin, std::distance(fragBegin, fragEnd));
|
||||
onDroppedInterest(Interest(frag));
|
||||
}
|
||||
|
||||
deleteUnackedFrag(txSeqIt);
|
||||
}
|
||||
else {
|
||||
// Assign new TxSequence
|
||||
lp::Sequence newTxSeq = assignTxSequence(txFrag.pkt);
|
||||
netPkt->didRetx = true;
|
||||
|
||||
// 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)
|
||||
{
|
||||
}
|
||||
|
||||
LpReliability::NetPkt::NetPkt(lp::Packet&& pkt, bool isInterest)
|
||||
: pkt(std::move(pkt))
|
||||
, isInterest(isInterest)
|
||||
, didRetx(false)
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace face
|
||||
} // namespace nfd
|
||||
@@ -0,0 +1,224 @@
|
||||
/* -*- 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 signals on Interest dropped by reliability system for exceeding allowed number of retx
|
||||
*/
|
||||
signal::Signal<LpReliability, Interest> onDroppedInterest;
|
||||
|
||||
/** \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
|
||||
* \param pkt encapsulated network packet
|
||||
* \param isInterest whether the network packet is an Interest
|
||||
*/
|
||||
void
|
||||
handleOutgoing(std::vector<lp::Packet>& frags, lp::Packet&& pkt, bool isInterest);
|
||||
|
||||
/** \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:
|
||||
NetPkt(lp::Packet&& pkt, bool isInterest);
|
||||
|
||||
public:
|
||||
std::vector<UnackedFrags::iterator> unackedFrags;
|
||||
lp::Packet pkt;
|
||||
bool isInterest;
|
||||
bool didRetx;
|
||||
};
|
||||
|
||||
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
|
||||
@@ -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-2018, Regents of the University of California,
|
||||
* Arizona Board of Regents,
|
||||
* Colorado State University,
|
||||
* University Pierre & Marie Curie, Sorbonne University,
|
||||
@@ -24,39 +24,62 @@
|
||||
*/
|
||||
|
||||
#include "multicast-udp-transport.hpp"
|
||||
#include "socket-utils.hpp"
|
||||
#include "udp-protocol.hpp"
|
||||
|
||||
#include "core/privilege-helper.hpp"
|
||||
|
||||
#include <boost/functional/hash.hpp>
|
||||
|
||||
#ifdef __linux__
|
||||
#include <cerrno> // for errno
|
||||
#include <cstring> // for std::strerror()
|
||||
#include <sys/socket.h> // for setsockopt()
|
||||
#endif // __linux__
|
||||
|
||||
namespace nfd {
|
||||
namespace face {
|
||||
|
||||
NFD_LOG_INCLASS_2TEMPLATE_SPECIALIZATION_DEFINE(DatagramTransport, MulticastUdpTransport::protocol,
|
||||
Multicast, "MulticastUdpTransport");
|
||||
|
||||
MulticastUdpTransport::MulticastUdpTransport(const protocol::endpoint& localEndpoint,
|
||||
const protocol::endpoint& multicastGroup,
|
||||
MulticastUdpTransport::MulticastUdpTransport(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))
|
||||
{
|
||||
this->setLocalUri(FaceUri(localEndpoint));
|
||||
this->setLocalUri(FaceUri(m_sendSocket.local_endpoint()));
|
||||
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->setMtu(udp::computeMtu(localEndpoint));
|
||||
this->setLinkType(linkType);
|
||||
this->setMtu(udp::computeMtu(m_sendSocket.local_endpoint()));
|
||||
|
||||
protocol::socket::send_buffer_size sendBufferSizeOption;
|
||||
boost::system::error_code error;
|
||||
m_sendSocket.get_option(sendBufferSizeOption);
|
||||
if (error) {
|
||||
NFD_LOG_FACE_WARN("Failed to obtain send queue capacity from socket: " << error.message());
|
||||
this->setSendQueueCapacity(QUEUE_ERROR);
|
||||
}
|
||||
else {
|
||||
this->setSendQueueCapacity(sendBufferSizeOption.value());
|
||||
}
|
||||
|
||||
NFD_LOG_FACE_INFO("Creating transport");
|
||||
}
|
||||
|
||||
void
|
||||
MulticastUdpTransport::beforeChangePersistency(ndn::nfd::FacePersistency newPersistency)
|
||||
ssize_t
|
||||
MulticastUdpTransport::getSendQueueLength()
|
||||
{
|
||||
if (newPersistency != ndn::nfd::FACE_PERSISTENCY_PERMANENT) {
|
||||
BOOST_THROW_EXCEPTION(
|
||||
std::invalid_argument("MulticastUdpTransport supports only FACE_PERSISTENCY_PERMANENT"));
|
||||
ssize_t queueLength = getTxQueueLength(m_sendSocket.native_handle());
|
||||
if (queueLength == QUEUE_ERROR) {
|
||||
NFD_LOG_FACE_WARN("Failed to obtain send queue length from socket: " << std::strerror(errno));
|
||||
}
|
||||
return queueLength;
|
||||
}
|
||||
|
||||
void
|
||||
@@ -87,15 +110,101 @@ MulticastUdpTransport::doClose()
|
||||
DatagramTransport::doClose();
|
||||
}
|
||||
|
||||
static void
|
||||
bindToDevice(int fd, const std::string& ifname)
|
||||
{
|
||||
// On Linux, if there is more than one MulticastUdpTransport for the same multicast
|
||||
// group but they are on different network interfaces, each socket needs to be bound
|
||||
// to the corresponding interface using SO_BINDTODEVICE, otherwise the transport will
|
||||
// receive all packets sent to the other interfaces as well.
|
||||
// This is needed only on Linux. On macOS, the boost::asio::ip::multicast::join_group
|
||||
// option is sufficient to obtain the desired behavior.
|
||||
|
||||
#ifdef __linux__
|
||||
PrivilegeHelper::runElevated([=] {
|
||||
if (::setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, ifname.data(), ifname.size() + 1) < 0) {
|
||||
BOOST_THROW_EXCEPTION(MulticastUdpTransport::Error("Cannot bind multicast rx socket to " +
|
||||
ifname + ": " + std::strerror(errno)));
|
||||
}
|
||||
});
|
||||
#endif // __linux__
|
||||
}
|
||||
|
||||
void
|
||||
MulticastUdpTransport::openRxSocket(protocol::socket& sock,
|
||||
const protocol::endpoint& multicastGroup,
|
||||
const boost::asio::ip::address& localAddress,
|
||||
const shared_ptr<const ndn::net::NetworkInterface>& netif)
|
||||
{
|
||||
BOOST_ASSERT(!sock.is_open());
|
||||
|
||||
sock.open(multicastGroup.protocol());
|
||||
sock.set_option(protocol::socket::reuse_address(true));
|
||||
|
||||
if (multicastGroup.address().is_v4()) {
|
||||
BOOST_ASSERT(localAddress.is_v4());
|
||||
sock.bind(multicastGroup);
|
||||
sock.set_option(boost::asio::ip::multicast::join_group(multicastGroup.address().to_v4(),
|
||||
localAddress.to_v4()));
|
||||
}
|
||||
else {
|
||||
BOOST_ASSERT(localAddress.is_v6());
|
||||
sock.set_option(boost::asio::ip::v6_only(true));
|
||||
#ifdef WITH_TESTS
|
||||
// To simplify unit tests, we bind to the "any" IPv6 address if the supplied multicast
|
||||
// address lacks a scope id. Calling bind() without a scope id would otherwise fail.
|
||||
if (multicastGroup.address().to_v6().scope_id() == 0)
|
||||
sock.bind(protocol::endpoint(boost::asio::ip::address_v6::any(), multicastGroup.port()));
|
||||
else
|
||||
#endif
|
||||
sock.bind(multicastGroup);
|
||||
sock.set_option(boost::asio::ip::multicast::join_group(multicastGroup.address().to_v6()));
|
||||
}
|
||||
|
||||
if (netif)
|
||||
bindToDevice(sock.native_handle(), netif->getName());
|
||||
}
|
||||
|
||||
void
|
||||
MulticastUdpTransport::openTxSocket(protocol::socket& sock,
|
||||
const protocol::endpoint& localEndpoint,
|
||||
const shared_ptr<const ndn::net::NetworkInterface>& netif,
|
||||
bool enableLoopback)
|
||||
{
|
||||
BOOST_ASSERT(!sock.is_open());
|
||||
|
||||
sock.open(localEndpoint.protocol());
|
||||
sock.set_option(protocol::socket::reuse_address(true));
|
||||
sock.set_option(boost::asio::ip::multicast::enable_loopback(enableLoopback));
|
||||
|
||||
if (localEndpoint.address().is_v4()) {
|
||||
sock.bind(localEndpoint);
|
||||
if (!localEndpoint.address().is_unspecified())
|
||||
sock.set_option(boost::asio::ip::multicast::outbound_interface(localEndpoint.address().to_v4()));
|
||||
}
|
||||
else {
|
||||
sock.set_option(boost::asio::ip::v6_only(true));
|
||||
sock.bind(localEndpoint);
|
||||
if (netif)
|
||||
sock.set_option(boost::asio::ip::multicast::outbound_interface(netif->getIndex()));
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
Transport::EndpointId
|
||||
DatagramTransport<boost::asio::ip::udp, Multicast>::makeEndpointId(const protocol::endpoint& ep)
|
||||
{
|
||||
// IPv6 multicast is not supported
|
||||
BOOST_ASSERT(ep.address().is_v4());
|
||||
|
||||
return (static_cast<uint64_t>(ep.port()) << 32) |
|
||||
static_cast<uint64_t>(ep.address().to_v4().to_ulong());
|
||||
if (ep.address().is_v4()) {
|
||||
return (static_cast<uint64_t>(ep.port()) << 32) |
|
||||
static_cast<uint64_t>(ep.address().to_v4().to_ulong());
|
||||
}
|
||||
else {
|
||||
size_t seed = 0;
|
||||
const auto& addrBytes = ep.address().to_v6().to_bytes();
|
||||
boost::hash_range(seed, addrBytes.begin(), addrBytes.end());
|
||||
boost::hash_combine(seed, ep.port());
|
||||
return seed;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace face
|
||||
|
||||
@@ -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-2018, Regents of the University of California,
|
||||
* Arizona Board of Regents,
|
||||
* Colorado State University,
|
||||
* University Pierre & Marie Curie, Sorbonne University,
|
||||
@@ -28,6 +28,8 @@
|
||||
|
||||
#include "datagram-transport.hpp"
|
||||
|
||||
#include <ndn-cxx/net/network-interface.hpp>
|
||||
|
||||
namespace nfd {
|
||||
namespace face {
|
||||
|
||||
@@ -42,31 +44,52 @@ DatagramTransport<boost::asio::ip::udp, Multicast>::makeEndpointId(const protoco
|
||||
/**
|
||||
* \brief A Transport that communicates on a UDP multicast group
|
||||
*/
|
||||
class MulticastUdpTransport DECL_CLASS_FINAL : public DatagramTransport<boost::asio::ip::udp, Multicast>
|
||||
class MulticastUdpTransport final : public DatagramTransport<boost::asio::ip::udp, Multicast>
|
||||
{
|
||||
public:
|
||||
class Error : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
explicit
|
||||
Error(const std::string& what)
|
||||
: std::runtime_error(what)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Creates a UDP-based transport for multicast communication
|
||||
* \param localEndpoint local endpoint
|
||||
* \param multicastGroup multicast group
|
||||
* \param recvSocket socket used to receive packets
|
||||
* \param recvSocket socket used to receive multicast packets
|
||||
* \param sendSocket socket used to send to the multicast group
|
||||
* \param linkType either `ndn::nfd::LINK_TYPE_MULTI_ACCESS` or `ndn::nfd::LINK_TYPE_AD_HOC`
|
||||
*/
|
||||
MulticastUdpTransport(const protocol::endpoint& localEndpoint,
|
||||
const protocol::endpoint& multicastGroup,
|
||||
MulticastUdpTransport(const protocol::endpoint& multicastGroup,
|
||||
protocol::socket&& recvSocket,
|
||||
protocol::socket&& sendSocket);
|
||||
protocol::socket&& sendSocket,
|
||||
ndn::nfd::LinkType linkType);
|
||||
|
||||
protected:
|
||||
virtual void
|
||||
beforeChangePersistency(ndn::nfd::FacePersistency newPersistency) DECL_FINAL;
|
||||
ssize_t
|
||||
getSendQueueLength() final;
|
||||
|
||||
static void
|
||||
openRxSocket(protocol::socket& sock,
|
||||
const protocol::endpoint& multicastGroup,
|
||||
const boost::asio::ip::address& localAddress,
|
||||
const shared_ptr<const ndn::net::NetworkInterface>& netif = nullptr);
|
||||
|
||||
static void
|
||||
openTxSocket(protocol::socket& sock,
|
||||
const protocol::endpoint& localEndpoint,
|
||||
const shared_ptr<const ndn::net::NetworkInterface>& netif = nullptr,
|
||||
bool enableLoopback = false);
|
||||
|
||||
private:
|
||||
virtual void
|
||||
doSend(Transport::Packet&& packet) DECL_FINAL;
|
||||
void
|
||||
doSend(Transport::Packet&& packet) final;
|
||||
|
||||
virtual void
|
||||
doClose() DECL_FINAL;
|
||||
void
|
||||
doClose() final;
|
||||
|
||||
private:
|
||||
protocol::endpoint m_multicastGroup;
|
||||
|
||||
@@ -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,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,
|
||||
@@ -23,36 +23,48 @@
|
||||
* NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "broadcast-strategy.hpp"
|
||||
#include "protocol-factory.hpp"
|
||||
#include <boost/range/adaptor/map.hpp>
|
||||
#include <boost/range/algorithm/copy.hpp>
|
||||
|
||||
namespace nfd {
|
||||
namespace fw {
|
||||
namespace face {
|
||||
|
||||
NFD_LOG_INIT("BroadcastStrategy");
|
||||
|
||||
const Name BroadcastStrategy::STRATEGY_NAME("ndn:/localhost/nfd/strategy/broadcast/%FD%02");
|
||||
NFD_REGISTER_STRATEGY(BroadcastStrategy);
|
||||
|
||||
BroadcastStrategy::BroadcastStrategy(Forwarder& forwarder, const Name& name)
|
||||
: MulticastStrategy(forwarder, name)
|
||||
, m_isFirstUse(true)
|
||||
ProtocolFactory::Registry&
|
||||
ProtocolFactory::getRegistry()
|
||||
{
|
||||
static Registry registry;
|
||||
return registry;
|
||||
}
|
||||
|
||||
void
|
||||
BroadcastStrategy::afterReceiveInterest(const Face& inFace,
|
||||
const Interest& interest,
|
||||
shared_ptr<fib::Entry> fibEntry,
|
||||
shared_ptr<pit::Entry> pitEntry)
|
||||
unique_ptr<ProtocolFactory>
|
||||
ProtocolFactory::create(const std::string& id, const CtorParams& params)
|
||||
{
|
||||
if (m_isFirstUse) {
|
||||
NFD_LOG_WARN("The broadcast strategy has been renamed as multicast strategy. "
|
||||
"Use ndn:/localhost/nfd/strategy/multicast to select the multicast strategy.");
|
||||
m_isFirstUse = false;
|
||||
Registry& registry = getRegistry();
|
||||
auto found = registry.find(id);
|
||||
if (found == registry.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
this->MulticastStrategy::afterReceiveInterest(inFace, interest, fibEntry, pitEntry);
|
||||
return found->second(params);
|
||||
}
|
||||
|
||||
} // namespace fw
|
||||
std::set<std::string>
|
||||
ProtocolFactory::listRegistered()
|
||||
{
|
||||
std::set<std::string> factoryIds;
|
||||
boost::copy(getRegistry() | boost::adaptors::map_keys,
|
||||
std::inserter(factoryIds, factoryIds.end()));
|
||||
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,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-2018, Regents of the University of California,
|
||||
* Arizona Board of Regents,
|
||||
* Colorado State University,
|
||||
* University Pierre & Marie Curie, Sorbonne University,
|
||||
@@ -27,18 +27,66 @@
|
||||
#define NFD_DAEMON_FACE_PROTOCOL_FACTORY_HPP
|
||||
|
||||
#include "channel.hpp"
|
||||
#include "face-system.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 Abstract base class for all protocol factories
|
||||
/** \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.
|
||||
*/
|
||||
class ProtocolFactory
|
||||
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.
|
||||
* It can process a subsection of face_system config section and create channels and multicast
|
||||
* faces accordingly.
|
||||
*/
|
||||
class ProtocolFactory : noncopyable
|
||||
{
|
||||
public: // registry
|
||||
using CtorParams = ProtocolFactoryCtorParams;
|
||||
|
||||
/** \brief Register a protocol factory type
|
||||
* \tparam S subclass of ProtocolFactory
|
||||
* \param id factory identifier
|
||||
*/
|
||||
template<typename PF>
|
||||
static void
|
||||
registerType(const std::string& id = PF::getId())
|
||||
{
|
||||
Registry& registry = getRegistry();
|
||||
BOOST_ASSERT(registry.count(id) == 0);
|
||||
registry[id] = &make_unique<PF, const CtorParams&>;
|
||||
}
|
||||
|
||||
/** \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, const CtorParams& params);
|
||||
|
||||
/** \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
|
||||
{
|
||||
@@ -50,24 +98,111 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
/** \brief Try to create Face using the supplied FaceUri
|
||||
virtual
|
||||
~ProtocolFactory() = default;
|
||||
|
||||
#ifdef DOXYGEN
|
||||
/** \brief Get id for this ProtocolFactory
|
||||
*
|
||||
* This method should automatically choose channel, based on supplied FaceUri
|
||||
* and create face.
|
||||
* face_system.factory-id config section is processed by the protocol factory.
|
||||
*/
|
||||
static const std::string&
|
||||
getId();
|
||||
#endif
|
||||
|
||||
/** \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
|
||||
*
|
||||
* \throw Error Factory does not support connect operation
|
||||
* \throw Error specified \p persistency is not supported
|
||||
* This function updates \p providedSchemes
|
||||
*/
|
||||
virtual void
|
||||
createFace(const FaceUri& uri,
|
||||
ndn::nfd::FacePersistency persistency,
|
||||
processConfig(OptionalConfigSection configSection,
|
||||
FaceSystem::ConfigContext& context) = 0;
|
||||
|
||||
/** \brief Get FaceUri schemes accepted by this ProtocolFactory
|
||||
*/
|
||||
const std::set<std::string>&
|
||||
getProvidedSchemes()
|
||||
{
|
||||
return providedSchemes;
|
||||
}
|
||||
|
||||
/** \brief Encapsulates a face creation request and all its parameters
|
||||
*
|
||||
* 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 CreateFaceRequest
|
||||
{
|
||||
FaceUri remoteUri;
|
||||
ndn::optional<FaceUri> localUri;
|
||||
FaceParams params;
|
||||
};
|
||||
|
||||
/** \brief Try to create a unicast face using the supplied parameters
|
||||
*
|
||||
* \param req request object containing the face creation parameters
|
||||
* \param onCreated callback if face creation succeeds or face already exists; the settings
|
||||
* of an existing face are not updated if they differ from the request
|
||||
* \param onFailure callback if face creation fails
|
||||
*/
|
||||
virtual void
|
||||
createFace(const CreateFaceRequest& req,
|
||||
const FaceCreatedCallback& onCreated,
|
||||
const FaceCreationFailedCallback& onConnectFailed) = 0;
|
||||
const FaceCreationFailedCallback& onFailure) = 0;
|
||||
|
||||
virtual std::vector<shared_ptr<const Channel>>
|
||||
getChannels() const = 0;
|
||||
|
||||
protected:
|
||||
explicit
|
||||
ProtocolFactory(const CtorParams& params);
|
||||
|
||||
template<typename ChannelMap>
|
||||
static std::vector<shared_ptr<const Channel>>
|
||||
getChannelsFromMap(const ChannelMap& channelMap)
|
||||
{
|
||||
std::vector<shared_ptr<const Channel>> channels;
|
||||
boost::copy(channelMap | boost::adaptors::map_values, std::back_inserter(channels));
|
||||
return channels;
|
||||
}
|
||||
|
||||
private: // registry
|
||||
using CreateFunc = std::function<unique_ptr<ProtocolFactory>(const CtorParams&)>;
|
||||
using Registry = std::map<std::string, CreateFunc>; // indexed by factory id
|
||||
|
||||
static Registry&
|
||||
getRegistry();
|
||||
|
||||
protected:
|
||||
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.
|
||||
*/
|
||||
shared_ptr<ndn::net::NetworkMonitor> netmon;
|
||||
};
|
||||
|
||||
} // namespace face
|
||||
} // namespace nfd
|
||||
|
||||
/** \brief registers a protocol factory
|
||||
*
|
||||
* This macro should appear once in .cpp of each protocol factory.
|
||||
*/
|
||||
#define NFD_REGISTER_PROTOCOL_FACTORY(PF) \
|
||||
static class NfdAuto ## PF ## ProtocolFactoryRegistrationClass \
|
||||
{ \
|
||||
public: \
|
||||
NfdAuto ## PF ## ProtocolFactoryRegistrationClass() \
|
||||
{ \
|
||||
::nfd::face::ProtocolFactory::registerType<PF>(); \
|
||||
} \
|
||||
} g_nfdAuto ## PF ## ProtocolFactoryRegistrationVariable
|
||||
|
||||
#endif // NFD_DAEMON_FACE_PROTOCOL_FACTORY_HPP
|
||||
@@ -0,0 +1,57 @@
|
||||
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
|
||||
/*
|
||||
* Copyright (c) 2014-2018, 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 "socket-utils.hpp"
|
||||
#include "transport.hpp"
|
||||
|
||||
#if defined(__linux__)
|
||||
#include <linux/sockios.h>
|
||||
#include <sys/ioctl.h>
|
||||
#elif defined(__APPLE__)
|
||||
#include <sys/socket.h>
|
||||
#endif
|
||||
|
||||
namespace nfd {
|
||||
namespace face {
|
||||
|
||||
ssize_t
|
||||
getTxQueueLength(int fd)
|
||||
{
|
||||
int queueLength = QUEUE_UNSUPPORTED;
|
||||
#if defined(__linux__)
|
||||
if (ioctl(fd, SIOCOUTQ, &queueLength) < 0) {
|
||||
queueLength = QUEUE_ERROR;
|
||||
}
|
||||
#elif defined(__APPLE__)
|
||||
socklen_t queueLengthSize = sizeof(queueLength);
|
||||
if (getsockopt(fd, SOL_SOCKET, SO_NWRITE, &queueLength, &queueLengthSize) < 0) {
|
||||
queueLength = QUEUE_ERROR;
|
||||
}
|
||||
#endif
|
||||
return queueLength;
|
||||
}
|
||||
|
||||
} // namespace face
|
||||
} // namespace nfd
|
||||
@@ -0,0 +1,48 @@
|
||||
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
|
||||
/*
|
||||
* Copyright (c) 2014-2018, 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_SOCKET_UTILS_HPP
|
||||
#define NFD_DAEMON_FACE_SOCKET_UTILS_HPP
|
||||
|
||||
#include "core/common.hpp"
|
||||
|
||||
namespace nfd {
|
||||
namespace face {
|
||||
|
||||
/** \brief obtain send queue length from a specified system socket
|
||||
* \param fd file descriptor of the socket
|
||||
* \retval QUEUE_UNSUPPORTED this operation is unsupported on the current platform
|
||||
* \retval QUEUE_ERROR there was an error retrieving the send queue length
|
||||
*
|
||||
* On Linux, ioctl() is used to obtain the value of SIOCOUTQ from the socket.
|
||||
* On macOS, getsockopt() is used to obtain the value of SO_NWRITE from the socket.
|
||||
*/
|
||||
ssize_t
|
||||
getTxQueueLength(int fd);
|
||||
|
||||
} // namespace face
|
||||
} // namespace nfd
|
||||
|
||||
#endif // NFD_DAEMON_FACE_SOCKET_UTILS_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-2018, 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_FACE_STREAM_TRANSPORT_HPP
|
||||
|
||||
#include "transport.hpp"
|
||||
#include "socket-utils.hpp"
|
||||
#include "core/global-io.hpp"
|
||||
|
||||
#include <queue>
|
||||
@@ -51,15 +52,18 @@ public:
|
||||
explicit
|
||||
StreamTransport(typename protocol::socket&& socket);
|
||||
|
||||
ssize_t
|
||||
getSendQueueLength() override;
|
||||
|
||||
protected:
|
||||
virtual void
|
||||
doClose() DECL_OVERRIDE;
|
||||
void
|
||||
doClose() override;
|
||||
|
||||
void
|
||||
deferredClose();
|
||||
|
||||
virtual void
|
||||
doSend(Transport::Packet&& packet) DECL_OVERRIDE;
|
||||
void
|
||||
doSend(Transport::Packet&& packet) override;
|
||||
|
||||
void
|
||||
sendFromQueue();
|
||||
@@ -68,6 +72,9 @@ protected:
|
||||
handleSend(const boost::system::error_code& error,
|
||||
size_t nBytesSent);
|
||||
|
||||
void
|
||||
startReceive();
|
||||
|
||||
void
|
||||
handleReceive(const boost::system::error_code& error,
|
||||
size_t nBytesReceived);
|
||||
@@ -75,6 +82,18 @@ protected:
|
||||
void
|
||||
processErrorCode(const boost::system::error_code& error);
|
||||
|
||||
virtual void
|
||||
handleError(const boost::system::error_code& error);
|
||||
|
||||
void
|
||||
resetReceiveBuffer();
|
||||
|
||||
void
|
||||
resetSendQueue();
|
||||
|
||||
size_t
|
||||
getSendQueueBytes() const;
|
||||
|
||||
protected:
|
||||
typename protocol::socket m_socket;
|
||||
|
||||
@@ -84,6 +103,7 @@ private:
|
||||
uint8_t m_receiveBuffer[ndn::MAX_NDN_PACKET_SIZE];
|
||||
size_t m_receiveBufferSize;
|
||||
std::queue<Block> m_sendQueue;
|
||||
size_t m_sendQueueBytes;
|
||||
};
|
||||
|
||||
|
||||
@@ -91,11 +111,24 @@ template<class T>
|
||||
StreamTransport<T>::StreamTransport(typename StreamTransport::protocol::socket&& socket)
|
||||
: m_socket(std::move(socket))
|
||||
, m_receiveBufferSize(0)
|
||||
, m_sendQueueBytes(0)
|
||||
{
|
||||
m_socket.async_receive(boost::asio::buffer(m_receiveBuffer, ndn::MAX_NDN_PACKET_SIZE),
|
||||
bind(&StreamTransport<T>::handleReceive, this,
|
||||
boost::asio::placeholders::error,
|
||||
boost::asio::placeholders::bytes_transferred));
|
||||
// No queue capacity is set because there is no theoretical limit to the size of m_sendQueue.
|
||||
// Therefore, protecting against send queue overflows is less critical than in other transport
|
||||
// types. Instead, we use the default threshold specified in the GenericLinkService options.
|
||||
|
||||
startReceive();
|
||||
}
|
||||
|
||||
template<class T>
|
||||
ssize_t
|
||||
StreamTransport<T>::getSendQueueLength()
|
||||
{
|
||||
ssize_t queueLength = getTxQueueLength(m_socket.native_handle());
|
||||
if (queueLength == QUEUE_ERROR) {
|
||||
NFD_LOG_FACE_WARN("Failed to obtain send queue length from socket: " << std::strerror(errno));
|
||||
}
|
||||
return getSendQueueBytes() + std::max<ssize_t>(0, queueLength);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
@@ -115,9 +148,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.
|
||||
@@ -136,9 +169,7 @@ StreamTransport<T>::deferredClose()
|
||||
{
|
||||
NFD_LOG_FACE_TRACE(__func__);
|
||||
|
||||
// clear send queue
|
||||
std::queue<Block> emptyQueue;
|
||||
std::swap(emptyQueue, m_sendQueue);
|
||||
resetSendQueue();
|
||||
|
||||
// use the non-throwing variant and ignore errors, if any
|
||||
boost::system::error_code error;
|
||||
@@ -153,8 +184,12 @@ StreamTransport<T>::doSend(Transport::Packet&& packet)
|
||||
{
|
||||
NFD_LOG_FACE_TRACE(__func__);
|
||||
|
||||
if (getState() != TransportState::UP)
|
||||
return;
|
||||
|
||||
bool wasQueueEmpty = m_sendQueue.empty();
|
||||
m_sendQueue.push(packet.packet);
|
||||
m_sendQueueBytes += packet.packet.size();
|
||||
|
||||
if (wasQueueEmpty)
|
||||
sendFromQueue();
|
||||
@@ -181,12 +216,27 @@ StreamTransport<T>::handleSend(const boost::system::error_code& error,
|
||||
NFD_LOG_FACE_TRACE("Successfully sent: " << nBytesSent << " bytes");
|
||||
|
||||
BOOST_ASSERT(!m_sendQueue.empty());
|
||||
BOOST_ASSERT(m_sendQueue.front().size() == nBytesSent);
|
||||
m_sendQueueBytes -= nBytesSent;
|
||||
m_sendQueue.pop();
|
||||
|
||||
if (!m_sendQueue.empty())
|
||||
sendFromQueue();
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void
|
||||
StreamTransport<T>::startReceive()
|
||||
{
|
||||
BOOST_ASSERT(getState() == TransportState::UP);
|
||||
|
||||
m_socket.async_receive(boost::asio::buffer(m_receiveBuffer + m_receiveBufferSize,
|
||||
ndn::MAX_NDN_PACKET_SIZE - m_receiveBufferSize),
|
||||
bind(&StreamTransport<T>::handleReceive, this,
|
||||
boost::asio::placeholders::error,
|
||||
boost::asio::placeholders::bytes_transferred));
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void
|
||||
StreamTransport<T>::handleReceive(const boost::system::error_code& error,
|
||||
@@ -198,12 +248,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;
|
||||
@@ -215,7 +263,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;
|
||||
@@ -231,11 +279,7 @@ StreamTransport<T>::handleReceive(const boost::system::error_code& error,
|
||||
}
|
||||
}
|
||||
|
||||
m_socket.async_receive(boost::asio::buffer(m_receiveBuffer + m_receiveBufferSize,
|
||||
ndn::MAX_NDN_PACKET_SIZE - m_receiveBufferSize),
|
||||
bind(&StreamTransport<T>::handleReceive, this,
|
||||
boost::asio::placeholders::error,
|
||||
boost::asio::placeholders::bytes_transferred));
|
||||
startReceive();
|
||||
}
|
||||
|
||||
template<class T>
|
||||
@@ -252,13 +296,46 @@ StreamTransport<T>::processErrorCode(const boost::system::error_code& error)
|
||||
// transport is shutting down, ignore any errors
|
||||
return;
|
||||
|
||||
if (error != boost::asio::error::eof)
|
||||
NFD_LOG_FACE_WARN("Send or receive operation failed: " << error.message());
|
||||
handleError(error);
|
||||
}
|
||||
|
||||
this->setState(TransportState::FAILED);
|
||||
template<class T>
|
||||
void
|
||||
StreamTransport<T>::handleError(const boost::system::error_code& error)
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void
|
||||
StreamTransport<T>::resetReceiveBuffer()
|
||||
{
|
||||
m_receiveBufferSize = 0;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void
|
||||
StreamTransport<T>::resetSendQueue()
|
||||
{
|
||||
std::queue<Block> emptyQueue;
|
||||
std::swap(emptyQueue, m_sendQueue);
|
||||
m_sendQueueBytes = 0;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
size_t
|
||||
StreamTransport<T>::getSendQueueBytes() const
|
||||
{
|
||||
return m_sendQueueBytes;
|
||||
}
|
||||
|
||||
} // namespace face
|
||||
} // namespace nfd
|
||||
|
||||
|
||||
+70
-58
@@ -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-2018, Regents of the University of California,
|
||||
* Arizona Board of Regents,
|
||||
* Colorado State University,
|
||||
* University Pierre & Marie Curie, Sorbonne University,
|
||||
@@ -29,17 +29,20 @@
|
||||
#include "core/global-io.hpp"
|
||||
|
||||
namespace nfd {
|
||||
namespace face {
|
||||
|
||||
NFD_LOG_INIT("TcpChannel");
|
||||
|
||||
namespace ip = boost::asio::ip;
|
||||
|
||||
TcpChannel::TcpChannel(const tcp::Endpoint& localEndpoint)
|
||||
TcpChannel::TcpChannel(const tcp::Endpoint& localEndpoint, bool wantCongestionMarking)
|
||||
: m_localEndpoint(localEndpoint)
|
||||
, m_acceptor(getGlobalIoService())
|
||||
, m_acceptSocket(getGlobalIoService())
|
||||
, m_socket(getGlobalIoService())
|
||||
, m_wantCongestionMarking(wantCongestionMarking)
|
||||
{
|
||||
setUri(FaceUri(m_localEndpoint));
|
||||
NFD_LOG_CHAN_INFO("Creating channel");
|
||||
}
|
||||
|
||||
void
|
||||
@@ -48,78 +51,87 @@ 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,
|
||||
const FaceParams& params,
|
||||
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, connectTimeoutEvent,
|
||||
onFaceCreated, onConnectFailed));
|
||||
}
|
||||
|
||||
size_t
|
||||
TcpChannel::size() const
|
||||
{
|
||||
return m_channelFaces.size();
|
||||
boost::asio::placeholders::error, remoteEndpoint, clientSocket,
|
||||
params, timeoutEvent, onFaceCreated, onConnectFailed));
|
||||
}
|
||||
|
||||
void
|
||||
TcpChannel::createFace(ip::tcp::socket&& socket,
|
||||
const FaceCreatedCallback& onFaceCreated,
|
||||
bool isOnDemand)
|
||||
const FaceParams& params,
|
||||
const FaceCreatedCallback& onFaceCreated)
|
||||
{
|
||||
shared_ptr<Face> face;
|
||||
tcp::Endpoint remoteEndpoint = socket.remote_endpoint();
|
||||
|
||||
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 transport = make_unique<face::TcpTransport>(std::move(socket), persistency);
|
||||
GenericLinkService::Options options;
|
||||
options.allowLocalFields = params.wantLocalFields;
|
||||
options.reliabilityOptions.isEnabled = params.wantLpReliability;
|
||||
|
||||
if (boost::logic::indeterminate(params.wantCongestionMarking)) {
|
||||
// Use default value for this channel if parameter is indeterminate
|
||||
options.allowCongestionMarking = m_wantCongestionMarking;
|
||||
}
|
||||
else {
|
||||
options.allowCongestionMarking = params.wantCongestionMarking;
|
||||
}
|
||||
|
||||
if (params.baseCongestionMarkingInterval) {
|
||||
options.baseCongestionMarkingInterval = *params.baseCongestionMarkingInterval;
|
||||
}
|
||||
if (params.defaultCongestionThreshold) {
|
||||
options.defaultCongestionThreshold = *params.defaultCongestionThreshold;
|
||||
}
|
||||
|
||||
auto linkService = make_unique<GenericLinkService>(options);
|
||||
auto transport = make_unique<TcpTransport>(std::move(socket), params.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);
|
||||
@@ -135,9 +147,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
|
||||
@@ -146,18 +158,19 @@ 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(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());
|
||||
NFD_LOG_CHAN_TRACE("Incoming connection from " << m_socket.remote_endpoint());
|
||||
|
||||
createFace(std::move(m_acceptSocket), onFaceCreated, true);
|
||||
FaceParams params;
|
||||
params.persistency = ndn::nfd::FACE_PERSISTENCY_ON_DEMAND;
|
||||
createFace(std::move(m_socket), params, onFaceCreated);
|
||||
|
||||
// prepare accepting the next connection
|
||||
accept(onFaceCreated, onAcceptFailed);
|
||||
@@ -165,7 +178,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,
|
||||
const FaceParams& params,
|
||||
const scheduler::EventId& connectTimeoutEvent,
|
||||
const FaceCreatedCallback& onFaceCreated,
|
||||
const FaceCreationFailedCallback& onConnectFailed)
|
||||
@@ -181,35 +196,32 @@ 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(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), onFaceCreated, false);
|
||||
NFD_LOG_CHAN_TRACE("Connected to " << socket->remote_endpoint());
|
||||
createFace(std::move(*socket), params, 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("Connect to remote endpoint timed out");
|
||||
onConnectFailed(504, "Connection timed out");
|
||||
}
|
||||
|
||||
} // namespace face
|
||||
} // namespace nfd
|
||||
+31
-28
@@ -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-2018, 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
|
||||
*
|
||||
@@ -51,8 +53,19 @@ public:
|
||||
* To enable creation faces upon incoming connections,
|
||||
* one needs to explicitly call TcpChannel::listen method.
|
||||
*/
|
||||
explicit
|
||||
TcpChannel(const tcp::Endpoint& localEndpoint);
|
||||
TcpChannel(const tcp::Endpoint& localEndpoint, bool wantCongestionMarking);
|
||||
|
||||
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,
|
||||
@@ -69,28 +82,20 @@ 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,
|
||||
const FaceParams& params,
|
||||
const FaceCreatedCallback& onFaceCreated,
|
||||
const FaceCreationFailedCallback& onConnectFailed,
|
||||
const time::seconds& timeout = time::seconds(4));
|
||||
|
||||
/**
|
||||
* \brief Get number of faces in the channel
|
||||
*/
|
||||
size_t
|
||||
size() const;
|
||||
|
||||
bool
|
||||
isListening() const;
|
||||
time::nanoseconds timeout = 8_s);
|
||||
|
||||
private:
|
||||
void
|
||||
createFace(boost::asio::ip::tcp::socket&& socket,
|
||||
const FaceCreatedCallback& onFaceCreated,
|
||||
bool isOnDemand);
|
||||
const FaceParams& params,
|
||||
const FaceCreatedCallback& onFaceCreated);
|
||||
|
||||
void
|
||||
accept(const FaceCreatedCallback& onFaceCreated,
|
||||
@@ -103,29 +108,27 @@ private:
|
||||
|
||||
void
|
||||
handleConnect(const boost::system::error_code& error,
|
||||
const tcp::Endpoint& remoteEndpoint,
|
||||
const shared_ptr<boost::asio::ip::tcp::socket>& socket,
|
||||
const FaceParams& 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;
|
||||
bool m_wantCongestionMarking;
|
||||
};
|
||||
|
||||
inline bool
|
||||
TcpChannel::isListening() const
|
||||
{
|
||||
return m_acceptor.is_open();
|
||||
}
|
||||
|
||||
} // namespace face
|
||||
} // namespace nfd
|
||||
|
||||
#endif // NFD_DAEMON_FACE_TCP_CHANNEL_HPP
|
||||
+126
-86
@@ -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-2018, Regents of the University of California,
|
||||
* Arizona Board of Regents,
|
||||
* Colorado State University,
|
||||
* University Pierre & Marie Curie, Sorbonne University,
|
||||
@@ -24,96 +24,138 @@
|
||||
*/
|
||||
|
||||
#include "tcp-factory.hpp"
|
||||
#include "core/logger.hpp"
|
||||
#include "core/network-interface.hpp"
|
||||
|
||||
#include <ndn-cxx/net/address-converter.hpp>
|
||||
|
||||
namespace nfd {
|
||||
namespace face {
|
||||
|
||||
namespace ip = boost::asio::ip;
|
||||
|
||||
NFD_LOG_INIT("TcpFactory");
|
||||
NFD_REGISTER_PROTOCOL_FACTORY(TcpFactory);
|
||||
|
||||
void
|
||||
TcpFactory::prohibitEndpoint(const tcp::Endpoint& endpoint)
|
||||
const std::string&
|
||||
TcpFactory::getId()
|
||||
{
|
||||
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());
|
||||
}
|
||||
static std::string id("tcp");
|
||||
return id;
|
||||
}
|
||||
|
||||
NFD_LOG_TRACE("prohibiting TCP " << endpoint);
|
||||
m_prohibitedEndpoints.insert(endpoint);
|
||||
TcpFactory::TcpFactory(const CtorParams& params)
|
||||
: ProtocolFactory(params)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
TcpFactory::prohibitAllIpv4Endpoints(uint16_t port)
|
||||
TcpFactory::processConfig(OptionalConfigSection configSection,
|
||||
FaceSystem::ConfigContext& context)
|
||||
{
|
||||
for (const NetworkInterfaceInfo& nic : listNetworkInterfaces()) {
|
||||
for (const auto& addr : nic.ipv4Addresses) {
|
||||
if (addr != ip::address_v4::any()) {
|
||||
prohibitEndpoint(tcp::Endpoint(addr, port));
|
||||
// tcp
|
||||
// {
|
||||
// listen yes
|
||||
// port 6363
|
||||
// enable_v4 yes
|
||||
// enable_v6 yes
|
||||
// }
|
||||
|
||||
m_wantCongestionMarking = context.generalConfig.wantCongestionMarking;
|
||||
|
||||
if (!configSection) {
|
||||
if (!context.isDryRun && !m_channels.empty()) {
|
||||
NFD_LOG_WARN("Cannot disable tcp4 and tcp6 channels after initialization");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
bool wantListen = true;
|
||||
uint16_t port = 6363;
|
||||
bool enableV4 = true;
|
||||
bool enableV6 = true;
|
||||
|
||||
for (const auto& pair : *configSection) {
|
||||
const std::string& key = pair.first;
|
||||
|
||||
if (key == "listen") {
|
||||
wantListen = ConfigFile::parseYesNo(pair, "face_system.tcp");
|
||||
}
|
||||
else if (key == "port") {
|
||||
port = ConfigFile::parseNumber<uint16_t>(pair, "face_system.tcp");
|
||||
}
|
||||
else if (key == "enable_v4") {
|
||||
enableV4 = ConfigFile::parseYesNo(pair, "face_system.tcp");
|
||||
}
|
||||
else if (key == "enable_v6") {
|
||||
enableV6 = ConfigFile::parseYesNo(pair, "face_system.tcp");
|
||||
}
|
||||
else {
|
||||
BOOST_THROW_EXCEPTION(ConfigFile::Error("Unrecognized option face_system.tcp." + key));
|
||||
}
|
||||
}
|
||||
|
||||
if (!enableV4 && !enableV6) {
|
||||
BOOST_THROW_EXCEPTION(ConfigFile::Error(
|
||||
"IPv4 and IPv6 TCP channels have been disabled. Remove face_system.tcp section to disable "
|
||||
"TCP channels or enable at least one channel type."));
|
||||
}
|
||||
|
||||
if (!context.isDryRun) {
|
||||
providedSchemes.insert("tcp");
|
||||
|
||||
if (enableV4) {
|
||||
tcp::Endpoint endpoint(ip::tcp::v4(), port);
|
||||
shared_ptr<TcpChannel> v4Channel = this->createChannel(endpoint);
|
||||
if (wantListen && !v4Channel->isListening()) {
|
||||
v4Channel->listen(this->addFace, nullptr);
|
||||
}
|
||||
providedSchemes.insert("tcp4");
|
||||
}
|
||||
else if (providedSchemes.count("tcp4") > 0) {
|
||||
NFD_LOG_WARN("Cannot close tcp4 channel after its creation");
|
||||
}
|
||||
|
||||
if (enableV6) {
|
||||
tcp::Endpoint endpoint(ip::tcp::v6(), port);
|
||||
shared_ptr<TcpChannel> v6Channel = this->createChannel(endpoint);
|
||||
if (wantListen && !v6Channel->isListening()) {
|
||||
v6Channel->listen(this->addFace, nullptr);
|
||||
}
|
||||
providedSchemes.insert("tcp6");
|
||||
}
|
||||
else if (providedSchemes.count("tcp6") > 0) {
|
||||
NFD_LOG_WARN("Cannot close tcp6 channel after its creation");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TcpFactory::prohibitAllIpv6Endpoints(uint16_t port)
|
||||
{
|
||||
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;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void
|
||||
TcpFactory::createFace(const FaceUri& uri,
|
||||
ndn::nfd::FacePersistency persistency,
|
||||
TcpFactory::createFace(const CreateFaceRequest& req,
|
||||
const FaceCreatedCallback& onCreated,
|
||||
const FaceCreationFailedCallback& onConnectFailed)
|
||||
const FaceCreationFailedCallback& onFailure)
|
||||
{
|
||||
BOOST_ASSERT(uri.isCanonical());
|
||||
BOOST_ASSERT(req.remoteUri.isCanonical());
|
||||
|
||||
if (persistency != ndn::nfd::FACE_PERSISTENCY_PERSISTENT) {
|
||||
BOOST_THROW_EXCEPTION(Error("TcpFactory::createFace supports only FACE_PERSISTENCY_PERSISTENT"));
|
||||
if (req.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 (req.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 (m_prohibitedEndpoints.find(endpoint) != m_prohibitedEndpoints.end()) {
|
||||
onConnectFailed("Requested endpoint is prohibited "
|
||||
"(reserved by this NFD or disallowed by face management protocol)");
|
||||
tcp::Endpoint endpoint(ndn::ip::addressFromString(req.remoteUri.getHost()),
|
||||
boost::lexical_cast<uint16_t>(req.remoteUri.getPort()));
|
||||
|
||||
// a canonical tcp4/tcp6 FaceUri cannot have a multicast address
|
||||
BOOST_ASSERT(!endpoint.address().is_multicast());
|
||||
|
||||
if (req.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;
|
||||
}
|
||||
|
||||
@@ -121,34 +163,32 @@ 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, onCreated, onConnectFailed);
|
||||
i.second->connect(endpoint, req.params, onCreated, onFailure);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
onConnectFailed("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");
|
||||
}
|
||||
|
||||
shared_ptr<TcpChannel>
|
||||
TcpFactory::createChannel(const tcp::Endpoint& endpoint)
|
||||
{
|
||||
auto it = m_channels.find(endpoint);
|
||||
if (it != m_channels.end())
|
||||
return it->second;
|
||||
|
||||
auto channel = make_shared<TcpChannel>(endpoint, m_wantCongestionMarking);
|
||||
m_channels[endpoint] = channel;
|
||||
return channel;
|
||||
}
|
||||
|
||||
std::vector<shared_ptr<const Channel>>
|
||||
TcpFactory::getChannels() const
|
||||
{
|
||||
std::vector<shared_ptr<const Channel>> channels;
|
||||
channels.reserve(m_channels.size());
|
||||
|
||||
for (const auto& i : m_channels)
|
||||
channels.push_back(i.second);
|
||||
|
||||
return 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;
|
||||
return getChannelsFromMap(m_channels);
|
||||
}
|
||||
|
||||
} // namespace face
|
||||
} // namespace nfd
|
||||
+28
-66
@@ -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-2018, Regents of the University of California,
|
||||
* Arizona Board of Regents,
|
||||
* Colorado State University,
|
||||
* University Pierre & Marie Curie, Sorbonne University,
|
||||
@@ -30,91 +30,53 @@
|
||||
#include "tcp-channel.hpp"
|
||||
|
||||
namespace nfd {
|
||||
namespace face {
|
||||
|
||||
/** \brief protocol factory for TCP over IPv4 and IPv6
|
||||
*/
|
||||
class TcpFactory : public ProtocolFactory
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* \brief Exception of TcpFactory
|
||||
static const std::string&
|
||||
getId();
|
||||
|
||||
explicit
|
||||
TcpFactory(const CtorParams& params);
|
||||
|
||||
/** \brief process face_system.tcp config section
|
||||
*/
|
||||
class Error : public ProtocolFactory::Error
|
||||
{
|
||||
public:
|
||||
explicit
|
||||
Error(const std::string& what)
|
||||
: ProtocolFactory::Error(what)
|
||||
{
|
||||
}
|
||||
};
|
||||
void
|
||||
processConfig(OptionalConfigSection configSection,
|
||||
FaceSystem::ConfigContext& context) override;
|
||||
|
||||
void
|
||||
createFace(const CreateFaceRequest& req,
|
||||
const FaceCreatedCallback& onCreated,
|
||||
const FaceCreationFailedCallback& onFailure) override;
|
||||
|
||||
/**
|
||||
* \brief Create TCP-based channel using tcp::Endpoint
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* \throws TcpFactory::Error
|
||||
*
|
||||
* \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 TcpFactory::Error or std::runtime_error
|
||||
*/
|
||||
shared_ptr<TcpChannel>
|
||||
createChannel(const std::string& localIp, const std::string& localPort);
|
||||
|
||||
public: // from ProtocolFactory
|
||||
virtual void
|
||||
createFace(const FaceUri& uri,
|
||||
ndn::nfd::FacePersistency persistency,
|
||||
const FaceCreatedCallback& onCreated,
|
||||
const FaceCreationFailedCallback& onConnectFailed) DECL_OVERRIDE;
|
||||
|
||||
virtual std::vector<shared_ptr<const Channel>>
|
||||
getChannels() const DECL_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;
|
||||
std::vector<shared_ptr<const Channel>>
|
||||
getChannels() const override;
|
||||
|
||||
private:
|
||||
bool m_wantCongestionMarking = false;
|
||||
std::map<tcp::Endpoint, shared_ptr<TcpChannel>> m_channels;
|
||||
|
||||
PUBLIC_WITH_TESTS_ELSE_PRIVATE:
|
||||
std::set<tcp::Endpoint> m_prohibitedEndpoints;
|
||||
};
|
||||
|
||||
} // namespace face
|
||||
} // namespace nfd
|
||||
|
||||
#endif // NFD_DAEMON_FACE_TCP_FACTORY_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-2018, Regents of the University of California,
|
||||
* Arizona Board of Regents,
|
||||
* Colorado State University,
|
||||
* University Pierre & Marie Curie, Sorbonne University,
|
||||
@@ -25,14 +25,24 @@
|
||||
|
||||
#include "tcp-transport.hpp"
|
||||
|
||||
#if defined(__linux__)
|
||||
#include <linux/sockios.h>
|
||||
#include <sys/ioctl.h>
|
||||
#endif
|
||||
|
||||
namespace nfd {
|
||||
namespace face {
|
||||
|
||||
NFD_LOG_INCLASS_TEMPLATE_SPECIALIZATION_DEFINE(StreamTransport, TcpTransport::protocol,
|
||||
"TcpTransport");
|
||||
NFD_LOG_INCLASS_TEMPLATE_SPECIALIZATION_DEFINE(StreamTransport, TcpTransport::protocol, "TcpTransport");
|
||||
|
||||
time::milliseconds TcpTransport::s_initialReconnectWait = time::seconds(1);
|
||||
time::milliseconds TcpTransport::s_maxReconnectWait = time::minutes(5);
|
||||
float TcpTransport::s_reconnectWaitMultiplier = 2.0f;
|
||||
|
||||
TcpTransport::TcpTransport(protocol::socket&& socket, ndn::nfd::FacePersistency persistency)
|
||||
: StreamTransport(std::move(socket))
|
||||
, m_remoteEndpoint(m_socket.remote_endpoint())
|
||||
, m_nextReconnectWait(s_initialReconnectWait)
|
||||
{
|
||||
this->setLocalUri(FaceUri(m_socket.local_endpoint()));
|
||||
this->setRemoteUri(FaceUri(m_socket.remote_endpoint()));
|
||||
@@ -50,13 +60,138 @@ TcpTransport::TcpTransport(protocol::socket&& socket, ndn::nfd::FacePersistency
|
||||
NFD_LOG_FACE_INFO("Creating transport");
|
||||
}
|
||||
|
||||
void
|
||||
TcpTransport::beforeChangePersistency(ndn::nfd::FacePersistency newPersistency)
|
||||
ssize_t
|
||||
TcpTransport::getSendQueueLength()
|
||||
{
|
||||
if (newPersistency == ndn::nfd::FACE_PERSISTENCY_PERMANENT) {
|
||||
BOOST_THROW_EXCEPTION(
|
||||
std::invalid_argument("TcpTransport does not support FACE_PERSISTENCY_PERMANENT"));
|
||||
int queueLength = getSendQueueBytes();
|
||||
|
||||
// We want to obtain the amount of "not sent" bytes instead of the amount of "not sent" + "not
|
||||
// acked" bytes. On Linux, we use SIOCOUTQNSD for this reason. However, macOS does not provide an
|
||||
// efficient mechanism to obtain this value (SO_NWRITE includes both "not sent" and "not acked").
|
||||
#if defined(__linux__)
|
||||
int nsd;
|
||||
if (ioctl(m_socket.native_handle(), SIOCOUTQNSD, &nsd) < 0) {
|
||||
NFD_LOG_FACE_WARN("Failed to obtain send queue length from socket: " << std::strerror(errno));
|
||||
}
|
||||
else if (nsd > 0) {
|
||||
NFD_LOG_FACE_TRACE("SIOCOUTQNSD=" << nsd);
|
||||
queueLength += nsd;
|
||||
}
|
||||
#endif
|
||||
|
||||
return queueLength;
|
||||
}
|
||||
|
||||
bool
|
||||
TcpTransport::canChangePersistencyToImpl(ndn::nfd::FacePersistency newPersistency) const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
TcpTransport::afterChangePersistency(ndn::nfd::FacePersistency oldPersistency)
|
||||
{
|
||||
// if persistency was changed from permanent to any other value
|
||||
if (oldPersistency == ndn::nfd::FACE_PERSISTENCY_PERMANENT) {
|
||||
if (this->getState() == TransportState::DOWN) {
|
||||
// non-permanent transport cannot be in DOWN state, so fail hard
|
||||
this->setState(TransportState::FAILED);
|
||||
doClose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TcpTransport::handleError(const boost::system::error_code& error)
|
||||
{
|
||||
if (this->getPersistency() == ndn::nfd::FACE_PERSISTENCY_PERMANENT) {
|
||||
NFD_LOG_FACE_TRACE("TCP socket error: " << error.message());
|
||||
this->setState(TransportState::DOWN);
|
||||
|
||||
// cancel all outstanding operations
|
||||
boost::system::error_code error;
|
||||
m_socket.cancel(error);
|
||||
|
||||
// do this asynchronously because there could be some callbacks still pending
|
||||
getGlobalIoService().post([this] { reconnect(); });
|
||||
}
|
||||
else {
|
||||
StreamTransport::handleError(error);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TcpTransport::reconnect()
|
||||
{
|
||||
NFD_LOG_FACE_TRACE(__func__);
|
||||
|
||||
if (getState() == TransportState::CLOSING ||
|
||||
getState() == TransportState::FAILED ||
|
||||
getState() == TransportState::CLOSED) {
|
||||
// transport is shutting down, don't attempt to reconnect
|
||||
return;
|
||||
}
|
||||
|
||||
BOOST_ASSERT(getPersistency() == ndn::nfd::FACE_PERSISTENCY_PERMANENT);
|
||||
BOOST_ASSERT(getState() == TransportState::DOWN);
|
||||
|
||||
// recreate the socket
|
||||
m_socket = protocol::socket(m_socket.get_io_service());
|
||||
this->resetReceiveBuffer();
|
||||
this->resetSendQueue();
|
||||
|
||||
m_reconnectEvent = scheduler::schedule(m_nextReconnectWait,
|
||||
[this] { handleReconnectTimeout(); });
|
||||
m_socket.async_connect(m_remoteEndpoint,
|
||||
[this] (const boost::system::error_code& error) { handleReconnect(error); });
|
||||
}
|
||||
|
||||
void
|
||||
TcpTransport::handleReconnect(const boost::system::error_code& error)
|
||||
{
|
||||
if (getState() == TransportState::CLOSING ||
|
||||
getState() == TransportState::FAILED ||
|
||||
getState() == TransportState::CLOSED ||
|
||||
error == boost::asio::error::operation_aborted) {
|
||||
// transport is shutting down, abort the reconnection attempt and ignore any errors
|
||||
return;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
NFD_LOG_FACE_TRACE("Reconnection attempt failed: " << error.message());
|
||||
return;
|
||||
}
|
||||
|
||||
m_reconnectEvent.cancel();
|
||||
m_nextReconnectWait = s_initialReconnectWait;
|
||||
|
||||
this->setLocalUri(FaceUri(m_socket.local_endpoint()));
|
||||
NFD_LOG_FACE_TRACE("TCP connection reestablished");
|
||||
this->setState(TransportState::UP);
|
||||
this->startReceive();
|
||||
}
|
||||
|
||||
void
|
||||
TcpTransport::handleReconnectTimeout()
|
||||
{
|
||||
// abort the reconnection attempt
|
||||
boost::system::error_code error;
|
||||
m_socket.close(error);
|
||||
|
||||
// exponentially back off the reconnection timer
|
||||
m_nextReconnectWait =
|
||||
std::min(time::duration_cast<time::milliseconds>(m_nextReconnectWait * s_reconnectWaitMultiplier),
|
||||
s_maxReconnectWait);
|
||||
|
||||
// do this asynchronously because there could be some callbacks still pending
|
||||
getGlobalIoService().post([this] { reconnect(); });
|
||||
}
|
||||
|
||||
void
|
||||
TcpTransport::doClose()
|
||||
{
|
||||
m_reconnectEvent.cancel();
|
||||
StreamTransport::doClose();
|
||||
}
|
||||
|
||||
} // namespace face
|
||||
|
||||
@@ -1,11 +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
|
||||
/*
|
||||
* Copyright (c) 2014-2018, 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.
|
||||
@@ -20,28 +21,78 @@
|
||||
*
|
||||
* 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_TCP_TRANSPORT_HPP
|
||||
#define NFD_DAEMON_FACE_TCP_TRANSPORT_HPP
|
||||
|
||||
#include "stream-transport.hpp"
|
||||
#include "core/scheduler.hpp"
|
||||
|
||||
namespace nfd {
|
||||
namespace face {
|
||||
|
||||
/**
|
||||
* \brief A Transport that communicates on a connected TCP socket
|
||||
*
|
||||
* When persistency is set to permanent, whenever the TCP connection is severed, the transport
|
||||
* state is set to DOWN, and the connection is retried periodically with exponential backoff
|
||||
* until it is reestablished
|
||||
*/
|
||||
class TcpTransport DECL_CLASS_FINAL : public StreamTransport<boost::asio::ip::tcp>
|
||||
class TcpTransport FINAL_UNLESS_WITH_TESTS : public StreamTransport<boost::asio::ip::tcp>
|
||||
{
|
||||
public:
|
||||
TcpTransport(protocol::socket&& socket,
|
||||
ndn::nfd::FacePersistency persistency);
|
||||
TcpTransport(protocol::socket&& socket, ndn::nfd::FacePersistency persistency);
|
||||
|
||||
ssize_t
|
||||
getSendQueueLength() final;
|
||||
|
||||
protected:
|
||||
virtual void
|
||||
beforeChangePersistency(ndn::nfd::FacePersistency newPersistency) DECL_FINAL;
|
||||
bool
|
||||
canChangePersistencyToImpl(ndn::nfd::FacePersistency newPersistency) const final;
|
||||
|
||||
void
|
||||
afterChangePersistency(ndn::nfd::FacePersistency oldPersistency) final;
|
||||
|
||||
void
|
||||
doClose() final;
|
||||
|
||||
void
|
||||
handleError(const boost::system::error_code& error) final;
|
||||
|
||||
PROTECTED_WITH_TESTS_ELSE_PRIVATE:
|
||||
VIRTUAL_WITH_TESTS void
|
||||
reconnect();
|
||||
|
||||
VIRTUAL_WITH_TESTS void
|
||||
handleReconnect(const boost::system::error_code& error);
|
||||
|
||||
VIRTUAL_WITH_TESTS void
|
||||
handleReconnectTimeout();
|
||||
|
||||
PUBLIC_WITH_TESTS_ELSE_PRIVATE:
|
||||
/** \brief how long to wait before the first reconnection attempt after the TCP connection has been severed
|
||||
*/
|
||||
static time::milliseconds s_initialReconnectWait;
|
||||
|
||||
/** \brief maximum amount of time to wait before a reconnection attempt
|
||||
*/
|
||||
static time::milliseconds s_maxReconnectWait;
|
||||
|
||||
/** \brief multiplier for the exponential backoff of the reconnection timer
|
||||
*/
|
||||
static float s_reconnectWaitMultiplier;
|
||||
|
||||
private:
|
||||
typename protocol::endpoint m_remoteEndpoint;
|
||||
|
||||
/** \note valid only when persistency is set to permanent
|
||||
*/
|
||||
scheduler::ScopedEventId m_reconnectEvent;
|
||||
|
||||
/** \note valid only when persistency is set to permanent
|
||||
*/
|
||||
time::milliseconds m_nextReconnectWait;
|
||||
};
|
||||
|
||||
} // namespace face
|
||||
|
||||
+48
-17
@@ -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-2018, Regents of the University of California,
|
||||
* Arizona Board of Regents,
|
||||
* Colorado State University,
|
||||
* University Pierre & Marie Curie, Sorbonne University,
|
||||
@@ -63,14 +63,13 @@ Transport::Transport()
|
||||
, m_persistency(ndn::nfd::FACE_PERSISTENCY_NONE)
|
||||
, m_linkType(ndn::nfd::LINK_TYPE_NONE)
|
||||
, m_mtu(MTU_INVALID)
|
||||
, m_sendQueueCapacity(QUEUE_UNSUPPORTED)
|
||||
, m_state(TransportState::UP)
|
||||
, m_expirationTime(time::steady_clock::TimePoint::max())
|
||||
{
|
||||
}
|
||||
|
||||
Transport::~Transport()
|
||||
{
|
||||
}
|
||||
Transport::~Transport() = default;
|
||||
|
||||
void
|
||||
Transport::setFaceAndLinkService(Face& face, LinkService& service)
|
||||
@@ -91,7 +90,7 @@ Transport::close()
|
||||
|
||||
this->setState(TransportState::CLOSING);
|
||||
this->doClose();
|
||||
// warning: don't access any fields after this:
|
||||
// warning: don't access any members after this:
|
||||
// the Transport may be deallocated if doClose changes state to CLOSED
|
||||
}
|
||||
|
||||
@@ -127,23 +126,55 @@ Transport::receive(Packet&& packet)
|
||||
m_service->receivePacket(std::move(packet));
|
||||
}
|
||||
|
||||
ssize_t
|
||||
Transport::getSendQueueLength()
|
||||
{
|
||||
return QUEUE_UNSUPPORTED;
|
||||
}
|
||||
|
||||
bool
|
||||
Transport::canChangePersistencyTo(ndn::nfd::FacePersistency newPersistency) const
|
||||
{
|
||||
// not changing, or setting initial persistency in subclass constructor
|
||||
if (m_persistency == newPersistency || m_persistency == ndn::nfd::FACE_PERSISTENCY_NONE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (newPersistency == ndn::nfd::FACE_PERSISTENCY_NONE) {
|
||||
NFD_LOG_FACE_TRACE("cannot change persistency to NONE");
|
||||
return false;
|
||||
}
|
||||
|
||||
return this->canChangePersistencyToImpl(newPersistency);
|
||||
}
|
||||
|
||||
bool
|
||||
Transport::canChangePersistencyToImpl(ndn::nfd::FacePersistency newPersistency) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
Transport::setPersistency(ndn::nfd::FacePersistency newPersistency)
|
||||
{
|
||||
BOOST_ASSERT(canChangePersistencyTo(newPersistency));
|
||||
|
||||
if (m_persistency == newPersistency) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (newPersistency == ndn::nfd::FACE_PERSISTENCY_NONE) {
|
||||
throw std::runtime_error("invalid persistency transition");
|
||||
}
|
||||
|
||||
if (m_persistency != ndn::nfd::FACE_PERSISTENCY_NONE) {
|
||||
this->beforeChangePersistency(newPersistency);
|
||||
NFD_LOG_FACE_DEBUG("setPersistency " << m_persistency << " -> " << newPersistency);
|
||||
}
|
||||
|
||||
auto oldPersistency = m_persistency;
|
||||
m_persistency = newPersistency;
|
||||
|
||||
if (oldPersistency != ndn::nfd::FACE_PERSISTENCY_NONE) {
|
||||
NFD_LOG_FACE_INFO("setPersistency " << oldPersistency << " -> " << newPersistency);
|
||||
this->afterChangePersistency(oldPersistency);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Transport::afterChangePersistency(ndn::nfd::FacePersistency oldPersistency)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
@@ -174,7 +205,7 @@ Transport::setState(TransportState newState)
|
||||
}
|
||||
|
||||
if (!isValid) {
|
||||
throw std::runtime_error("invalid state transition");
|
||||
BOOST_THROW_EXCEPTION(std::runtime_error("invalid state transition"));
|
||||
}
|
||||
|
||||
NFD_LOG_FACE_INFO("setState " << m_state << " -> " << newState);
|
||||
@@ -182,7 +213,7 @@ Transport::setState(TransportState newState)
|
||||
TransportState oldState = m_state;
|
||||
m_state = newState;
|
||||
afterStateChange(oldState, newState);
|
||||
// warning: don't access any fields after this:
|
||||
// warning: don't access any members after this:
|
||||
// the Transport may be deallocated in the signal handler if newState is CLOSED
|
||||
}
|
||||
|
||||
|
||||
+71
-10
@@ -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-2018, Regents of the University of California,
|
||||
* Arizona Board of Regents,
|
||||
* Colorado State University,
|
||||
* University Pierre & Marie Curie, Sorbonne University,
|
||||
@@ -28,6 +28,7 @@
|
||||
|
||||
#include "core/counter.hpp"
|
||||
#include "face-log.hpp"
|
||||
|
||||
#include <ndn-cxx/encoding/nfd-constants.hpp>
|
||||
|
||||
namespace nfd {
|
||||
@@ -40,9 +41,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
|
||||
};
|
||||
@@ -98,6 +99,14 @@ const ssize_t MTU_UNLIMITED = -1;
|
||||
*/
|
||||
const ssize_t MTU_INVALID = -2;
|
||||
|
||||
/** \brief indicates that the transport does not support reading the queue capacity/length
|
||||
*/
|
||||
const ssize_t QUEUE_UNSUPPORTED = -1;
|
||||
|
||||
/** \brief indicates that the transport was unable to retrieve the queue capacity/length
|
||||
*/
|
||||
const ssize_t QUEUE_ERROR = -2;
|
||||
|
||||
/** \brief the lower part of a Face
|
||||
* \sa Face
|
||||
*/
|
||||
@@ -221,10 +230,21 @@ public: // static properties
|
||||
ndn::nfd::FacePersistency
|
||||
getPersistency() const;
|
||||
|
||||
/** \brief check whether the face persistency can be changed to \p newPersistency
|
||||
*
|
||||
* This function serves as the external API, and invokes the protected function
|
||||
* canChangePersistencyToImpl to perform further checks if \p newPersistency differs
|
||||
* from the current persistency.
|
||||
*
|
||||
* \return true if the change can be performed, false otherwise
|
||||
*/
|
||||
bool
|
||||
canChangePersistencyTo(ndn::nfd::FacePersistency newPersistency) const;
|
||||
|
||||
/** \brief changes face persistency setting
|
||||
*/
|
||||
void
|
||||
setPersistency(ndn::nfd::FacePersistency persistency);
|
||||
setPersistency(ndn::nfd::FacePersistency newPersistency);
|
||||
|
||||
/** \return whether face is point-to-point or multi-access
|
||||
*/
|
||||
@@ -243,6 +263,13 @@ public: // static properties
|
||||
ssize_t
|
||||
getMtu() const;
|
||||
|
||||
/** \return capacity of the send queue (in bytes)
|
||||
* \retval QUEUE_UNSUPPORTED transport does not support queue capacity retrieval
|
||||
* \retval QUEUE_ERROR transport was unable to retrieve the queue capacity
|
||||
*/
|
||||
ssize_t
|
||||
getSendQueueCapacity() const;
|
||||
|
||||
public: // dynamic properties
|
||||
/** \return transport state
|
||||
*/
|
||||
@@ -259,6 +286,13 @@ public: // dynamic properties
|
||||
time::steady_clock::TimePoint
|
||||
getExpirationTime() const;
|
||||
|
||||
/** \return current send queue length of the transport (in octets)
|
||||
* \retval QUEUE_UNSUPPORTED transport does not support queue length retrieval
|
||||
* \retval QUEUE_ERROR transport was unable to retrieve the queue length
|
||||
*/
|
||||
virtual ssize_t
|
||||
getSendQueueLength();
|
||||
|
||||
protected: // properties to be set by subclass
|
||||
void
|
||||
setLocalUri(const FaceUri& uri);
|
||||
@@ -275,6 +309,9 @@ protected: // properties to be set by subclass
|
||||
void
|
||||
setMtu(ssize_t mtu);
|
||||
|
||||
void
|
||||
setSendQueueCapacity(ssize_t sendQueueCapacity);
|
||||
|
||||
/** \brief set transport state
|
||||
*
|
||||
* Only the following transitions are valid:
|
||||
@@ -289,12 +326,23 @@ protected: // properties to be set by subclass
|
||||
setExpirationTime(const time::steady_clock::TimePoint& expirationTime);
|
||||
|
||||
protected: // to be overridden by subclass
|
||||
/** \brief invoked before persistency is changed
|
||||
* \throw std::invalid_argument new persistency is not supported
|
||||
* \throw std::runtime_error transition is disallowed
|
||||
/** \brief invoked by canChangePersistencyTo to perform the check
|
||||
*
|
||||
* Base class implementation returns false.
|
||||
*
|
||||
* \param newPersistency the new persistency, guaranteed to be different from current persistency
|
||||
*/
|
||||
virtual bool
|
||||
canChangePersistencyToImpl(ndn::nfd::FacePersistency newPersistency) const;
|
||||
|
||||
/** \brief invoked after the persistency has been changed
|
||||
*
|
||||
* The base class implementation does nothing.
|
||||
* When overridden in a subclass, the function should update internal states
|
||||
* after persistency setting has been changed.
|
||||
*/
|
||||
virtual void
|
||||
beforeChangePersistency(ndn::nfd::FacePersistency newPersistency) = 0;
|
||||
afterChangePersistency(ndn::nfd::FacePersistency oldPersistency);
|
||||
|
||||
/** \brief performs Transport specific operations to close the transport
|
||||
*
|
||||
@@ -324,6 +372,7 @@ private:
|
||||
ndn::nfd::FacePersistency m_persistency;
|
||||
ndn::nfd::LinkType m_linkType;
|
||||
ssize_t m_mtu;
|
||||
size_t m_sendQueueCapacity;
|
||||
TransportState m_state;
|
||||
time::steady_clock::TimePoint m_expirationTime;
|
||||
};
|
||||
@@ -419,6 +468,18 @@ Transport::setMtu(ssize_t mtu)
|
||||
m_mtu = mtu;
|
||||
}
|
||||
|
||||
inline ssize_t
|
||||
Transport::getSendQueueCapacity() const
|
||||
{
|
||||
return m_sendQueueCapacity;
|
||||
}
|
||||
|
||||
inline void
|
||||
Transport::setSendQueueCapacity(ssize_t sendQueueCapacity)
|
||||
{
|
||||
m_sendQueueCapacity = sendQueueCapacity;
|
||||
}
|
||||
|
||||
inline TransportState
|
||||
Transport::getState() const
|
||||
{
|
||||
|
||||
+64
-55
@@ -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-2018, Regents of the University of California,
|
||||
* Arizona Board of Regents,
|
||||
* Colorado State University,
|
||||
* University Pierre & Marie Curie, Sorbonne University,
|
||||
@@ -29,40 +29,38 @@
|
||||
#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,
|
||||
bool wantCongestionMarking)
|
||||
: m_localEndpoint(localEndpoint)
|
||||
, m_socket(getGlobalIoService())
|
||||
, m_idleFaceTimeout(timeout)
|
||||
, m_idleFaceTimeout(idleTimeout)
|
||||
, m_wantCongestionMarking(wantCongestionMarking)
|
||||
{
|
||||
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,
|
||||
const FaceParams& params,
|
||||
const FaceCreatedCallback& onFaceCreated,
|
||||
const FaceCreationFailedCallback& onConnectFailed)
|
||||
{
|
||||
shared_ptr<Face> face;
|
||||
try {
|
||||
face = createFace(remoteEndpoint, persistency).second;
|
||||
face = createFace(remoteEndpoint, params).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(e.what());
|
||||
onConnectFailed(504, std::string("Face creation failed: ") + e.what());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -73,28 +71,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,54 +107,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(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);
|
||||
FaceParams params;
|
||||
params.persistency = ndn::nfd::FACE_PERSISTENCY_ON_DEMAND;
|
||||
std::tie(isCreated, face) = createFace(m_remoteEndpoint, params);
|
||||
}
|
||||
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(e.what());
|
||||
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,
|
||||
const FaceParams& params)
|
||||
{
|
||||
auto it = m_channelFaces.find(remoteEndpoint);
|
||||
if (it != m_channelFaces.end()) {
|
||||
// we already have a face for this endpoint, just reuse it
|
||||
auto face = it->second;
|
||||
// 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};
|
||||
// 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
|
||||
@@ -164,20 +160,33 @@ 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 = params.wantLpReliability;
|
||||
|
||||
if (boost::logic::indeterminate(params.wantCongestionMarking)) {
|
||||
// Use default value for this channel if parameter is indeterminate
|
||||
options.allowCongestionMarking = m_wantCongestionMarking;
|
||||
}
|
||||
else {
|
||||
options.allowCongestionMarking = params.wantCongestionMarking;
|
||||
}
|
||||
|
||||
if (params.baseCongestionMarkingInterval) {
|
||||
options.baseCongestionMarkingInterval = *params.baseCongestionMarkingInterval;
|
||||
}
|
||||
if (params.defaultCongestionThreshold) {
|
||||
options.defaultCongestionThreshold = *params.defaultCongestionThreshold;
|
||||
}
|
||||
|
||||
auto linkService = make_unique<GenericLinkService>(options);
|
||||
auto transport = make_unique<UnicastUdpTransport>(std::move(socket), params.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
|
||||
+41
-60
@@ -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-2018, Regents of the University of California,
|
||||
* Arizona Board of Regents,
|
||||
* Colorado State University,
|
||||
* University Pierre & Marie Curie, Sorbonne University,
|
||||
@@ -27,12 +27,12 @@
|
||||
#define NFD_DAEMON_FACE_UDP_CHANNEL_HPP
|
||||
|
||||
#include "channel.hpp"
|
||||
#include "udp-protocol.hpp"
|
||||
|
||||
#include <array>
|
||||
|
||||
namespace nfd {
|
||||
|
||||
namespace udp {
|
||||
typedef boost::asio::ip::udp::endpoint Endpoint;
|
||||
} // namespace udp
|
||||
namespace face {
|
||||
|
||||
/**
|
||||
* \brief Class implementing UDP-based channel to create faces
|
||||
@@ -41,53 +41,51 @@ 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 wantCongestionMarking);
|
||||
|
||||
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
|
||||
*
|
||||
* \throw UdpChannel::Error if bind or connect on the socket fail
|
||||
* \brief Create a unicast UDP face toward \p remoteEndpoint
|
||||
*/
|
||||
void
|
||||
connect(const udp::Endpoint& remoteEndpoint,
|
||||
ndn::nfd::FacePersistency persistency,
|
||||
const FaceParams& params,
|
||||
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
|
||||
@@ -95,8 +93,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,
|
||||
@@ -105,37 +103,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,
|
||||
const FaceParams& params);
|
||||
|
||||
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
|
||||
bool m_wantCongestionMarking;
|
||||
};
|
||||
|
||||
inline bool
|
||||
UdpChannel::isListening() const
|
||||
{
|
||||
return m_socket.is_open();
|
||||
}
|
||||
|
||||
} // namespace face
|
||||
} // namespace nfd
|
||||
|
||||
#endif // NFD_DAEMON_FACE_UDP_CHANNEL_HPP
|
||||
+389
-230
@@ -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-2018, Regents of the University of California,
|
||||
* Arizona Board of Regents,
|
||||
* Colorado State University,
|
||||
* University Pierre & Marie Curie, Sorbonne University,
|
||||
@@ -27,232 +27,245 @@
|
||||
#include "generic-link-service.hpp"
|
||||
#include "multicast-udp-transport.hpp"
|
||||
#include "core/global-io.hpp"
|
||||
#include "core/network-interface.hpp"
|
||||
|
||||
#ifdef __linux__
|
||||
#include <cerrno> // for errno
|
||||
#include <cstring> // for std::strerror()
|
||||
#include <sys/socket.h> // for setsockopt()
|
||||
#endif
|
||||
#include <ndn-cxx/net/address-converter.hpp>
|
||||
#include <boost/range/adaptor/map.hpp>
|
||||
#include <boost/range/algorithm/copy.hpp>
|
||||
|
||||
namespace nfd {
|
||||
namespace face {
|
||||
|
||||
namespace ip = boost::asio::ip;
|
||||
namespace net = ndn::net;
|
||||
|
||||
NFD_LOG_INIT("UdpFactory");
|
||||
NFD_REGISTER_PROTOCOL_FACTORY(UdpFactory);
|
||||
|
||||
void
|
||||
UdpFactory::prohibitEndpoint(const udp::Endpoint& endpoint)
|
||||
const std::string&
|
||||
UdpFactory::getId()
|
||||
{
|
||||
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());
|
||||
}
|
||||
static std::string id("udp");
|
||||
return id;
|
||||
}
|
||||
|
||||
NFD_LOG_TRACE("prohibiting UDP " << endpoint);
|
||||
m_prohibitedEndpoints.insert(endpoint);
|
||||
UdpFactory::UdpFactory(const CtorParams& params)
|
||||
: ProtocolFactory(params)
|
||||
{
|
||||
m_netifAddConn = netmon->onInterfaceAdded.connect(bind(&UdpFactory::applyMcastConfigToNetif, this, _1));
|
||||
}
|
||||
|
||||
void
|
||||
UdpFactory::prohibitAllIpv4Endpoints(uint16_t port)
|
||||
UdpFactory::processConfig(OptionalConfigSection configSection,
|
||||
FaceSystem::ConfigContext& context)
|
||||
{
|
||||
for (const NetworkInterfaceInfo& nic : listNetworkInterfaces()) {
|
||||
for (const auto& addr : nic.ipv4Addresses) {
|
||||
if (addr != ip::address_v4::any()) {
|
||||
prohibitEndpoint(udp::Endpoint(addr, port));
|
||||
// udp
|
||||
// {
|
||||
// listen yes
|
||||
// port 6363
|
||||
// enable_v4 yes
|
||||
// enable_v6 yes
|
||||
// idle_timeout 600
|
||||
// mcast yes
|
||||
// mcast_group 224.0.23.170
|
||||
// mcast_port 56363
|
||||
// mcast_group_v6 ff02::1234
|
||||
// mcast_port_v6 56363
|
||||
// mcast_ad_hoc no
|
||||
// whitelist
|
||||
// {
|
||||
// *
|
||||
// }
|
||||
// blacklist
|
||||
// {
|
||||
// }
|
||||
// }
|
||||
|
||||
m_wantCongestionMarking = context.generalConfig.wantCongestionMarking;
|
||||
|
||||
bool wantListen = true;
|
||||
uint16_t port = 6363;
|
||||
bool enableV4 = false;
|
||||
bool enableV6 = false;
|
||||
uint32_t idleTimeout = 600;
|
||||
MulticastConfig mcastConfig;
|
||||
|
||||
if (configSection) {
|
||||
// These default to 'yes' but only if face_system.udp section is present
|
||||
enableV4 = enableV6 = mcastConfig.isEnabled = true;
|
||||
|
||||
for (const auto& pair : *configSection) {
|
||||
const std::string& key = pair.first;
|
||||
const ConfigSection& value = pair.second;
|
||||
|
||||
if (key == "listen") {
|
||||
wantListen = ConfigFile::parseYesNo(pair, "face_system.udp");
|
||||
}
|
||||
else if (key == "port") {
|
||||
port = ConfigFile::parseNumber<uint16_t>(pair, "face_system.udp");
|
||||
}
|
||||
else if (key == "enable_v4") {
|
||||
enableV4 = ConfigFile::parseYesNo(pair, "face_system.udp");
|
||||
}
|
||||
else if (key == "enable_v6") {
|
||||
enableV6 = ConfigFile::parseYesNo(pair, "face_system.udp");
|
||||
}
|
||||
else if (key == "idle_timeout") {
|
||||
idleTimeout = ConfigFile::parseNumber<uint32_t>(pair, "face_system.udp");
|
||||
}
|
||||
else if (key == "keep_alive_interval") {
|
||||
// ignored
|
||||
}
|
||||
else if (key == "mcast") {
|
||||
mcastConfig.isEnabled = ConfigFile::parseYesNo(pair, "face_system.udp");
|
||||
}
|
||||
else if (key == "mcast_group") {
|
||||
const std::string& valueStr = value.get_value<std::string>();
|
||||
boost::system::error_code ec;
|
||||
mcastConfig.group.address(ip::address_v4::from_string(valueStr, ec));
|
||||
if (ec) {
|
||||
BOOST_THROW_EXCEPTION(ConfigFile::Error("face_system.udp.mcast_group: '" +
|
||||
valueStr + "' cannot be parsed as an IPv4 address"));
|
||||
}
|
||||
else if (!mcastConfig.group.address().is_multicast()) {
|
||||
BOOST_THROW_EXCEPTION(ConfigFile::Error("face_system.udp.mcast_group: '" +
|
||||
valueStr + "' is not a multicast address"));
|
||||
}
|
||||
}
|
||||
else if (key == "mcast_port") {
|
||||
mcastConfig.group.port(ConfigFile::parseNumber<uint16_t>(pair, "face_system.udp"));
|
||||
}
|
||||
else if (key == "mcast_group_v6") {
|
||||
const std::string& valueStr = value.get_value<std::string>();
|
||||
boost::system::error_code ec;
|
||||
mcastConfig.groupV6.address(ndn::ip::addressV6FromString(valueStr, ec));
|
||||
if (ec) {
|
||||
BOOST_THROW_EXCEPTION(ConfigFile::Error("face_system.udp.mcast_group_v6: '" +
|
||||
valueStr + "' cannot be parsed as an IPv6 address"));
|
||||
}
|
||||
else if (!mcastConfig.groupV6.address().is_multicast()) {
|
||||
BOOST_THROW_EXCEPTION(ConfigFile::Error("face_system.udp.mcast_group_v6: '" +
|
||||
valueStr + "' is not a multicast address"));
|
||||
}
|
||||
}
|
||||
else if (key == "mcast_port_v6") {
|
||||
mcastConfig.groupV6.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);
|
||||
}
|
||||
else if (key == "blacklist") {
|
||||
mcastConfig.netifPredicate.parseBlacklist(value);
|
||||
}
|
||||
else {
|
||||
BOOST_THROW_EXCEPTION(ConfigFile::Error("Unrecognized option face_system.udp." + key));
|
||||
}
|
||||
}
|
||||
|
||||
if (nic.isBroadcastCapable() &&
|
||||
nic.broadcastAddress != ip::address_v4::any()) {
|
||||
prohibitEndpoint(udp::Endpoint(nic.broadcastAddress, port));
|
||||
if (!enableV4 && !enableV6 && !mcastConfig.isEnabled) {
|
||||
BOOST_THROW_EXCEPTION(ConfigFile::Error(
|
||||
"IPv4 and IPv6 UDP channels and UDP multicast have been disabled. "
|
||||
"Remove face_system.udp section to disable UDP channels or enable at least one of them."));
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
NFD_LOG_DEBUG("Creating unicast channel " << endpoint);
|
||||
|
||||
auto channel = findChannel(endpoint);
|
||||
if (channel)
|
||||
return channel;
|
||||
|
||||
if (endpoint.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) {
|
||||
BOOST_THROW_EXCEPTION(Error("Cannot create the requested UDP unicast channel, local "
|
||||
"endpoint is already allocated for a UDP multicast face"));
|
||||
}
|
||||
|
||||
channel = make_shared<UdpChannel>(endpoint, timeout);
|
||||
m_channels[endpoint] = channel;
|
||||
prohibitEndpoint(endpoint);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
shared_ptr<Face>
|
||||
UdpFactory::createMulticastFace(const udp::Endpoint& localEndpoint,
|
||||
const udp::Endpoint& multicastEndpoint,
|
||||
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;
|
||||
else
|
||||
BOOST_THROW_EXCEPTION(Error("Cannot create the requested UDP multicast face, local "
|
||||
"endpoint is already allocated for a UDP multicast face "
|
||||
"on a different multicast group"));
|
||||
}
|
||||
|
||||
// checking if the local endpoint is already in use for a unicast channel
|
||||
auto unicastCh = findChannel(localEndpoint);
|
||||
if (unicastCh) {
|
||||
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"));
|
||||
}
|
||||
|
||||
if (localEndpoint.port() != multicastEndpoint.port()) {
|
||||
BOOST_THROW_EXCEPTION(Error("Cannot create the requested UDP multicast face, "
|
||||
"both endpoints should have the same port number. "));
|
||||
}
|
||||
|
||||
if (!multicastEndpoint.address().is_multicast()) {
|
||||
BOOST_THROW_EXCEPTION(Error("Cannot create the requested UDP multicast face, "
|
||||
"the multicast group given as input is not a multicast address"));
|
||||
}
|
||||
|
||||
ip::udp::socket receiveSocket(getGlobalIoService());
|
||||
receiveSocket.open(multicastEndpoint.protocol());
|
||||
receiveSocket.set_option(ip::udp::socket::reuse_address(true));
|
||||
receiveSocket.bind(multicastEndpoint);
|
||||
|
||||
ip::udp::socket sendSocket(getGlobalIoService());
|
||||
sendSocket.open(multicastEndpoint.protocol());
|
||||
sendSocket.set_option(ip::udp::socket::reuse_address(true));
|
||||
sendSocket.set_option(ip::multicast::enable_loopback(false));
|
||||
sendSocket.bind(udp::Endpoint(ip::address_v4::any(), multicastEndpoint.port()));
|
||||
if (localEndpoint.address() != ip::address_v4::any())
|
||||
sendSocket.set_option(ip::multicast::outbound_interface(localEndpoint.address().to_v4()));
|
||||
|
||||
sendSocket.set_option(ip::multicast::join_group(multicastEndpoint.address().to_v4(),
|
||||
localEndpoint.address().to_v4()));
|
||||
receiveSocket.set_option(ip::multicast::join_group(multicastEndpoint.address().to_v4(),
|
||||
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.
|
||||
*/
|
||||
if (!networkInterfaceName.empty()) {
|
||||
if (::setsockopt(receiveSocket.native_handle(), SOL_SOCKET, SO_BINDTODEVICE,
|
||||
networkInterfaceName.c_str(), networkInterfaceName.size() + 1) < 0) {
|
||||
BOOST_THROW_EXCEPTION(Error("Cannot bind multicast face to " + networkInterfaceName +
|
||||
": " + std::strerror(errno)));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
auto linkService = make_unique<face::GenericLinkService>();
|
||||
auto transport = make_unique<face::MulticastUdpTransport>(localEndpoint, multicastEndpoint,
|
||||
std::move(receiveSocket),
|
||||
std::move(sendSocket));
|
||||
face = make_shared<Face>(std::move(linkService), std::move(transport));
|
||||
|
||||
m_multicastFaces[localEndpoint] = face;
|
||||
connectFaceClosedSignal(*face, [this, localEndpoint] { m_multicastFaces.erase(localEndpoint); });
|
||||
|
||||
return face;
|
||||
}
|
||||
|
||||
shared_ptr<Face>
|
||||
UdpFactory::createMulticastFace(const std::string& localIp,
|
||||
const std::string& multicastIp,
|
||||
const std::string& multicastPort,
|
||||
const std::string& networkInterfaceName/* = ""*/)
|
||||
{
|
||||
udp::Endpoint localEndpoint(ip::address::from_string(localIp),
|
||||
boost::lexical_cast<uint16_t>(multicastPort));
|
||||
udp::Endpoint multicastEndpoint(ip::address::from_string(multicastIp),
|
||||
boost::lexical_cast<uint16_t>(multicastPort));
|
||||
return createMulticastFace(localEndpoint, multicastEndpoint, networkInterfaceName);
|
||||
}
|
||||
|
||||
void
|
||||
UdpFactory::createFace(const FaceUri& uri,
|
||||
ndn::nfd::FacePersistency persistency,
|
||||
const FaceCreatedCallback& onCreated,
|
||||
const FaceCreationFailedCallback& onConnectFailed)
|
||||
{
|
||||
BOOST_ASSERT(uri.isCanonical());
|
||||
|
||||
if (persistency == ndn::nfd::FACE_PERSISTENCY_ON_DEMAND) {
|
||||
BOOST_THROW_EXCEPTION(Error("UdpFactory::createFace does not support FACE_PERSISTENCY_ON_DEMAND"));
|
||||
}
|
||||
|
||||
udp::Endpoint endpoint(ip::address::from_string(uri.getHost()),
|
||||
boost::lexical_cast<uint16_t>(uri.getPort()));
|
||||
|
||||
if (endpoint.address().is_multicast()) {
|
||||
onConnectFailed("The provided address is multicast. Please use createMulticastFace method");
|
||||
if (context.isDryRun) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_prohibitedEndpoints.find(endpoint) != m_prohibitedEndpoints.end()) {
|
||||
onConnectFailed("Requested endpoint is prohibited "
|
||||
"(reserved by this NFD or disallowed by face management protocol)");
|
||||
if (enableV4) {
|
||||
udp::Endpoint endpoint(ip::udp::v4(), port);
|
||||
shared_ptr<UdpChannel> v4Channel = this->createChannel(endpoint, time::seconds(idleTimeout));
|
||||
if (wantListen && !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 (wantListen && !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);
|
||||
NFD_LOG_INFO("enabling multicast on " << mcastConfig.groupV6);
|
||||
}
|
||||
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 IPv4 multicast group from " << m_mcastConfig.group <<
|
||||
" to " << mcastConfig.group);
|
||||
}
|
||||
if (m_mcastConfig.groupV6 != mcastConfig.groupV6) {
|
||||
NFD_LOG_INFO("changing IPv6 multicast group from " << m_mcastConfig.groupV6 <<
|
||||
" to " << mcastConfig.groupV6);
|
||||
}
|
||||
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 CreateFaceRequest& req,
|
||||
const FaceCreatedCallback& onCreated,
|
||||
const FaceCreationFailedCallback& onFailure)
|
||||
{
|
||||
BOOST_ASSERT(req.remoteUri.isCanonical());
|
||||
|
||||
if (req.localUri) {
|
||||
NFD_LOG_TRACE("Cannot create unicast UDP face with LocalUri");
|
||||
onFailure(406, "Unicast UDP faces cannot be created with a LocalUri");
|
||||
return;
|
||||
}
|
||||
|
||||
if (req.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(req.remoteUri.getHost()),
|
||||
boost::lexical_cast<uint16_t>(req.remoteUri.getPort()));
|
||||
|
||||
if (endpoint.address().is_multicast()) {
|
||||
NFD_LOG_TRACE("createFace does not support multicast faces");
|
||||
onFailure(406, "Cannot create multicast UDP faces");
|
||||
return;
|
||||
}
|
||||
|
||||
if (req.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");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -260,44 +273,190 @@ 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, onConnectFailed);
|
||||
i.second->connect(endpoint, req.params, onCreated, onFailure);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
onConnectFailed("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");
|
||||
}
|
||||
|
||||
shared_ptr<UdpChannel>
|
||||
UdpFactory::createChannel(const udp::Endpoint& localEndpoint,
|
||||
time::nanoseconds idleTimeout)
|
||||
{
|
||||
auto it = m_channels.find(localEndpoint);
|
||||
if (it != m_channels.end())
|
||||
return it->second;
|
||||
|
||||
// check if the endpoint is already used by a multicast face
|
||||
if (m_mcastFaces.find(localEndpoint) != m_mcastFaces.end()) {
|
||||
BOOST_THROW_EXCEPTION(Error("Cannot create UDP channel on " +
|
||||
boost::lexical_cast<std::string>(localEndpoint) +
|
||||
", endpoint already allocated for a UDP multicast face"));
|
||||
}
|
||||
|
||||
auto channel = std::make_shared<UdpChannel>(localEndpoint, idleTimeout, m_wantCongestionMarking);
|
||||
m_channels[localEndpoint] = channel;
|
||||
|
||||
return channel;
|
||||
}
|
||||
|
||||
std::vector<shared_ptr<const Channel>>
|
||||
UdpFactory::getChannels() const
|
||||
{
|
||||
std::vector<shared_ptr<const Channel>> channels;
|
||||
channels.reserve(m_channels.size());
|
||||
|
||||
for (const auto& i : m_channels)
|
||||
channels.push_back(i.second);
|
||||
|
||||
return 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;
|
||||
return getChannelsFromMap(m_channels);
|
||||
}
|
||||
|
||||
shared_ptr<Face>
|
||||
UdpFactory::findMulticastFace(const udp::Endpoint& localEndpoint) const
|
||||
UdpFactory::createMulticastFace(const shared_ptr<const net::NetworkInterface>& netif,
|
||||
const ip::address& localAddress,
|
||||
const udp::Endpoint& multicastEndpoint)
|
||||
{
|
||||
auto i = m_multicastFaces.find(localEndpoint);
|
||||
if (i != m_multicastFaces.end())
|
||||
return i->second;
|
||||
else
|
||||
return nullptr;
|
||||
BOOST_ASSERT(multicastEndpoint.address().is_multicast());
|
||||
|
||||
udp::Endpoint localEp(localAddress, multicastEndpoint.port());
|
||||
BOOST_ASSERT(localEp.protocol() == multicastEndpoint.protocol());
|
||||
|
||||
auto mcastEp = multicastEndpoint;
|
||||
if (mcastEp.address().is_v6()) {
|
||||
// in IPv6, a scope id on the multicast address is always required
|
||||
auto mcastAddress = mcastEp.address().to_v6();
|
||||
mcastAddress.scope_id(netif->getIndex());
|
||||
mcastEp.address(mcastAddress);
|
||||
}
|
||||
|
||||
// check if the local endpoint is already used by another multicast face
|
||||
auto it = m_mcastFaces.find(localEp);
|
||||
if (it != m_mcastFaces.end()) {
|
||||
if (it->second->getRemoteUri() == FaceUri(mcastEp))
|
||||
return it->second;
|
||||
else
|
||||
BOOST_THROW_EXCEPTION(Error("Cannot create UDP multicast face on " +
|
||||
boost::lexical_cast<std::string>(localEp) +
|
||||
", endpoint already allocated for a different UDP multicast face"));
|
||||
}
|
||||
|
||||
// check if the local endpoint is already used by a unicast channel
|
||||
if (m_channels.find(localEp) != m_channels.end()) {
|
||||
BOOST_THROW_EXCEPTION(Error("Cannot create UDP multicast face on " +
|
||||
boost::lexical_cast<std::string>(localEp) +
|
||||
", endpoint already allocated for a UDP channel"));
|
||||
}
|
||||
|
||||
ip::udp::socket rxSock(getGlobalIoService());
|
||||
MulticastUdpTransport::openRxSocket(rxSock, mcastEp, localAddress, netif);
|
||||
ip::udp::socket txSock(getGlobalIoService());
|
||||
MulticastUdpTransport::openTxSocket(txSock, udp::Endpoint(localAddress, 0), netif);
|
||||
|
||||
GenericLinkService::Options options;
|
||||
options.allowCongestionMarking = m_wantCongestionMarking;
|
||||
auto linkService = make_unique<GenericLinkService>(options);
|
||||
auto transport = make_unique<MulticastUdpTransport>(mcastEp, std::move(rxSock), std::move(txSock),
|
||||
m_mcastConfig.linkType);
|
||||
auto face = make_shared<Face>(std::move(linkService), std::move(transport));
|
||||
|
||||
m_mcastFaces[localEp] = face;
|
||||
connectFaceClosedSignal(*face, [this, localEp] { m_mcastFaces.erase(localEp); });
|
||||
|
||||
return face;
|
||||
}
|
||||
|
||||
static ndn::optional<ip::address>
|
||||
pickAddress(const net::NetworkInterface& netif, net::AddressFamily af)
|
||||
{
|
||||
for (const auto& na : netif.getNetworkAddresses()) {
|
||||
if (na.getFamily() == af &&
|
||||
(na.getScope() == net::AddressScope::LINK || na.getScope() == net::AddressScope::GLOBAL)) {
|
||||
return na.getIp();
|
||||
}
|
||||
}
|
||||
return ndn::nullopt;
|
||||
}
|
||||
|
||||
std::vector<shared_ptr<Face>>
|
||||
UdpFactory::applyMcastConfigToNetif(const shared_ptr<const net::NetworkInterface>& netif)
|
||||
{
|
||||
BOOST_ASSERT(netif != nullptr);
|
||||
|
||||
if (!m_mcastConfig.isEnabled) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!netif->isUp()) {
|
||||
NFD_LOG_DEBUG("Not creating multicast faces on " << netif->getName() << ": netif is down");
|
||||
return {};
|
||||
}
|
||||
|
||||
if (netif->isLoopback()) {
|
||||
NFD_LOG_DEBUG("Not creating multicast faces on " << netif->getName() << ": netif is loopback");
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!netif->canMulticast()) {
|
||||
NFD_LOG_DEBUG("Not creating multicast faces on " << netif->getName() << ": netif cannot multicast");
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!m_mcastConfig.netifPredicate(*netif)) {
|
||||
NFD_LOG_DEBUG("Not creating multicast faces on " << netif->getName() << ": rejected by whitelist/blacklist");
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<ip::address> addrs;
|
||||
for (auto af : {net::AddressFamily::V4, net::AddressFamily::V6}) {
|
||||
auto addr = pickAddress(*netif, af);
|
||||
if (addr)
|
||||
addrs.push_back(*addr);
|
||||
}
|
||||
|
||||
if (addrs.empty()) {
|
||||
NFD_LOG_DEBUG("Not creating multicast faces on " << netif->getName() << ": no viable IP address");
|
||||
// keep an eye on new addresses
|
||||
m_netifConns[netif->getIndex()].addrAddConn =
|
||||
netif->onAddressAdded.connect(bind(&UdpFactory::applyMcastConfigToNetif, this, netif));
|
||||
return {};
|
||||
}
|
||||
|
||||
NFD_LOG_DEBUG("Creating multicast faces on " << netif->getName());
|
||||
|
||||
std::vector<shared_ptr<Face>> faces;
|
||||
for (const auto& addr : addrs) {
|
||||
auto face = this->createMulticastFace(netif, addr,
|
||||
addr.is_v4() ? m_mcastConfig.group : m_mcastConfig.groupV6);
|
||||
if (face->getId() == INVALID_FACEID) {
|
||||
// new face: register with forwarding
|
||||
this->addFace(face);
|
||||
}
|
||||
faces.push_back(std::move(face));
|
||||
}
|
||||
|
||||
return faces;
|
||||
}
|
||||
|
||||
void
|
||||
UdpFactory::applyMcastConfig(const FaceSystem::ConfigContext& context)
|
||||
{
|
||||
// collect old faces
|
||||
std::set<shared_ptr<Face>> facesToClose;
|
||||
boost::copy(m_mcastFaces | boost::adaptors::map_values,
|
||||
std::inserter(facesToClose, facesToClose.end()));
|
||||
|
||||
// create faces if requested by config
|
||||
for (const auto& netif : netmon->listNetworkInterfaces()) {
|
||||
auto facesToKeep = this->applyMcastConfigToNetif(netif);
|
||||
for (const auto& face : facesToKeep) {
|
||||
// don't destroy face
|
||||
facesToClose.erase(face);
|
||||
}
|
||||
}
|
||||
|
||||
// destroy old faces that are not needed in new configuration
|
||||
for (const auto& face : facesToClose) {
|
||||
face->close();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace face
|
||||
} // namespace nfd
|
||||
+76
-119
@@ -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-2018, Regents of the University of California,
|
||||
* Arizona Board of Regents,
|
||||
* Colorado State University,
|
||||
* University Pierre & Marie Curie, Sorbonne University,
|
||||
@@ -30,9 +30,13 @@
|
||||
#include "udp-channel.hpp"
|
||||
|
||||
namespace nfd {
|
||||
namespace face {
|
||||
|
||||
/// @todo IPv6 multicast support not implemented
|
||||
|
||||
/** \brief protocol factory for UDP over IPv4 and IPv6
|
||||
*
|
||||
* UDP unicast is available over both IPv4 and IPv6.
|
||||
* UDP multicast is available over IPv4 only.
|
||||
*/
|
||||
class UdpFactory : public ProtocolFactory
|
||||
{
|
||||
public:
|
||||
@@ -49,155 +53,108 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::map<udp::Endpoint, shared_ptr<Face>> MulticastFaceMap;
|
||||
static const std::string&
|
||||
getId();
|
||||
|
||||
explicit
|
||||
UdpFactory(const CtorParams& params);
|
||||
|
||||
/** \brief process face_system.udp config section
|
||||
*/
|
||||
void
|
||||
processConfig(OptionalConfigSection configSection,
|
||||
FaceSystem::ConfigContext& context) override;
|
||||
|
||||
void
|
||||
createFace(const CreateFaceRequest& req,
|
||||
const FaceCreatedCallback& onCreated,
|
||||
const FaceCreationFailedCallback& onFailure) override;
|
||||
|
||||
/**
|
||||
* \brief Create UDP-based channel using udp::Endpoint
|
||||
*
|
||||
* 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));
|
||||
time::nanoseconds idleTimeout);
|
||||
|
||||
std::vector<shared_ptr<const Channel>>
|
||||
getChannels() const override;
|
||||
|
||||
/**
|
||||
* \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));
|
||||
|
||||
/**
|
||||
* \brief Create MulticastUdpFace using udp::Endpoint
|
||||
* \brief Create a multicast UDP face
|
||||
*
|
||||
* 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 set of arguments, 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
|
||||
* If a UDP channel, unicast face, or multicast face already exists on the
|
||||
* same combination of local address and multicast port, the 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)
|
||||
* An empty string can be provided in other system or in linux machine with only one
|
||||
* MulticastUdpFace per multicast group
|
||||
* \param netif the network interface to which the face will be bound
|
||||
* \param localAddress the local IP address to which the face will be bound
|
||||
* \param multicastEndpoint the multicast endpoint (multicast group and port number)
|
||||
*
|
||||
*
|
||||
* \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 shared pointer to the created face;
|
||||
* an exception is thrown if the face cannot be created.
|
||||
* \throw UdpFactory::Error
|
||||
*/
|
||||
shared_ptr<Face>
|
||||
createMulticastFace(const udp::Endpoint& localEndpoint,
|
||||
const udp::Endpoint& multicastEndpoint,
|
||||
const std::string& networkInterfaceName = "");
|
||||
|
||||
shared_ptr<Face>
|
||||
createMulticastFace(const std::string& localIp,
|
||||
const std::string& multicastIp,
|
||||
const std::string& multicastPort,
|
||||
const std::string& networkInterfaceName = "");
|
||||
|
||||
/**
|
||||
* \brief Get map of configured multicast faces
|
||||
*/
|
||||
const MulticastFaceMap&
|
||||
getMulticastFaces() const;
|
||||
|
||||
public: // from ProtocolFactory
|
||||
virtual void
|
||||
createFace(const FaceUri& uri,
|
||||
ndn::nfd::FacePersistency persistency,
|
||||
const FaceCreatedCallback& onCreated,
|
||||
const FaceCreationFailedCallback& onConnectFailed) DECL_OVERRIDE;
|
||||
|
||||
virtual std::vector<shared_ptr<const Channel>>
|
||||
getChannels() const DECL_OVERRIDE;
|
||||
|
||||
PUBLIC_WITH_TESTS_ELSE_PRIVATE:
|
||||
void
|
||||
prohibitEndpoint(const udp::Endpoint& endpoint);
|
||||
|
||||
void
|
||||
prohibitAllIpv4Endpoints(uint16_t port);
|
||||
|
||||
void
|
||||
prohibitAllIpv6Endpoints(uint16_t port);
|
||||
createMulticastFace(const shared_ptr<const ndn::net::NetworkInterface>& netif,
|
||||
const boost::asio::ip::address& localAddress,
|
||||
const udp::Endpoint& multicastEndpoint);
|
||||
|
||||
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
|
||||
/** \brief Create UDP multicast faces on \p netif if needed by \p m_mcastConfig
|
||||
* \return list of faces (just created or already existing) on \p netif
|
||||
*/
|
||||
shared_ptr<UdpChannel>
|
||||
findChannel(const udp::Endpoint& localEndpoint) const;
|
||||
std::vector<shared_ptr<Face>>
|
||||
applyMcastConfigToNetif(const shared_ptr<const ndn::net::NetworkInterface>& netif);
|
||||
|
||||
/**
|
||||
* \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 and destroy UDP multicast faces according to \p m_mcastConfig
|
||||
*/
|
||||
shared_ptr<Face>
|
||||
findMulticastFace(const udp::Endpoint& localEndpoint) const;
|
||||
void
|
||||
applyMcastConfig(const FaceSystem::ConfigContext& context);
|
||||
|
||||
private:
|
||||
bool m_wantCongestionMarking = false;
|
||||
std::map<udp::Endpoint, shared_ptr<UdpChannel>> m_channels;
|
||||
MulticastFaceMap m_multicastFaces;
|
||||
|
||||
PUBLIC_WITH_TESTS_ELSE_PRIVATE:
|
||||
std::set<udp::Endpoint> m_prohibitedEndpoints;
|
||||
struct MulticastConfig
|
||||
{
|
||||
bool isEnabled = false;
|
||||
udp::Endpoint group = udp::getDefaultMulticastGroup();
|
||||
udp::Endpoint groupV6 = udp::getDefaultMulticastGroupV6();
|
||||
ndn::nfd::LinkType linkType = ndn::nfd::LINK_TYPE_MULTI_ACCESS;
|
||||
NetworkInterfacePredicate netifPredicate;
|
||||
};
|
||||
MulticastConfig m_mcastConfig;
|
||||
std::map<udp::Endpoint, shared_ptr<Face>> m_mcastFaces;
|
||||
|
||||
signal::ScopedConnection m_netifAddConn;
|
||||
struct NetifConns
|
||||
{
|
||||
signal::ScopedConnection addrAddConn;
|
||||
};
|
||||
std::map<int, NetifConns> m_netifConns; // ifindex => signal connections
|
||||
};
|
||||
|
||||
inline const UdpFactory::MulticastFaceMap&
|
||||
UdpFactory::getMulticastFaces() const
|
||||
{
|
||||
return m_multicastFaces;
|
||||
}
|
||||
|
||||
} // namespace face
|
||||
} // namespace nfd
|
||||
|
||||
#endif // NFD_DAEMON_FACE_UDP_FACTORY_HPP
|
||||
Loaded 100 of 534 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user