Add 'NFD/' from commit 'a22a2172611b1cb93b2e2f53d9d5da122b384f3e'

git-subtree-dir: NFD
git-subtree-mainline: ded1aa693720f9e2998b95c4850b59cbdfcabaa7
git-subtree-split: a22a2172611b1cb93b2e2f53d9d5da122b384f3e
diff --git a/NFD/.gitignore b/NFD/.gitignore
new file mode 100644
index 0000000..4629c34
--- /dev/null
+++ b/NFD/.gitignore
@@ -0,0 +1,8 @@
+.DS*
+.waf-1*
+.waf3-*
+.lock*
+**/*.pyc
+build/
+VERSION
+unit-tests.conf
diff --git a/NFD/.gitmodules b/NFD/.gitmodules
new file mode 100644
index 0000000..0b8f2ee
--- /dev/null
+++ b/NFD/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "websocketpp"]
+    path = websocketpp
+    url = https://github.com/zaphoyd/websocketpp.git
diff --git a/NFD/.jenkins b/NFD/.jenkins
new file mode 100755
index 0000000..674d751
--- /dev/null
+++ b/NFD/.jenkins
@@ -0,0 +1,10 @@
+#!/usr/bin/env bash
+set -e
+
+DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
+
+for file in "$DIR"/.jenkins.d/*; do
+    [[ -f $file && -x $file ]] || continue
+    echo "Run: $file"
+    "$file"
+done
diff --git a/NFD/.jenkins.d/00-deps-ndn-cxx.sh b/NFD/.jenkins.d/00-deps-ndn-cxx.sh
new file mode 100755
index 0000000..951e3f9
--- /dev/null
+++ b/NFD/.jenkins.d/00-deps-ndn-cxx.sh
@@ -0,0 +1,39 @@
+#!/usr/bin/env bash
+set -x
+set -e
+
+cd /tmp
+BUILD="no"
+if [ ! -d ndn-cxx ]; then
+    git clone --depth 1 git://github.com/named-data/ndn-cxx
+    cd ndn-cxx
+    BUILD="yes"
+else
+    cd ndn-cxx
+    INSTALLED_VERSION=`git rev-parse HEAD || echo NONE`
+    sudo rm -Rf latest-version
+    git clone --depth 1 git://github.com/named-data/ndn-cxx latest-version
+    cd latest-version
+    LATEST_VERSION=`git rev-parse HEAD || echo UNKNOWN`
+    cd ..
+    rm -Rf latest-version
+    if [ "$INSTALLED_VERSION" != "$LATEST_VERSION" ]; then
+        cd ..
+        sudo rm -Rf ndn-cxx
+        git clone --depth 1 git://github.com/named-data/ndn-cxx
+        cd ndn-cxx
+        BUILD="yes"
+    fi
+fi
+
+sudo rm -Rf /usr/local/include/ndn-cxx
+sudo rm -f /usr/local/lib/libndn-cxx*
+sudo rm -f /usr/local/lib/pkgconfig/libndn-cxx*
+
+if [ "$BUILD" = "yes" ]; then
+    sudo ./waf distclean -j1 --color=yes
+fi
+
+./waf configure -j1 --color=yes --without-osx-keychain
+./waf -j1 --color=yes
+sudo ./waf install -j1 --color=yes
diff --git a/NFD/.jenkins.d/10-build.sh b/NFD/.jenkins.d/10-build.sh
new file mode 100755
index 0000000..1c4fdf2
--- /dev/null
+++ b/NFD/.jenkins.d/10-build.sh
@@ -0,0 +1,35 @@
+#!/usr/bin/env bash
+set -x
+set -e
+
+git submodule init
+git submodule sync
+git submodule update
+
+COVERAGE=$( python -c "print '--with-coverage' if 'code-coverage' in '$JOB_NAME' else ''" )
+
+# Cleanup
+sudo ./waf -j1 --color=yes distclean
+
+# Configure/build in debug mode
+./waf -j1 --color=yes configure --with-tests --debug
+./waf -j1 --color=yes build
+
+# Cleanup
+sudo ./waf -j1 --color=yes distclean
+
+# Configure/build in optimized mode without tests with precompiled headers
+./waf -j1 --color=yes configure
+./waf -j1 --color=yes build
+
+# Cleanup
+sudo ./waf -j1 --color=yes distclean
+
+# Configure/build in optimized mode
+./waf -j1 --color=yes configure --with-tests --without-pch $COVERAGE
+./waf -j1 --color=yes build
+
+# (tests will be run against optimized version)
+
+# Install
+sudo ./waf -j1 --color=yes install
diff --git a/NFD/.jenkins.d/20-tests.sh b/NFD/.jenkins.d/20-tests.sh
new file mode 100755
index 0000000..6385fc9
--- /dev/null
+++ b/NFD/.jenkins.d/20-tests.sh
@@ -0,0 +1,33 @@
+#!/usr/bin/env bash
+set -x
+set -e
+
+# Prepare environment
+rm -Rf ~/.ndnx ~/.ndn
+
+echo $NODE_LABELS
+IS_OSX=$( python -c "print 'yes' if 'OSX' in '$NODE_LABELS'.strip().split(' ') else 'no'" )
+IS_LINUX=$( python -c "print 'yes' if 'Linux' in '$NODE_LABELS'.strip().split(' ') else 'no'" )
+
+if [[ $IS_OSX == "yes" ]]; then
+  security unlock-keychain -p "named-data"
+  sudo chgrp admin /dev/bpf*
+  sudo chmod g+rw /dev/bpf*
+fi
+if [[ $IS_LINUX = "yes" ]]; 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
+fi
+
+ndnsec-keygen "/tmp/jenkins/$NODE_NAME" | ndnsec-install-cert -
+
+# Run unit tests
+# Core
+./build/unit-tests-core -l test_suite
+
+# Daemon
+./build/unit-tests-daemon -l test_suite
+
+# RIB
+./build/unit-tests-rib -l test_suite
diff --git a/NFD/.jenkins.d/30-coverage.sh b/NFD/.jenkins.d/30-coverage.sh
new file mode 100755
index 0000000..e462884
--- /dev/null
+++ b/NFD/.jenkins.d/30-coverage.sh
@@ -0,0 +1,10 @@
+#!/usr/bin/env bash
+set -x
+set -e
+
+IS_COVR=$( python -c "print 'yes' if 'code-coverage' in '$JOB_NAME' else 'no'" )
+
+if [[ $IS_COVR == "yes" ]]; then
+  BASE="`pwd | sed -e 's|/|\\\/|g'`\\"
+  (cd build && gcovr -x -f $BASE/core -f $BASE/daemon -f $BASE/rib -r ../ -o coverage.xml ./)
+fi
diff --git a/NFD/.travis.yml b/NFD/.travis.yml
new file mode 100644
index 0000000..a4e4b2c
--- /dev/null
+++ b/NFD/.travis.yml
@@ -0,0 +1,15 @@
+# For Ubuntu only
+language: cpp
+os:
+  - linux
+compiler:
+  - gcc
+notifications:
+  email:
+    on_success: always
+    on_failure: always
+before_install:
+  - travis_retry sudo apt-get update
+  - travis_retry sudo apt-get install -qq libssl-dev libpcap-dev libboost1.48-all-dev libcrypto++-dev libsqlite3-dev
+script:
+  - ./.jenkins
diff --git a/NFD/.waf-tools/boost-kqueue.py b/NFD/.waf-tools/boost-kqueue.py
new file mode 100644
index 0000000..d4d5139
--- /dev/null
+++ b/NFD/.waf-tools/boost-kqueue.py
@@ -0,0 +1,23 @@
+# -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
+#
+# Copyright (c) 2014, Regents of the University of California
+#
+# GPL 3.0 license, see the COPYING.md file for more information
+
+from waflib import Configure
+
+BOOST_ASIO_HAS_KQUEUE_CHECK = '''
+#include <boost/asio.hpp>
+#if defined(BOOST_ASIO_HAS_KQUEUE) and BOOST_VERSION == 105600
+#error "Ethernet face support cannot be enabled on this platform with boost 1.56"
+#endif
+'''
+
+@Configure.conf
+def check_asio_pcap_support(conf):
+    # https://svn.boost.org/trac/boost/ticket/10367
+    if conf.check_cxx(msg='Checking if Ethernet face support can be enabled',
+                      fragment=BOOST_ASIO_HAS_KQUEUE_CHECK,
+                      features='cxx', use='BOOST', mandatory=False):
+        conf.define('HAVE_ASIO_PCAP_SUPPORT', 1)
+        conf.env['HAVE_ASIO_PCAP_SUPPORT'] = True
diff --git a/NFD/.waf-tools/boost.py b/NFD/.waf-tools/boost.py
new file mode 100644
index 0000000..305945a
--- /dev/null
+++ b/NFD/.waf-tools/boost.py
@@ -0,0 +1,378 @@
+#!/usr/bin/env python
+# encoding: utf-8
+#
+# partially based on boost.py written by Gernot Vormayr
+# written by Ruediger Sonderfeld <ruediger@c-plusplus.de>, 2008
+# modified by Bjoern Michaelsen, 2008
+# modified by Luca Fossati, 2008
+# rewritten for waf 1.5.1, Thomas Nagy, 2008
+# rewritten for waf 1.6.2, Sylvain Rouquette, 2011
+
+'''
+
+This is an extra tool, not bundled with the default waf binary.
+To add the boost tool to the waf file:
+$ ./waf-light --tools=compat15,boost
+	or, if you have waf >= 1.6.2
+$ ./waf update --files=boost
+
+When using this tool, the wscript will look like:
+
+	def options(opt):
+		opt.load('compiler_cxx boost')
+
+	def configure(conf):
+		conf.load('compiler_cxx boost')
+		conf.check_boost(lib='system filesystem')
+
+	def build(bld):
+		bld(source='main.cpp', target='app', use='BOOST')
+
+Options are generated, in order to specify the location of boost includes/libraries.
+The `check_boost` configuration function allows to specify the used boost libraries.
+It can also provide default arguments to the --boost-static and --boost-mt command-line arguments.
+Everything will be packaged together in a BOOST component that you can use.
+
+When using MSVC, a lot of compilation flags need to match your BOOST build configuration:
+ - you may have to add /EHsc to your CXXFLAGS or define boost::throw_exception if BOOST_NO_EXCEPTIONS is defined.
+   Errors: C4530
+ - boost libraries will try to be smart and use the (pretty but often not useful) auto-linking feature of MSVC
+   So before calling `conf.check_boost` you might want to disabling by adding:
+   	conf.env.DEFINES_BOOST += ['BOOST_ALL_NO_LIB']
+   Errors:
+ - boost might also be compiled with /MT, which links the runtime statically.
+   If you have problems with redefined symbols,
+		self.env['DEFINES_%s' % var] += ['BOOST_ALL_NO_LIB']
+		self.env['CXXFLAGS_%s' % var] += ['/MD', '/EHsc']
+Passing `--boost-linkage_autodetect` might help ensuring having a correct linkage in some basic cases.
+
+'''
+
+import sys
+import re
+from waflib import Utils, Logs, Errors
+from waflib.Configure import conf
+
+BOOST_LIBS = ['/usr/lib', '/usr/local/lib', '/opt/local/lib', '/sw/lib', '/lib', '/usr/lib/x86_64-linux-gnu', '/usr/lib/i386-linux-gnu', '/usr/local/ndn/lib']
+BOOST_INCLUDES = ['/usr/include', '/usr/local/include', '/opt/local/include', '/sw/include', '/usr/local/ndn/include']
+BOOST_VERSION_FILE = 'boost/version.hpp'
+BOOST_VERSION_CODE = '''
+#include <iostream>
+#include <boost/version.hpp>
+int main() { std::cout << BOOST_LIB_VERSION << ":" << BOOST_VERSION << std::endl; }
+'''
+BOOST_SYSTEM_CODE = '''
+#include <boost/system/error_code.hpp>
+int main() { boost::system::error_code c; }
+'''
+BOOST_THREAD_CODE = '''
+#include <boost/thread.hpp>
+int main() { boost::thread t; }
+'''
+
+# toolsets from {boost_dir}/tools/build/v2/tools/common.jam
+PLATFORM = Utils.unversioned_sys_platform()
+detect_intel = lambda env: (PLATFORM == 'win32') and 'iw' or 'il'
+detect_clang = lambda env: (PLATFORM == 'darwin') and 'clang-darwin' or 'clang'
+detect_mingw = lambda env: (re.search('MinGW', env.CXX[0])) and 'mgw' or 'gcc'
+BOOST_TOOLSETS = {
+	'borland':  'bcb',
+	'clang':	detect_clang,
+	'como':	 'como',
+	'cw':	   'cw',
+	'darwin':   'xgcc',
+	'edg':	  'edg',
+	'g++':	  detect_mingw,
+	'gcc':	  detect_mingw,
+	'icpc':	 detect_intel,
+	'intel':	detect_intel,
+	'kcc':	  'kcc',
+	'kylix':	'bck',
+	'mipspro':  'mp',
+	'mingw':	'mgw',
+	'msvc':	 'vc',
+	'qcc':	  'qcc',
+	'sun':	  'sw',
+	'sunc++':   'sw',
+	'tru64cxx': 'tru',
+	'vacpp':	'xlc'
+}
+
+
+def options(opt):
+	opt = opt.add_option_group('Boost Options')
+
+	opt.add_option('--boost-includes', type='string',
+				   default='', dest='boost_includes',
+				   help='''path to the directory where the boost includes are, e.g., /path/to/boost_1_55_0/stage/include''')
+	opt.add_option('--boost-libs', type='string',
+				   default='', dest='boost_libs',
+				   help='''path to the directory where the boost libs are, e.g., /path/to/boost_1_55_0/stage/lib''')
+	opt.add_option('--boost-static', action='store_true',
+				   default=False, dest='boost_static',
+				   help='link with static boost libraries (.lib/.a)')
+	opt.add_option('--boost-mt', action='store_true',
+				   default=False, dest='boost_mt',
+				   help='select multi-threaded libraries')
+	opt.add_option('--boost-abi', type='string', default='', dest='boost_abi',
+				   help='''select libraries with tags (dgsyp, d for debug), see doc Boost, Getting Started, chapter 6.1''')
+	opt.add_option('--boost-linkage_autodetect', action="store_true", dest='boost_linkage_autodetect',
+				   help="auto-detect boost linkage options (don't get used to it / might break other stuff)")
+	opt.add_option('--boost-toolset', type='string',
+				   default='', dest='boost_toolset',
+				   help='force a toolset e.g. msvc, vc90, gcc, mingw, mgw45 (default: auto)')
+	py_version = '%d%d' % (sys.version_info[0], sys.version_info[1])
+	opt.add_option('--boost-python', type='string',
+				   default=py_version, dest='boost_python',
+				   help='select the lib python with this version (default: %s)' % py_version)
+
+
+@conf
+def __boost_get_version_file(self, d):
+	dnode = self.root.find_dir(d)
+	if dnode:
+		return dnode.find_node(BOOST_VERSION_FILE)
+	return None
+
+@conf
+def boost_get_version(self, d):
+	"""silently retrieve the boost version number"""
+	node = self.__boost_get_version_file(d)
+	if node:
+		try:
+			txt = node.read()
+		except (OSError, IOError):
+			Logs.error("Could not read the file %r" % node.abspath())
+		else:
+			re_but1 = re.compile('^#define\\s+BOOST_LIB_VERSION\\s+"(.+)"', re.M)
+			m1 = re_but1.search(txt)
+
+			re_but2 = re.compile('^#define\\s+BOOST_VERSION\\s+(\\d+)', re.M)
+			m2 = re_but2.search(txt)
+
+			if m1 and m2:
+				return (m1.group(1), m2.group(1))
+
+	return self.check_cxx(fragment=BOOST_VERSION_CODE, includes=[d], execute=True, define_ret=True).split(":")
+
+@conf
+def boost_get_includes(self, *k, **kw):
+	includes = k and k[0] or kw.get('includes', None)
+	if includes and self.__boost_get_version_file(includes):
+		return includes
+	for d in Utils.to_list(self.environ.get('INCLUDE', '')) + BOOST_INCLUDES:
+		if self.__boost_get_version_file(d):
+			return d
+	if includes:
+		self.end_msg('headers not found in %s' % includes)
+		self.fatal('The configuration failed')
+	else:
+		self.end_msg('headers not found, please provide a --boost-includes argument (see help)')
+		self.fatal('The configuration failed')
+
+
+@conf
+def boost_get_toolset(self, cc):
+	toolset = cc
+	if not cc:
+		build_platform = Utils.unversioned_sys_platform()
+		if build_platform in BOOST_TOOLSETS:
+			cc = build_platform
+		else:
+			cc = self.env.CXX_NAME
+	if cc in BOOST_TOOLSETS:
+		toolset = BOOST_TOOLSETS[cc]
+	return isinstance(toolset, str) and toolset or toolset(self.env)
+
+
+@conf
+def __boost_get_libs_path(self, *k, **kw):
+	''' return the lib path and all the files in it '''
+	if 'files' in kw:
+		return self.root.find_dir('.'), Utils.to_list(kw['files'])
+	libs = k and k[0] or kw.get('libs', None)
+	if libs:
+		path = self.root.find_dir(libs)
+		files = path.ant_glob('*boost_*')
+	if not libs or not files:
+		for d in Utils.to_list(self.environ.get('LIB', [])) + BOOST_LIBS:
+			path = self.root.find_dir(d)
+			if path:
+				files = path.ant_glob('*boost_*')
+				if files:
+					break
+			path = self.root.find_dir(d + '64')
+			if path:
+				files = path.ant_glob('*boost_*')
+				if files:
+					break
+	if not path:
+		if libs:
+			self.end_msg('libs not found in %s' % libs)
+			self.fatal('The configuration failed')
+		else:
+			self.end_msg('libs not found, please provide a --boost-libs argument (see help)')
+			self.fatal('The configuration failed')
+
+	self.to_log('Found the boost path in %r with the libraries:' % path)
+	for x in files:
+		self.to_log('    %r' % x)
+	return path, files
+
+@conf
+def boost_get_libs(self, *k, **kw):
+	'''
+	return the lib path and the required libs
+	according to the parameters
+	'''
+	path, files = self.__boost_get_libs_path(**kw)
+	t = []
+	if kw.get('mt', False):
+		t.append('mt')
+	if kw.get('abi', None):
+		t.append(kw['abi'])
+	tags = t and '(-%s)+' % '-'.join(t) or ''
+	toolset = self.boost_get_toolset(kw.get('toolset', ''))
+	toolset_pat = '(-%s[0-9]{0,3})+' % toolset
+	version = '(-%s)+' % self.env.BOOST_VERSION
+
+	def find_lib(re_lib, files):
+		for file in files:
+			if re_lib.search(file.name):
+				self.to_log('Found boost lib %s' % file)
+				return file
+		return None
+
+	def format_lib_name(name):
+		if name.startswith('lib') and self.env.CC_NAME != 'msvc':
+			name = name[3:]
+		return name[:name.rfind('.')]
+
+	libs = []
+	for lib in Utils.to_list(k and k[0] or kw.get('lib', None)):
+		py = (lib == 'python') and '(-py%s)+' % kw['python'] or ''
+		# Trying libraries, from most strict match to least one
+		for pattern in ['boost_%s%s%s%s%s' % (lib, toolset_pat, tags, py, version),
+						'boost_%s%s%s%s' % (lib, tags, py, version),
+						'boost_%s%s%s' % (lib, tags, version),
+						# Give up trying to find the right version
+						'boost_%s%s%s%s' % (lib, toolset_pat, tags, py),
+						'boost_%s%s%s' % (lib, tags, py),
+						'boost_%s%s' % (lib, tags)]:
+			self.to_log('Trying pattern %s' % pattern)
+			file = find_lib(re.compile(pattern), files)
+			if file:
+				libs.append(format_lib_name(file.name))
+				break
+		else:
+			self.end_msg('lib %s not found in %s' % (lib, path.abspath()))
+			self.fatal('The configuration failed')
+
+	return path.abspath(), libs
+
+
+@conf
+def check_boost(self, *k, **kw):
+	"""
+	Initialize boost libraries to be used.
+
+	Keywords: you can pass the same parameters as with the command line (without "--boost-").
+	Note that the command line has the priority, and should preferably be used.
+	"""
+	if not self.env['CXX']:
+		self.fatal('load a c++ compiler first, conf.load("compiler_cxx")')
+
+	params = {'lib': k and k[0] or kw.get('lib', None)}
+	for key, value in self.options.__dict__.items():
+		if not key.startswith('boost_'):
+			continue
+		key = key[len('boost_'):]
+		params[key] = value and value or kw.get(key, '')
+
+	var = kw.get('uselib_store', 'BOOST')
+
+	self.start_msg('Checking boost includes')
+	self.env['INCLUDES_%s' % var] = inc = self.boost_get_includes(**params)
+	versions = self.boost_get_version(inc)
+	self.env.BOOST_VERSION = versions[0]
+	self.env.BOOST_VERSION_NUMBER = int(versions[1])
+	self.end_msg("%d.%d.%d" % (int(versions[1]) / 100000,
+				   int(versions[1]) / 100 % 1000,
+				   int(versions[1]) % 100))
+	if Logs.verbose:
+		Logs.pprint('CYAN', '	path : %s' % self.env['INCLUDES_%s' % var])
+
+	if not params['lib']:
+		return
+	self.start_msg('Checking boost libs')
+	suffix = params.get('static', None) and 'ST' or ''
+	path, libs = self.boost_get_libs(**params)
+	self.env['%sLIBPATH_%s' % (suffix, var)] = [path]
+	self.env['%sLIB_%s' % (suffix, var)] = libs
+	self.end_msg('ok')
+	if Logs.verbose:
+		Logs.pprint('CYAN', '	path : %s' % path)
+		Logs.pprint('CYAN', '	libs : %s' % libs)
+
+
+	def try_link():
+		if 'system' in params['lib']:
+			self.check_cxx(
+			 fragment=BOOST_SYSTEM_CODE,
+			 use=var,
+			 execute=False,
+			)
+		if 'thread' in params['lib']:
+			self.check_cxx(
+			 fragment=BOOST_THREAD_CODE,
+			 use=var,
+			 execute=False,
+			)
+
+	if params.get('linkage_autodetect', False):
+		self.start_msg("Attempting to detect boost linkage flags")
+		toolset = self.boost_get_toolset(kw.get('toolset', ''))
+		if toolset in ['vc']:
+			# disable auto-linking feature, causing error LNK1181
+			# because the code wants to be linked against
+			self.env['DEFINES_%s' % var] += ['BOOST_ALL_NO_LIB']
+
+			# if no dlls are present, we guess the .lib files are not stubs
+			has_dlls = False
+			for x in Utils.listdir(path):
+				if x.endswith(self.env.cxxshlib_PATTERN % ''):
+					has_dlls = True
+					break
+			if not has_dlls:
+				self.env['STLIBPATH_%s' % var] = [path]
+				self.env['STLIB_%s' % var] = libs
+				del self.env['LIB_%s' % var]
+				del self.env['LIBPATH_%s' % var]
+
+			# we attempt to play with some known-to-work CXXFLAGS combinations
+			for cxxflags in (['/MD', '/EHsc'], []):
+				self.env.stash()
+				self.env["CXXFLAGS_%s" % var] += cxxflags
+				try:
+					try_link()
+					self.end_msg("ok: winning cxxflags combination: %s" % (self.env["CXXFLAGS_%s" % var]))
+					e = None
+					break
+				except Errors.ConfigurationError as exc:
+					self.env.revert()
+					e = exc
+
+			if e is not None:
+				self.end_msg("Could not auto-detect boost linking flags combination, you may report it to boost.py author", ex=e)
+				self.fatal('The configuration failed')
+		else:
+			self.end_msg("Boost linkage flags auto-detection not implemented (needed ?) for this toolchain")
+			self.fatal('The configuration failed')
+	else:
+		self.start_msg('Checking for boost linkage')
+		try:
+			try_link()
+		except Errors.ConfigurationError as e:
+			self.end_msg("Could not link against boost libraries using supplied options")
+			self.fatal('The configuration failed')
+		self.end_msg('ok')
diff --git a/NFD/.waf-tools/compiler-features.py b/NFD/.waf-tools/compiler-features.py
new file mode 100644
index 0000000..5344939
--- /dev/null
+++ b/NFD/.waf-tools/compiler-features.py
@@ -0,0 +1,27 @@
+# -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
+
+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):
+    if self.check_cxx(msg='Checking for override specifier',
+                      fragment=OVERRIDE,
+                      features='cxx', mandatory=False):
+        self.define('HAVE_CXX_OVERRIDE', 1)
+
+def configure(conf):
+    conf.check_override()
diff --git a/NFD/.waf-tools/coverage.py b/NFD/.waf-tools/coverage.py
new file mode 100644
index 0000000..0a3db65
--- /dev/null
+++ b/NFD/.waf-tools/coverage.py
@@ -0,0 +1,24 @@
+# -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
+#
+# Copyright (c) 2014, Regents of the University of California
+#
+# GPL 3.0 license, see the COPYING.md file for more information
+
+from waflib import TaskGen
+
+def options(opt):
+    opt.add_option('--with-coverage', action='store_true', default=False, dest='with_coverage',
+                   help='''Set compiler flags for gcc to enable code coverage information''')
+
+def configure(conf):
+    if conf.options.with_coverage:
+        conf.check_cxx(cxxflags=['-fprofile-arcs', '-ftest-coverage', '-fPIC'],
+                       linkflags=['-fprofile-arcs'], uselib_store='GCOV', mandatory=True)
+
+@TaskGen.feature('cxx','cc')
+@TaskGen.after('process_source')
+def add_coverage(self):
+    if getattr(self, 'use', ''):
+        self.use += ' GCOV'
+    else:
+        self.use = 'GCOV'
diff --git a/NFD/.waf-tools/default-compiler-flags.py b/NFD/.waf-tools/default-compiler-flags.py
new file mode 100644
index 0000000..1bb2563
--- /dev/null
+++ b/NFD/.waf-tools/default-compiler-flags.py
@@ -0,0 +1,76 @@
+# -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
+#
+# Copyright (c) 2014, Regents of the University of California
+#
+# GPL 3.0 license, see the COPYING.md file for more information
+
+from waflib import Logs, Configure
+
+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)''')
+
+def configure(conf):
+    areCustomCxxflagsPresent = (len(conf.env.CXXFLAGS) > 0)
+    defaultFlags = ['-std=c++0x', '-std=c++11',
+                    '-stdlib=libc++',   # clang on OSX < 10.9 by default uses gcc's
+                                        # libstdc++, which is not C++11 compatible
+                    '-pedantic', '-Wall']
+
+    if conf.options.debug:
+        conf.define('_DEBUG', 1)
+        defaultFlags += ['-O0',
+                         '-Og', # gcc >= 4.8
+                         '-g3',
+                         '-fcolor-diagnostics', # clang
+                         '-fdiagnostics-color', # gcc >= 4.9
+                         '-Werror',
+                         '-Wno-error=maybe-uninitialized', # Bug #1560
+                         '-Wno-error=unneeded-internal-declaration', # Bug #1588
+                         '-Wno-error=deprecated-register', # Bug #2288
+                        ]
+        if areCustomCxxflagsPresent:
+            missingFlags = [x for x in defaultFlags 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:
+            conf.add_supported_cxxflags(defaultFlags)
+    else:
+        defaultFlags += ['-O2', '-g']
+        if not areCustomCxxflagsPresent:
+            conf.add_supported_cxxflags(defaultFlags)
+
+    # clang on OSX < 10.9 by default uses gcc's libstdc++, which is not C++11 compatible
+    conf.add_supported_linkflags(['-stdlib=libc++'])
+
+@Configure.conf
+def add_supported_cxxflags(self, cxxflags):
+    """
+    Check which cxxflags are supported by compiler and add them to env.CXXFLAGS variable
+    """
+    self.start_msg('Checking supported CXXFLAGS')
+
+    supportedFlags = []
+    for flag in cxxflags:
+        if self.check_cxx(cxxflags=['-Werror', flag], mandatory=False):
+            supportedFlags += [flag]
+
+    self.end_msg(' '.join(supportedFlags))
+    self.env.CXXFLAGS = supportedFlags + self.env.CXXFLAGS
+
+@Configure.conf
+def add_supported_linkflags(self, linkflags):
+    """
+    Check which linkflags are supported by compiler and add them to env.LINKFLAGS variable
+    """
+    self.start_msg('Checking supported LINKFLAGS')
+
+    supportedFlags = []
+    for flag in linkflags:
+        if self.check_cxx(linkflags=['-Werror', flag], mandatory=False):
+            supportedFlags += [flag]
+
+    self.end_msg(' '.join(supportedFlags))
+    self.env.LINKFLAGS = supportedFlags + self.env.LINKFLAGS
diff --git a/NFD/.waf-tools/dependency-checker.py b/NFD/.waf-tools/dependency-checker.py
new file mode 100644
index 0000000..629fbfd
--- /dev/null
+++ b/NFD/.waf-tools/dependency-checker.py
@@ -0,0 +1,28 @@
+# encoding: utf-8
+
+from waflib import Options, Logs
+from waflib.Configure import conf
+
+def addDependencyOptions(self, opt, name, extraHelp=''):
+    opt.add_option('--with-%s' % name, type='string', default=None,
+                   dest='with_%s' % name,
+                   help='Path to %s, e.g., /usr/local %s' % (name, extraHelp))
+setattr(Options.OptionsContext, "addDependencyOptions", addDependencyOptions)
+
+@conf
+def checkDependency(self, name, **kw):
+    root = kw.get('path', getattr(Options.options, 'with_%s' % name))
+    kw['msg'] = kw.get('msg', 'Checking for %s library' % name)
+    kw['uselib_store'] = kw.get('uselib_store', name.upper())
+    kw['define_name'] = kw.get('define_name', 'HAVE_%s' % kw['uselib_store'])
+    kw['mandatory'] = kw.get('mandatory', True)
+
+    if root:
+        isOk = self.check_cxx(includes="%s/include" % root,
+                              libpath="%s/lib" % root,
+                              **kw)
+    else:
+        isOk = self.check_cxx(**kw)
+
+    if isOk:
+        self.env[kw['define_name']] = True
diff --git a/NFD/.waf-tools/doxygen.py b/NFD/.waf-tools/doxygen.py
new file mode 100644
index 0000000..ac8c70b
--- /dev/null
+++ b/NFD/.waf-tools/doxygen.py
@@ -0,0 +1,214 @@
+#! /usr/bin/env python
+# encoding: UTF-8
+# Thomas Nagy 2008-2010 (ita)
+
+"""
+
+Doxygen support
+
+Variables passed to bld():
+* doxyfile -- the Doxyfile to use
+
+When using this tool, the wscript will look like:
+
+	def options(opt):
+		opt.load('doxygen')
+
+	def configure(conf):
+		conf.load('doxygen')
+		# check conf.env.DOXYGEN, if it is mandatory
+
+	def build(bld):
+		if bld.env.DOXYGEN:
+			bld(features="doxygen", doxyfile='Doxyfile', ...)
+
+        def doxygen(bld):
+		if bld.env.DOXYGEN:
+			bld(features="doxygen", doxyfile='Doxyfile', ...)
+"""
+
+from fnmatch import fnmatchcase
+import os, os.path, re, stat
+from waflib import Task, Utils, Node, Logs, Errors, Build
+from waflib.TaskGen import feature
+
+DOXY_STR = '"${DOXYGEN}" - '
+DOXY_FMTS = 'html latex man rft xml'.split()
+DOXY_FILE_PATTERNS = '*.' + ' *.'.join('''
+c cc cxx cpp c++ java ii ixx ipp i++ inl h hh hxx hpp h++ idl odl cs php php3
+inc m mm py f90c cc cxx cpp c++ java ii ixx ipp i++ inl h hh hxx
+'''.split())
+
+re_rl = re.compile('\\\\\r*\n', re.MULTILINE)
+re_nl = re.compile('\r*\n', re.M)
+def parse_doxy(txt):
+	tbl = {}
+	txt   = re_rl.sub('', txt)
+	lines = re_nl.split(txt)
+	for x in lines:
+		x = x.strip()
+		if not x or x.startswith('#') or x.find('=') < 0:
+			continue
+		if x.find('+=') >= 0:
+			tmp = x.split('+=')
+			key = tmp[0].strip()
+			if key in tbl:
+				tbl[key] += ' ' + '+='.join(tmp[1:]).strip()
+			else:
+				tbl[key] = '+='.join(tmp[1:]).strip()
+		else:
+			tmp = x.split('=')
+			tbl[tmp[0].strip()] = '='.join(tmp[1:]).strip()
+	return tbl
+
+class doxygen(Task.Task):
+	vars  = ['DOXYGEN', 'DOXYFLAGS']
+	color = 'BLUE'
+
+	def runnable_status(self):
+		'''
+		self.pars are populated in runnable_status - because this function is being
+		run *before* both self.pars "consumers" - scan() and run()
+
+		set output_dir (node) for the output
+		'''
+
+		for x in self.run_after:
+			if not x.hasrun:
+				return Task.ASK_LATER
+
+		if not getattr(self, 'pars', None):
+			txt = self.inputs[0].read()
+			self.pars = parse_doxy(txt)
+			if not self.pars.get('OUTPUT_DIRECTORY'):
+				self.pars['OUTPUT_DIRECTORY'] = self.inputs[0].parent.get_bld().abspath()
+
+			# Override with any parameters passed to the task generator
+			if getattr(self.generator, 'pars', None):
+				for k, v in self.generator.pars.iteritems():
+					self.pars[k] = v
+
+			self.doxy_inputs = getattr(self, 'doxy_inputs', [])
+			if not self.pars.get('INPUT'):
+				self.doxy_inputs.append(self.inputs[0].parent)
+			else:
+				for i in self.pars.get('INPUT').split():
+					if os.path.isabs(i):
+						node = self.generator.bld.root.find_node(i)
+					else:
+						node = self.generator.path.find_node(i)
+					if not node:
+						self.generator.bld.fatal('Could not find the doxygen input %r' % i)
+					self.doxy_inputs.append(node)
+
+		if not getattr(self, 'output_dir', None):
+			bld = self.generator.bld
+			# First try to find an absolute path, then find or declare a relative path
+			self.output_dir = bld.root.find_dir(self.pars['OUTPUT_DIRECTORY'])
+			if not self.output_dir:
+				self.output_dir = bld.path.find_or_declare(self.pars['OUTPUT_DIRECTORY'])
+
+		self.signature()
+		return Task.Task.runnable_status(self)
+
+	def scan(self):
+		exclude_patterns = self.pars.get('EXCLUDE_PATTERNS','').split()
+		file_patterns = self.pars.get('FILE_PATTERNS','').split()
+		if not file_patterns:
+			file_patterns = DOXY_FILE_PATTERNS
+		if self.pars.get('RECURSIVE') == 'YES':
+			file_patterns = ["**/%s" % pattern for pattern in file_patterns]
+		nodes = []
+		names = []
+		for node in self.doxy_inputs:
+			if os.path.isdir(node.abspath()):
+				for m in node.ant_glob(incl=file_patterns, excl=exclude_patterns):
+					nodes.append(m)
+			else:
+				nodes.append(node)
+		return (nodes, names)
+
+	def run(self):
+		dct = self.pars.copy()
+		dct['INPUT'] = ' '.join(['"%s"' % x.abspath() for x in self.doxy_inputs])
+		code = '\n'.join(['%s = %s' % (x, dct[x]) for x in self.pars])
+		code = code.encode() # for python 3
+		#fmt = DOXY_STR % (self.inputs[0].parent.abspath())
+		cmd = Utils.subst_vars(DOXY_STR, self.env)
+		env = self.env.env or None
+		proc = Utils.subprocess.Popen(cmd, shell=True, stdin=Utils.subprocess.PIPE, env=env, cwd=self.generator.bld.path.get_bld().abspath())
+		proc.communicate(code)
+		return proc.returncode
+
+	def post_run(self):
+		nodes = self.output_dir.ant_glob('**/*', quiet=True)
+		for x in nodes:
+			x.sig = Utils.h_file(x.abspath())
+		self.outputs += nodes
+		return Task.Task.post_run(self)
+
+class tar(Task.Task):
+	"quick tar creation"
+	run_str = '${TAR} ${TAROPTS} ${TGT} ${SRC}'
+	color   = 'RED'
+	after   = ['doxygen']
+	def runnable_status(self):
+		for x in getattr(self, 'input_tasks', []):
+			if not x.hasrun:
+				return Task.ASK_LATER
+
+		if not getattr(self, 'tar_done_adding', None):
+			# execute this only once
+			self.tar_done_adding = True
+			for x in getattr(self, 'input_tasks', []):
+				self.set_inputs(x.outputs)
+			if not self.inputs:
+				return Task.SKIP_ME
+		return Task.Task.runnable_status(self)
+
+	def __str__(self):
+		tgt_str = ' '.join([a.nice_path(self.env) for a in self.outputs])
+		return '%s: %s\n' % (self.__class__.__name__, tgt_str)
+
+@feature('doxygen')
+def process_doxy(self):
+	if not getattr(self, 'doxyfile', None):
+		self.generator.bld.fatal('no doxyfile??')
+
+	node = self.doxyfile
+	if not isinstance(node, Node.Node):
+		node = self.path.find_resource(node)
+	if not node:
+		raise ValueError('doxygen file not found')
+
+	# the task instance
+	dsk = self.create_task('doxygen', node)
+
+	if getattr(self, 'doxy_tar', None):
+		tsk = self.create_task('tar')
+		tsk.input_tasks = [dsk]
+		tsk.set_outputs(self.path.find_or_declare(self.doxy_tar))
+		if self.doxy_tar.endswith('bz2'):
+			tsk.env['TAROPTS'] = ['cjf']
+		elif self.doxy_tar.endswith('gz'):
+			tsk.env['TAROPTS'] = ['czf']
+		else:
+			tsk.env['TAROPTS'] = ['cf']
+
+def configure(conf):
+	'''
+	Check if doxygen and tar commands are present in the system
+
+	If the commands are present, then conf.env.DOXYGEN and conf.env.TAR
+	variables will be set. Detection can be controlled by setting DOXYGEN and
+	TAR environmental variables.
+	'''
+
+	conf.find_program('doxygen', var='DOXYGEN', mandatory=False)
+	conf.find_program('tar', var='TAR', mandatory=False)
+
+# doxygen docs
+from waflib.Build import BuildContext
+class doxy(BuildContext):
+    cmd = "doxygen"
+    fun = "doxygen"
diff --git a/NFD/.waf-tools/pch.py b/NFD/.waf-tools/pch.py
new file mode 100644
index 0000000..08cc5de
--- /dev/null
+++ b/NFD/.waf-tools/pch.py
@@ -0,0 +1,148 @@
+#! /usr/bin/env python
+# encoding: utf-8
+# Alexander Afanasyev (UCLA), 2014
+
+"""
+Enable precompiled C++ header support (currently only clang++ and g++ are supported)
+
+To use this tool, wscript should look like:
+
+	def options(opt):
+		opt.load('pch')
+		# This will add `--with-pch` configure option.
+		# Unless --with-pch during configure stage specified, the precompiled header support is disabled
+
+	def configure(conf):
+		conf.load('pch')
+		# this will set conf.env.WITH_PCH if --with-pch is specified and the supported compiler is used
+		# Unless conf.env.WITH_PCH is set, the precompiled header support is disabled
+
+	def build(bld):
+		bld(features='cxx pch',
+			target='precompiled-headers',
+			name='precompiled-headers',
+			headers='a.h b.h c.h', # headers to pre-compile into `precompiled-headers`
+
+			# Other parameters to compile precompiled headers
+			# includes=...,
+			# export_includes=...,
+			# use=...,
+			# ...
+
+			# Exported parameters will be propagated even if precompiled headers are disabled
+		)
+
+		bld(
+			target='test',
+			features='cxx cxxprogram',
+			source='a.cpp b.cpp d.cpp main.cpp',
+			use='precompiled-headers',
+		)
+
+		# or
+
+		bld(
+			target='test',
+			features='pch cxx cxxprogram',
+			source='a.cpp b.cpp d.cpp main.cpp',
+			headers='a.h b.h c.h',
+		)
+
+Note that precompiled header must have multiple inclusion guards.  If the guards are missing, any benefit of precompiled header will be voided and compilation may fail in some cases.
+"""
+
+import os
+from waflib import Task, TaskGen, Logs, Utils
+from waflib.Tools import c_preproc, cxx
+
+
+PCH_COMPILER_OPTIONS = {
+	'clang++': [['-include'], '.pch', ['-x', 'c++-header']],
+	'g++':     [['-include'], '.gch', ['-x', 'c++-header']],
+}
+
+
+def options(opt):
+	opt.add_option('--without-pch', action='store_false', default=True, dest='with_pch', help='''Try to use precompiled header to speed up compilation (only g++ and clang++)''')
+
+def configure(conf):
+	if (conf.options.with_pch and conf.env['COMPILER_CXX'] in PCH_COMPILER_OPTIONS.keys()):
+		conf.env.WITH_PCH = True
+		flags = PCH_COMPILER_OPTIONS[conf.env['COMPILER_CXX']]
+		conf.env.CXXPCH_F = flags[0]
+		conf.env.CXXPCH_EXT = flags[1]
+		conf.env.CXXPCH_FLAGS = flags[2]
+
+
+@TaskGen.feature('pch')
+@TaskGen.before('process_source')
+def apply_pch(self):
+	if not self.env.WITH_PCH:
+		return
+
+	if getattr(self.bld, 'pch_tasks', None) is None:
+		self.bld.pch_tasks = {}
+
+	if getattr(self, 'headers', None) is None:
+		return
+
+	self.headers = self.to_nodes(self.headers)
+
+	if getattr(self, 'name', None):
+		try:
+			task = self.bld.pch_tasks[self.name]
+			self.bld.fatal("Duplicated 'pch' task with name %r" % self.name)
+		except KeyError:
+			pass
+
+	out = '%s.%d%s' % (self.target, self.idx, self.env['CXXPCH_EXT'])
+	out = self.path.find_or_declare(out)
+	task = self.create_task('gchx', self.headers, out)
+
+	# target should be an absolute path of `out`, but without precompiled header extension
+	task.target = out.abspath()[:-len(out.suffix())]
+
+	self.pch_task = task
+	if getattr(self, 'name', None):
+		self.bld.pch_tasks[self.name] = task
+
+@TaskGen.feature('cxx')
+@TaskGen.after_method('process_source', 'propagate_uselib_vars')
+def add_pch(self):
+	if not (self.env['WITH_PCH'] and getattr(self, 'use', None) and getattr(self, 'compiled_tasks', None) and getattr(self.bld, 'pch_tasks', None)):
+		return
+
+	pch = None
+	# find pch task, if any
+
+	if getattr(self, 'pch_task', None):
+		pch = self.pch_task
+	else:
+		for use in Utils.to_list(self.use):
+			try:
+				pch = self.bld.pch_tasks[use]
+			except KeyError:
+				pass
+
+	if pch:
+		for x in self.compiled_tasks:
+			x.env.append_value('CXXFLAGS', self.env['CXXPCH_F'] + [pch.target])
+
+class gchx(Task.Task):
+	run_str = '${CXX} ${ARCH_ST:ARCH} ${CXXFLAGS} ${CPPFLAGS} ${CXXPCH_FLAGS} ${FRAMEWORKPATH_ST:FRAMEWORKPATH} ${CPPPATH_ST:INCPATHS} ${DEFINES_ST:DEFINES} ${CXXPCH_F:SRC} ${CXX_SRC_F}${SRC[0].abspath()} ${CXX_TGT_F}${TGT[0].abspath()}'
+	scan    = c_preproc.scan
+	color   = 'BLUE'
+	ext_out=['.h']
+
+	def runnable_status(self):
+		try:
+			node_deps = self.generator.bld.node_deps[self.uid()]
+		except KeyError:
+			node_deps = []
+		ret = Task.Task.runnable_status(self)
+		if ret == Task.SKIP_ME and self.env.CXX_NAME == 'clang':
+			t = os.stat(self.outputs[0].abspath()).st_mtime
+			for n in self.inputs + node_deps:
+				if os.stat(n.abspath()).st_mtime > t:
+					return Task.RUN_ME
+		return ret
diff --git a/NFD/.waf-tools/sphinx_build.py b/NFD/.waf-tools/sphinx_build.py
new file mode 100644
index 0000000..e61da6e
--- /dev/null
+++ b/NFD/.waf-tools/sphinx_build.py
@@ -0,0 +1,79 @@
+#!/usr/bin/env python
+# encoding: utf-8
+
+# inspired by code by Hans-Martin von Gaudecker, 2012
+
+import os
+from waflib import Node, Task, TaskGen, Errors, Logs, Build, Utils
+
+class sphinx_build(Task.Task):
+    color = 'BLUE'
+    run_str = '${SPHINX_BUILD} -D ${VERSION} -D ${RELEASE} -q -b ${BUILDERNAME} -d ${DOCTREEDIR} ${SRCDIR} ${OUTDIR}'
+
+    def __str__(self):
+        env = self.env
+        src_str = ' '.join([a.path_from(a.ctx.launch_node()) for a in self.inputs])
+        tgt_str = ' '.join([a.path_from(a.ctx.launch_node()) for a in self.outputs])
+        if self.outputs: sep = ' -> '
+        else: sep = ''
+        return'%s [%s]: %s%s%s\n'%(self.__class__.__name__.replace('_task',''),
+                                   self.env['BUILDERNAME'], src_str, sep, tgt_str)
+
+@TaskGen.extension('.py', '.rst')
+def sig_hook(self, node):
+    node.sig=Utils.h_file(node.abspath())
+
+@TaskGen.feature("sphinx")
+@TaskGen.before_method("process_source")
+def apply_sphinx(self):
+    """Set up the task generator with a Sphinx instance and create a task."""
+
+    inputs = []
+    for i in Utils.to_list(self.source):
+        if not isinstance(i, Node.Node):
+            node = self.path.find_node(node)
+        else:
+            node = i
+        if not node:
+            raise ValueError('[%s] file not found' % i)
+        inputs.append(node)
+
+    task = self.create_task('sphinx_build', inputs)
+
+    conf = self.path.find_node(self.config)
+    task.inputs.append(conf)
+
+    confdir = conf.parent.abspath()
+    buildername = getattr(self, "builder", "html")
+    srcdir = getattr(self, "srcdir", confdir)
+    outdir = self.path.find_or_declare(getattr(self, "outdir", buildername)).get_bld()
+    doctreedir = getattr(self, "doctreedir", os.path.join(outdir.abspath(), ".doctrees"))
+
+    task.env['BUILDERNAME'] = buildername
+    task.env['SRCDIR'] = srcdir
+    task.env['DOCTREEDIR'] = doctreedir
+    task.env['OUTDIR'] = outdir.abspath()
+    task.env['VERSION'] = "version=%s" % self.VERSION
+    task.env['RELEASE'] = "release=%s" % self.VERSION
+
+    import imp
+    confData = imp.load_source('sphinx_conf', conf.abspath())
+
+    if buildername == "man":
+        for i in confData.man_pages:
+            target = outdir.find_or_declare('%s.%d' % (i[1], i[4]))
+            task.outputs.append(target)
+
+            if self.install_path:
+                self.bld.install_files("%s/man%d/" % (self.install_path, i[4]), target)
+    else:
+        task.outputs.append(outdir)
+
+def configure(conf):
+    conf.find_program('sphinx-build', var='SPHINX_BUILD', mandatory=False)
+
+# sphinx docs
+from waflib.Build import BuildContext
+class sphinx(BuildContext):
+    cmd = "sphinx"
+    fun = "sphinx"
diff --git a/NFD/.waf-tools/type_traits.py b/NFD/.waf-tools/type_traits.py
new file mode 100644
index 0000000..07eb129
--- /dev/null
+++ b/NFD/.waf-tools/type_traits.py
@@ -0,0 +1,30 @@
+# -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
+#
+# Copyright (c) 2014, Regents of the University of California
+#
+# GPL 3.0 license, see the COPYING.md file for more information
+
+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):
+        conf.define('HAVE_IS_MOVE_CONSTRUCTIBLE', 1)
+        conf.env['HAVE_IS_MOVE_CONSTRUCTIBLE'] = True
diff --git a/NFD/.waf-tools/unix-socket.py b/NFD/.waf-tools/unix-socket.py
new file mode 100644
index 0000000..52e038a
--- /dev/null
+++ b/NFD/.waf-tools/unix-socket.py
@@ -0,0 +1,29 @@
+# -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
+#
+# Copyright (c) 2014, Regents of the University of California
+#
+# GPL 3.0 license, see the COPYING.md file for more information
+
+from waflib import Options
+
+BOOST_ASIO_HAS_LOCAL_SOCKETS_CHECK = '''
+#include <boost/asio.hpp>
+#ifndef BOOST_ASIO_HAS_LOCAL_SOCKETS
+#error "Unix sockets are not available on this platform"
+#endif
+'''
+
+def addUnixOptions(self, opt):
+    opt.add_option('--force-unix-socket', action='store_true', default=False,
+                   dest='force_unix_socket', help='''Forcefully enable Unix sockets support''')
+setattr(Options.OptionsContext, "addUnixOptions", addUnixOptions)
+
+def configure(conf):
+    def boost_asio_has_local_sockets():
+        return conf.check_cxx(msg='Checking if Unix sockets are supported',
+                              fragment=BOOST_ASIO_HAS_LOCAL_SOCKETS_CHECK,
+                              features='cxx', use='BOOST', mandatory=False)
+
+    if conf.options.force_unix_socket or boost_asio_has_local_sockets():
+        conf.define('HAVE_UNIX_SOCKETS', 1)
+        conf.env['HAVE_UNIX_SOCKETS'] = True
diff --git a/NFD/.waf-tools/websocket.py b/NFD/.waf-tools/websocket.py
new file mode 100644
index 0000000..f8d2e33
--- /dev/null
+++ b/NFD/.waf-tools/websocket.py
@@ -0,0 +1,79 @@
+# encoding: utf-8
+
+from waflib import Options, Logs, Errors
+from waflib.Configure import conf
+
+import re
+
+def addWebsocketOptions(self, opt):
+    opt.add_option('--without-websocket', action='store_false', default=True,
+                   dest='with_websocket',
+                   help='Disable WebSocket face support')
+setattr(Options.OptionsContext, "addWebsocketOptions", addWebsocketOptions)
+
+@conf
+def checkWebsocket(self, **kw):
+    if not self.options.with_websocket:
+        return
+
+    isMandatory = kw.get('mandatory', True)
+
+    self.start_msg('Checking for WebSocket includes')
+
+    try:
+        websocketDir = self.path.find_dir('websocketpp/websocketpp')
+        if not websocketDir:
+            raise Errors.WafError('Not found')
+
+        versionFile = websocketDir.find_node('version.hpp')
+        if not websocketDir:
+            raise Errors.WafError('Corrupted: WebSocket version file not found')
+
+        try:
+            txt = versionFile.read()
+        except (OSError, IOError):
+            raise Errors.WafError('Corrupted: cannot read WebSocket version file')
+
+        # Looking for the following:
+        # static int const major_version = 0;
+        # static int const minor_version = 3;
+        # static int const patch_version = 0;
+
+        version = [None, None, None]
+
+        majorVersion = re.compile('^static int const major_version = (\\d+);$', re.M)
+        version[0] = majorVersion.search(txt)
+
+        minorVersion = re.compile('^static int const minor_version = (\\d+);$', re.M)
+        version[1] = minorVersion.search(txt)
+
+        patchVersion = re.compile('^static int const patch_version = (\\d+);$', re.M)
+        version[2] = patchVersion.search(txt)
+
+        if not version[0] or not version[1] or not version[2]:
+            raise Errors.WafError('Corrupted: cannot detect websocket version')
+
+        self.env['WEBSOCKET_VERSION'] = [i.group(1) for i in version]
+
+        # todo: version checking, if necessary
+
+        self.end_msg('.'.join(self.env['WEBSOCKET_VERSION']))
+
+        self.env['INCLUDES_WEBSOCKET'] = websocketDir.parent.abspath()
+        self.env['HAVE_WEBSOCKET'] = True
+        self.define('HAVE_WEBSOCKET', 1)
+        self.define('_WEBSOCKETPP_CPP11_STL_', 1)
+
+    except Errors.WafError as error:
+        if isMandatory:
+            self.end_msg(str(error), color='RED')
+            Logs.warn('If you are using git NFD repository, checkout websocketpp submodule: ')
+            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/tarball/4309749dd98937b8a7be5dc0bfe679ba201c5512 > 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")
+        else:
+            self.end_msg(str(error))
diff --git a/NFD/AUTHORS.md b/NFD/AUTHORS.md
new file mode 100644
index 0000000..6538fcb
--- /dev/null
+++ b/NFD/AUTHORS.md
@@ -0,0 +1,73 @@
+NFD Authors
+===========
+
+NFD is an open source project started in late 2013, includes contributions
+from many people around the world, and open for new contributions from new
+volunteers.
+
+
+## Project technical leads:
+
+* Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
+* Junxiao Shi         <http://www.cs.arizona.edu/people/shijunxiao/>
+
+
+## Technical advisors:
+
+* Beichuan Zhang      <http://www.cs.arizona.edu/~bzhang/>
+* Lixia Zhang         <http://www.cs.ucla.edu/~lixia/>
+
+
+## Main project authors and their affiliations:
+
+* University of California, Los Angeles
+
+    * Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
+    * Ilya Moiseenko      <http://ilyamoiseenko.com/>
+    * Yingdi Yu           <http://irl.cs.ucla.edu/~yingdi/web/index.html>
+    * Wentao Shang        <http://irl.cs.ucla.edu/~wentao/>
+    * Lixia Zhang         <http://www.cs.ucla.edu/~lixia/>
+
+* The University of Arizona
+
+    * Junxiao Shi         <http://www.cs.arizona.edu/people/shijunxiao/>
+    * Yi Huang            <ethanhuang1991@gmail.com>
+    * Jerald Paul Abraham <http://www.cs.arizona.edu/people/jeraldabraham/>
+    * Beichuan Zhang      <http://www.cs.arizona.edu/~bzhang/>
+
+* Colorado State University
+
+    * Steve DiBenedetto     <http://www.cs.colostate.edu/~dibenede/>
+    * Chengyu Fan           <chengyu@cs.colostate.edu>
+    * Christos Papadopoulos <http://www.cs.colostate.edu/~christos/>
+
+* University Pierre & Marie Curie, Sorbonne University
+
+    * Davide Pesavento    <http://www.lip6.fr/actualite/personnes-fiche.php?ident=D1469>
+    * 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>
+
+* Beijing Institute of Technology
+
+    * Hang Zhang          <http://netlab.bit.edu.cn/z_hang>
+    * Tian Song           <http://cs.bit.edu.cn/songtian>
+
+* Washington University in St. Louis
+
+    * Haowei Yuan         <http://www.cse.wustl.edu/~yuanh/>
+    * Hila Ben Abraham    <http://research.engineering.wustl.edu/~abrahamh/>
+    * Patrick Crowley     <http://www.arl.wustl.edu/~pcrowley/>
+
+* The University of Memphis
+
+    * Syed Obaid Amin     <http://obaidamin.weebly.com/>
+    * Vince Lehman        <vslehman@memphis.edu>
+    * Lan Wang            <http://www.cs.memphis.edu/~lanwang/>
+
+## Project contributors
+
+The following is an inevitably incomplete list of MUCH-APPRECIATED CONTRIBUTORS,
+people who have reported bugs, submitted patches, and implemented new features
+in the library:
+
+* Tai-Lin Chu             <https://www.linkedin.com/pub/tai-lin-chu/55/5b2/669>
diff --git a/NFD/COPYING.md b/NFD/COPYING.md
new file mode 100644
index 0000000..d799430
--- /dev/null
+++ b/NFD/COPYING.md
@@ -0,0 +1,677 @@
+GNU GENERAL PUBLIC LICENSE
+==========================
+Version 3, 29 June 2007
+=======================
+
+> Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+  Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
+
+# Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+# TERMS AND CONDITIONS
+
+## 0. Definitions.
+
+  _"This License"_ refers to version 3 of the GNU General Public License.
+
+  _"Copyright"_ also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  _"The Program"_ refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as _"you"_.  _"Licensees"_ and
+"recipients" may be individuals or organizations.
+
+  To _"modify"_ a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a _"modified version"_ of the
+earlier work or a work _"based on"_ the earlier work.
+
+  A _"covered work"_ means either the unmodified Program or a work based
+on the Program.
+
+  To _"propagate"_ a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To _"convey"_ a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+## 1. Source Code.
+
+  The _"source code"_ for a work means the preferred form of the work
+for making modifications to it. _"Object code"_ means any non-source
+form of a work.
+
+  A _"Standard Interface"_ means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The _"System Libraries"_ of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The _"Corresponding Source"_ for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+## 2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+## 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+## 4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+## 5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+## 6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A _"User Product"_ is either (1) a _"consumer product"_, which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  _"Installation Information"_ for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+## 7. Additional Terms.
+
+  _"Additional permissions"_ are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+## 8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+## 9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+## 10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An _"entity transaction"_ is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+## 11. Patents.
+
+  A _"contributor"_ is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's _"essential patent claims"_ are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+## 12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+## 13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+## 14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+## 15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+## 16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+## 17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+# END OF TERMS AND CONDITIONS
+--------------------------------------------------------------------------
+
+
+# How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program 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.
+
+    This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program comes with ABSOLUTELY NO WARRANTY; for details type 'show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type 'show c' for details.
+
+  The hypothetical commands _'show w'_ and _'show c'_ should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/NFD/INSTALL.md b/NFD/INSTALL.md
new file mode 100644
index 0000000..7ea2ce3
--- /dev/null
+++ b/NFD/INSTALL.md
@@ -0,0 +1,5 @@
+NFD Installation Instructions
+=============================
+
+See [`docs/INSTALL.rst`](https://github.com/named-data/NFD/blob/master/docs/INSTALL.rst)
+for detailed NFD installation instructions.
diff --git a/NFD/README-dev.md b/NFD/README-dev.md
new file mode 100644
index 0000000..17af2ee
--- /dev/null
+++ b/NFD/README-dev.md
@@ -0,0 +1,98 @@
+Notes for NFD developers
+========================
+
+Requirements
+------------
+
+Include the following license boilerplate into all `.hpp` and `.cpp` files:
+
+    /* -*- 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/>.
+     ////// [optional part] //////
+     *
+     * \author Author's Name <email@domain>
+     * \author Other Author's Name <another.email@domain>
+     ////// [end of optional part] //////
+     */
+
+Recommendations
+---------------
+
+NFD code is subject to NFD [code style](http://redmine.named-data.net/projects/nfd/wiki/CodeStyle).
+
+
+Running unit-tests
+------------------
+
+To run unit tests, NFD needs to be configured and build with unit test support:
+
+    ./waf configure --with-tests
+    ./waf
+
+The simplest way to run tests, is just to run the compiled binary without any parameters:
+
+    # Run core tests
+    ./build/unit-tests-core
+
+    # Run  NFD daemon tests
+    ./build/unit-tests-daemon
+
+    # 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/)
+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:
+
+    # Run only TCP Face test suite of NFD daemon tests (see tests/daemon/face/tcp.cpp)
+    ./build/unit-tests-daemon -t FaceTcp
+
+    # Run only test case EndToEnd4 from the same test suite
+    ./build/unit-tests-daemon -t FaceTcp/EndToEnd4
+
+    # Run Basic test case from all core test suites
+    ./build/unit-tests-core -t */Basic
+
+By default, Boost.Test framework will produce verbose output only when a test case fails.
+If it is desired to see verbose output (result of each test assertion), add `-l all`
+option to `./build/unit-tests` command.  To see test progress, you can use `-l test_suite`
+or `-p` to show progress bar:
+
+    # Show report all log messages including the passed test notification
+    ./build/unit-tests-daemon -l all
+
+    # Show test suite messages
+    ./build/unit-tests-daemon -l test_suite
+
+    # Show nothing
+    ./build/unit-tests-daemon -l nothing
+
+    # Show progress bar
+    ./build/unit-tests-core -p
+
+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/)
+website.
diff --git a/NFD/README.md b/NFD/README.md
new file mode 100644
index 0000000..455be28
--- /dev/null
+++ b/NFD/README.md
@@ -0,0 +1,42 @@
+NFD - Named Data Networking Forwarding Daemon
+=============================================
+
+For complete documentation, including step-by-step installation instructions and
+tutorials, please visit the [NFD homepage](http://named-data.net/doc/NFD/).
+
+## 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
+release, NFD will become a core component of the
+[NDN Platform](http://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
+centerpiece of our committement to making NDN's core technology open and free to all
+Internet users and developers.  For more information about the licensing details and
+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/),
+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
+[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
+design emphasizes *modularity* and *extensibility* to allow easy experiments with new
+protocol features, algorithms, new applications.  We have not fully optimized the code for
+performance.  The intention is that performance optimizations are one type of experiments
+that developers can conduct by trying out different data structures and different
+algorithms; over time, better implementations may emerge within the same design framework.
+
+NFD will keep evolving in three aspects: improvement of the modularity framework, keeping
+up with the NDN protocol spec, and addition of other new features. We hope to keep the
+modular framework stable and lean, allowing researchers to implement and experiment with
+various features, some of which may eventually work into the protocol spec.
diff --git a/NFD/common.hpp b/NFD/common.hpp
new file mode 100644
index 0000000..8316184
--- /dev/null
+++ b/NFD/common.hpp
@@ -0,0 +1,111 @@
+/* -*- 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_COMMON_HPP
+#define NFD_COMMON_HPP
+
+#include "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
+#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 this feature, otherwise expands to nothing
+ */
+#ifdef HAVE_CXX_OVERRIDE
+#define DECL_OVERRIDE override
+#else
+#define DECL_OVERRIDE
+#endif
+
+#include <cstddef>
+#include <list>
+#include <set>
+#include <queue>
+#include <vector>
+
+#include <ndn-cxx/common.hpp>
+#include <ndn-cxx/interest.hpp>
+#include <ndn-cxx/data.hpp>
+#include <ndn-cxx/util/event-emitter.hpp> // deprecated
+#include <ndn-cxx/util/signal.hpp>
+
+#include <boost/algorithm/string.hpp>
+#include <boost/asio.hpp>
+#include <boost/assert.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/noncopyable.hpp>
+#include <boost/property_tree/ptree.hpp>
+
+namespace nfd {
+
+using std::size_t;
+
+using boost::noncopyable;
+
+using std::shared_ptr;
+using std::unique_ptr;
+using std::weak_ptr;
+using std::bad_weak_ptr;
+using std::make_shared;
+using std::enable_shared_from_this;
+
+using std::static_pointer_cast;
+using std::dynamic_pointer_cast;
+using std::const_pointer_cast;
+
+using std::function;
+using std::bind;
+using std::ref;
+using std::cref;
+
+using ndn::Interest;
+using ndn::Data;
+using ndn::Name;
+using ndn::Exclude;
+using ndn::Block;
+using ndn::util::EventEmitter; // deprecated
+
+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 name = ndn::name;
+namespace time = ndn::time;
+namespace signal = ndn::util::signal;
+
+} // namespace nfd
+
+#endif // NFD_COMMON_HPP
diff --git a/NFD/contrib/osx-launchd/README.md b/NFD/contrib/osx-launchd/README.md
new file mode 100644
index 0000000..68d3b9b
--- /dev/null
+++ b/NFD/contrib/osx-launchd/README.md
@@ -0,0 +1,138 @@
+Starting NFD on OSX >= 10.8
+===========================
+
+OSX provides a standard way to start system daemons, monitor their health, and restart
+when they die.
+
+Initial setup
+-------------
+
+Edit `net.named-data.nfd` and `net.named-data.nrd` correcting paths for `nfd` and `nfd`
+binaries, configuration file, and log files.
+
+    # Copy launchd.plist for nfd (forwarding daemon)
+    sudo cp net.named-data.nfd.plist /Library/LaunchDaemons/
+    sudo chown root /Library/LaunchDaemons/net.named-data.nfd.plist
+
+    # Copy launchd.plist for nrd (RIB management daemon)
+    sudo cp net.named-data.nrd.plist /Library/LaunchDaemons/
+    sudo chown root /Library/LaunchDaemons/net.named-data.nrd.plist
+
+### Assumptions in the default scripts
+
+* `nfd` and `nrd` are installed into `/usr/local/bin`
+* Configuration file is `/usr/local/etc/ndn/nfd.conf`
+* `nfd` will be run as root
+* `nrd` will be run as user `ndn` and group `ndn`
+* Log files will be written to `/usr/local/var/log/ndn` folder, which is owned by user `ndn`
+
+### Creating users
+
+If `ndn` user does not exists, it needs to be manually created (procedure copied from
+[macports script](https://trac.macports.org/browser/trunk/base/src/port1.0/portutil.tcl)).
+Update uid/gid if 6363 is already used.
+
+    # Create user `ndn`
+    sudo dscl . -create /Users/ndn UniqueID 6363
+
+    # These are implicitly added on Mac OSX Lion.  AuthenticationAuthority
+    # causes the user to be visible in the Users & Groups Preference Pane,
+    # and the others are just noise, so delete them.
+    # https://trac.macports.org/ticket/30168
+    sudo dscl . -delete /Users/ndn AuthenticationAuthority
+    sudo dscl . -delete /Users/ndn PasswordPolicyOptions
+    sudo dscl . -delete /Users/ndn dsAttrTypeNative:KerberosKeys
+    sudo dscl . -delete /Users/ndn dsAttrTypeNative:ShadowHashData
+
+    sudo dscl . -create /Users/ndn RealName "NDN User"
+    sudo dscl . -create /Users/ndn Password "{*}"
+    sudo dscl . -create /Users/ndn PrimaryGroupID 6363
+    sudo dscl . -create /Users/ndn NFSHomeDirectory /var/empty
+    sudo dscl . -create /Users/ndn UserShell /usr/bin/false
+
+    # Create group `ndn`
+    sudo dscl . -create /Groupsndn Password "{*}"
+    sudo dscl . -create /Groups/ndn RealName "NDN User"
+    sudo dscl . -create /Groups/ndn PrimaryGroupID 6363
+
+### Creating folders
+
+Folder `/usr/local/var/log/ndn` should be created and assigned proper user and group:
+
+    sudo mkdir -p /usr/local/var/log/ndn
+    sudo chown -R ndn:ndn /usr/local/var/log/ndn
+
+`HOME` directories for `nfd` and `nrd` should be created and configured with correct
+library's config file and contain proper NDN security credentials for signing Data
+packets.  This is necessary since default private key storage on OSX (`osx-keychain`) does
+not support non-interactive access, and file-based private key storage needs to be used:
+
+    # Generate self-signed NDN certificate for nfd (owned by root)
+    sudo mkdir -p /usr/local/var/lib/ndn/nfd/.ndn
+    sudo sh -c 'echo tpm=file > /usr/local/var/lib/ndn/nfd/.ndn/client.conf'
+    sudo HOME=/usr/local/var/lib/ndn/nfd ndnsec-keygen /localhost/daemons/nfd | \
+      sudo HOME=/usr/local/var/lib/ndn/nfd ndnsec-install-cert -
+
+    # Generate self-signed NDN certificate for nrd (owned by ndn)
+    sudo mkdir -p /usr/local/var/lib/ndn/nrd/.ndn
+    sudo chown -R ndn:ndn /usr/local/var/lib/ndn/nrd
+    sudo -u ndn -g ndn sh -c 'echo tpm=file > /usr/local/var/lib/ndn/nrd/.ndn/client.conf'
+    sudo -u ndn -g ndn HOME=/usr/local/var/lib/ndn/nrd ndnsec-keygen /localhost/daemons/nrd | \
+      sudo -u ndn -g ndn HOME=/usr/local/var/lib/ndn/nrd ndnsec-install-cert -
+
+### Configuring NFD's security
+
+NFD sample configuration allows anybody to create faces, add nexthops to FIB,
+and set strategy choice for namespaces.  While such settings could be a good start, it is
+generally not a good idea to run NFD in this mode.
+
+While thorough discussion about security configuration of NFD is outside the scope of this
+document, at least the following change should be done to nfd.conf in authorize section:
+
+    authorizations
+    {
+      authorize
+      {
+        certfile certs/localhost_daemons_nrd.ndncert
+        privileges
+        {
+            faces
+            fib
+            strategy-choice
+        }
+      }
+
+      authorize
+      {
+        certfile any
+        privileges
+        {
+            faces
+            strategy-choice
+        }
+      }
+    }
+
+While this configuration still allows management of faces and updating strategy choice by
+anybody, only NFD's RIB Manager Daemon (`nrd`) is allowed to manage FIB.
+
+As the final step to make this configuration work, nrd's self-signed certificate needs to
+be exported into `localhost_daemons_nrd.ndncert` file:
+
+    sudo mkdir /usr/local/etc/ndn/certs
+    sudo sh -c 'sudo -u ndn -g ndn HOME=/usr/local/var/lib/ndn/nrd \
+      ndnsec-dump-certificate -i /localhost/daemons/nrd \
+      > /usr/local/etc/ndn/certs/localhost_daemons_nrd.ndncert'
+
+
+Enable auto-start
+-----------------
+
+    sudo launchctl load -w /Library/LaunchDaemons/net.named-data.nfd.plist
+    sudo launchctl load -w /Library/LaunchDaemons/net.named-data.nrd.plist
+
+Disable auto-start
+------------------
+
+    sudo launchctl unload -w /Library/LaunchDaemons/net.named-data.nfd.plist
+    sudo launchctl unload -w /Library/LaunchDaemons/net.named-data.nrd.plist
diff --git a/NFD/contrib/osx-launchd/net.named-data.nfd.plist b/NFD/contrib/osx-launchd/net.named-data.nfd.plist
new file mode 100644
index 0000000..a8da970
--- /dev/null
+++ b/NFD/contrib/osx-launchd/net.named-data.nfd.plist
@@ -0,0 +1,23 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
+"http://www.apple.com/DTDs/PropertyList-1.0.dtd" >
+<plist version='1.0'>
+<dict>
+<key>Label</key><string>net.named-data.nfd</string>
+<key>ProgramArguments</key>
+<array>
+  <string>/usr/local/bin/nfd</string>
+  <string>--config</string>
+  <string>/usr/local/etc/ndn/nfd.conf</string>
+</array>
+<key>EnvironmentVariables</key>
+<dict>
+  <key>HOME</key><string>/usr/local/var/lib/ndn/nfd</string>
+</dict>
+<key>Debug</key><true/>
+<key>Disabled</key><true/>
+<key>KeepAlive</key><true/>
+<key>StandardErrorPath</key><string>/usr/local/var/log/ndn/nfd.log</string>
+<key>ProcessType</key><string>Background</string>
+</dict>
+</plist>
diff --git a/NFD/contrib/osx-launchd/net.named-data.nrd.plist b/NFD/contrib/osx-launchd/net.named-data.nrd.plist
new file mode 100644
index 0000000..3cd7aaa
--- /dev/null
+++ b/NFD/contrib/osx-launchd/net.named-data.nrd.plist
@@ -0,0 +1,25 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
+"http://www.apple.com/DTDs/PropertyList-1.0.dtd" >
+<plist version='1.0'>
+<dict>
+<key>Label</key><string>net.named-data.nrd</string>
+<key>ProgramArguments</key>
+<array>
+  <string>/usr/local/bin/nrd</string>
+  <string>--config</string>
+  <string>/usr/local/etc/ndn/nfd.conf</string>
+</array>
+<key>UserName</key><string>ndn</string>
+<key>GroupName</key><string>ndn</string>
+<key>EnvironmentVariables</key>
+<dict>
+  <key>HOME</key><string>/usr/local/var/lib/ndn/nrd</string>
+</dict>
+<key>Debug</key><true/>
+<key>Disabled</key><true/>
+<key>KeepAlive</key><true/>
+<key>StandardErrorPath</key><string>/usr/local/var/log/ndn/nrd.log</string>
+<key>ProcessType</key><string>Background</string>
+</dict>
+</plist>
diff --git a/NFD/contrib/upstart/README.md b/NFD/contrib/upstart/README.md
new file mode 100644
index 0000000..760e8a8
--- /dev/null
+++ b/NFD/contrib/upstart/README.md
@@ -0,0 +1,144 @@
+Starting NFD on Linux with upstart
+==================================
+
+Some Linux distributions, such as Ubuntu, use [upstart](http://upstart.ubuntu.com/) as a
+standard mechanism to start system daemons, monitor their health, and restart
+when they die.
+
+Initial setup
+-------------
+
+Edit `nfd.conf` and `nrd.conf` correcting paths for `nfd` and `nfd` binaries,
+configuration file, and log files.
+
+    # Copy upstart config file for nfd (forwarding daemon)
+    sudo cp nfd.conf /etc/init/
+
+    # Copy upstart config file for nrd (RIB management daemon)
+    sudo cp nrd.conf /etc/init/
+
+    # Copy upstart config file for nfd-watcher (will restart NFD when network change detected)
+    sudo cp nfd-watcher.conf /etc/init/
+
+### Assumptions in the default scripts
+
+* `nfd` and `nrd` are installed into `/usr/local/bin`
+* Configuration file is `/usr/local/etc/ndn/nfd.conf`
+* `nfd` will be run as root
+* `nrd` will be run as user `ndn` and group `ndn`
+* Log files will be written to `/usr/local/var/log/ndn` folder, which is owned by user `ndn`
+* Whenever network connectivity changes, both `nfd` and `nrd` are restarted
+
+### Creating users
+
+If `ndn` user and group does not exists, they need to be manually created.
+
+    # Create group `ndn`
+    addgroup --system ndn
+
+    # Create user `ndn`
+    sudo adduser --system \
+                 --disabled-login \
+                 --ingroup ndn \
+                 --home /nonexistent \
+                 --gecos "NDN User" \
+                 --shell /bin/false \
+                 ndn
+
+
+### Creating folders
+
+Folder `/usr/local/var/log/ndn` should be created and assigned proper user and group:
+
+    sudo mkdir -p /usr/local/var/log/ndn
+    sudo chown -R ndn:ndn /usr/local/var/log/ndn
+
+`HOME` directories for `nfd` and `nrd` should be created prior to starting.  This is
+necessary to manage unique security credentials for the deamons.
+
+    # Create HOME and generate self-signed NDN certificate for nfd
+    sudo mkdir -p /usr/local/var/lib/ndn/nfd/.ndn
+    sudo HOME=/usr/local/var/lib/ndn/nfd ndnsec-keygen /localhost/daemons/nfd | \
+      sudo HOME=/usr/local/var/lib/ndn/nfd ndnsec-install-cert -
+
+    # Create HOME and generate self-signed NDN certificate for nrd
+    sudo mkdir -p /usr/local/var/lib/ndn/nrd/.ndn
+    sudo chown -R ndn:ndn /usr/local/var/lib/ndn/nrd
+    sudo -u ndn -g ndn HOME=/usr/local/var/lib/ndn/nrd ndnsec-keygen /localhost/daemons/nrd | \
+      sudo -u ndn -g ndn HOME=/usr/local/var/lib/ndn/nrd ndnsec-install-cert -
+
+### Configuring NFD's security
+
+NFD sample configuration allows anybody to create faces, add nexthops to FIB, and set
+strategy choice for namespaces.  While such settings could be a good start, it is
+generally not a good idea to run NFD in this mode.
+
+While thorough discussion about security configuration of NFD is outside the scope of this
+document, at least the following change should be done to ``nfd.conf`` in authorize
+section:
+
+    authorizations
+    {
+      authorize
+      {
+        certfile certs/localhost_daemons_nrd.ndncert
+        privileges
+        {
+            faces
+            fib
+            strategy-choice
+        }
+      }
+
+      authorize
+      {
+        certfile any
+        privileges
+        {
+            faces
+            strategy-choice
+        }
+      }
+    }
+
+While this configuration still allows management of faces and updating strategy choice by
+anybody, only NFD's RIB Manager Daemon (`nrd`) is allowed to manage FIB.
+
+As the final step to make this configuration work, nrd's self-signed certificate needs to
+be exported into `localhost_daemons_nrd.ndncert` file:
+
+    sudo mkdir /usr/local/etc/ndn/certs
+    sudo sh -c 'sudo -u ndn -g ndn HOME=/usr/local/var/lib/ndn/nrd \
+      ndnsec-dump-certificate -i /localhost/daemons/nrd \
+      > /usr/local/etc/ndn/certs/localhost_daemons_nrd.ndncert'
+
+
+Enable auto-start
+-----------------
+
+After copying the provided upstart scripts, `nfd` and `nrd` daemons will automatically run
+after the reboot.  To manually start them, use the following commands:
+
+    sudo start nfd
+    # nrd will be automatically started by upstart
+
+Note that an additional upstart job, ``nfd-watcher``, will automatically monitor for
+network connectivity changes, such as when network interface gets connected, disconnected,
+or IP addresses of the network interface get updated.  When ``nfd-watcher`` detects the
+event, it will restart `nfd` and `nrd`.
+
+Disable auto-start
+------------------
+
+To stop `nrd` and `nfd` daemon, use the following commands:
+
+    sudo stop nfd
+    # nrd will be automatically stopped by upstart
+
+Note that as long as upstart files are present in `/etc/init/`, the daemons will
+automatically start after the reboot.  To permanently stop `nfd` and `nrd` daemons, delete
+the upstart files:
+
+    sudo rm /etc/init/nfd.conf
+    sudo rm /etc/init/nrd.conf
+    sudo rm /etc/init/nfd-watcher.conf
diff --git a/NFD/contrib/upstart/nfd-watcher.conf b/NFD/contrib/upstart/nfd-watcher.conf
new file mode 100644
index 0000000..392af52
--- /dev/null
+++ b/NFD/contrib/upstart/nfd-watcher.conf
@@ -0,0 +1,13 @@
+# nfd-watcher.conf
+#
+# Restarting NDN Forwarding Daemon on network connectivity changes
+
+start on (net-device-up or
+          net-device-removed or
+          net-device-changed)
+task
+
+script
+  status nfd | grep -q start/ || stop
+  restart nfd
+end script
diff --git a/NFD/contrib/upstart/nfd.conf b/NFD/contrib/upstart/nfd.conf
new file mode 100644
index 0000000..97179a2
--- /dev/null
+++ b/NFD/contrib/upstart/nfd.conf
@@ -0,0 +1,17 @@
+# nfd.conf
+#
+# NDN Forwarding Daemon
+
+description "NDN forwarding daemon"
+
+start on (local-filesystems and net-device-up IFACE!=lo)
+stop on runlevel [!2345]
+
+respawn
+respawn limit unlimited
+
+env HOME=/usr/local/var/lib/ndn/nfd
+export HOME
+
+exec /usr/local/bin/nfd --config /usr/local/etc/ndn/nfd.conf 2>> /usr/local/var/log/ndn/nfd.log
+post-stop exec sleep 2
diff --git a/NFD/contrib/upstart/nrd.conf b/NFD/contrib/upstart/nrd.conf
new file mode 100644
index 0000000..574b81c
--- /dev/null
+++ b/NFD/contrib/upstart/nrd.conf
@@ -0,0 +1,22 @@
+# nrd.conf
+#
+# NDN RIB Manager Daemon
+
+description "NDN RIB Manager Daemon"
+
+start on started  nfd
+stop  on stopping nfd
+
+respawn
+respawn limit unlimited
+
+setuid ndn
+setgid ndn
+
+pre-start exec sleep 2
+script
+  export HOME=/usr/local/var/lib/ndn/nrd
+  /usr/local/bin/nrd --config /usr/local/etc/ndn/nfd.conf 2>> /usr/local/var/log/ndn/nrd.log
+end script
+
+post-stop exec sleep 2
diff --git a/NFD/core/city-hash.cpp b/NFD/core/city-hash.cpp
new file mode 100644
index 0000000..b8a7791
--- /dev/null
+++ b/NFD/core/city-hash.cpp
@@ -0,0 +1,650 @@
+// Copyright (c) 2011 Google, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+// CityHash, by Geoff Pike and Jyrki Alakuijala
+//
+// This file provides CityHash64() and related functions.
+//
+// It's probably possible to create even faster hash functions by
+// writing a program that systematically explores some of the space of
+// possible hash functions, by using SIMD instructions, or by
+// compromising on hash quality.
+
+//#include "config.h"
+#include "city-hash.hpp"
+#include <algorithm>
+#include <string.h>  // for memcpy and memset
+
+using namespace std;
+
+
+static uint64 UNALIGNED_LOAD64(const char *p) {
+  uint64 result;
+  memcpy(&result, p, sizeof(result));
+  return result;
+}
+
+static uint32 UNALIGNED_LOAD32(const char *p) {
+  uint32 result;
+  memcpy(&result, p, sizeof(result));
+  return result;
+}
+
+#ifdef _MSC_VER
+
+#include <stdlib.h>
+#define bswap_32(x) _byteswap_ulong(x)
+#define bswap_64(x) _byteswap_uint64(x)
+
+#elif defined(__APPLE__)
+
+// Mac OS X / Darwin features
+#include <libkern/OSByteOrder.h>
+#define bswap_32(x) OSSwapInt32(x)
+#define bswap_64(x) OSSwapInt64(x)
+
+#elif defined(__NetBSD__)
+
+#include <sys/types.h>
+#include <machine/bswap.h>
+#if defined(__BSWAP_RENAME) && !defined(__bswap_32)
+#define bswap_32(x) bswap32(x)
+#define bswap_64(x) bswap64(x)
+#endif
+
+
+#elif defined(__FreeBSD__)
+
+// Based on https://code.google.com/p/freebsd/source/browse/sys/ofed/include/byteswap.h?spec=svn38a8350a629d959c8c5509221cd07ffb891b5a77&r=38a8350a629d959c8c5509221cd07ffb891b5a77
+
+#include <sys/types.h>
+#include <sys/endian.h>
+#define bswap_16(x) bswap16(x)
+#define bswap_32(x) bswap32(x)
+#define bswap_64(x) bswap64(x)
+
+#else
+
+#include <byteswap.h>
+
+#endif
+
+#ifdef WORDS_BIGENDIAN
+#define uint32_in_expected_order(x) (bswap_32(x))
+#define uint64_in_expected_order(x) (bswap_64(x))
+#else
+#define uint32_in_expected_order(x) (x)
+#define uint64_in_expected_order(x) (x)
+#endif
+
+#if !defined(LIKELY)
+#if HAVE_BUILTIN_EXPECT
+#define LIKELY(x) (__builtin_expect(!!(x), 1))
+#else
+#define LIKELY(x) (x)
+#endif
+#endif
+
+static uint64 Fetch64(const char *p) {
+  return uint64_in_expected_order(UNALIGNED_LOAD64(p));
+}
+
+static uint32 Fetch32(const char *p) {
+  return uint32_in_expected_order(UNALIGNED_LOAD32(p));
+}
+
+// Some primes between 2^63 and 2^64 for various uses.
+static const uint64 k0 = 0xc3a5c85c97cb3127ULL;
+static const uint64 k1 = 0xb492b66fbe98f273ULL;
+static const uint64 k2 = 0x9ae16a3b2f90404fULL;
+
+// Magic numbers for 32-bit hashing.  Copied from Murmur3.
+static const uint32_t c1 = 0xcc9e2d51;
+static const uint32_t c2 = 0x1b873593;
+
+// A 32-bit to 32-bit integer hash copied from Murmur3.
+static uint32 fmix(uint32 h)
+{
+  h ^= h >> 16;
+  h *= 0x85ebca6b;
+  h ^= h >> 13;
+  h *= 0xc2b2ae35;
+  h ^= h >> 16;
+  return h;
+}
+
+static uint32 Rotate32(uint32 val, int shift) {
+  // Avoid shifting by 32: doing so yields an undefined result.
+  return shift == 0 ? val : ((val >> shift) | (val << (32 - shift)));
+}
+
+#undef PERMUTE3
+#define PERMUTE3(a, b, c) do { std::swap(a, b); std::swap(a, c); } while (0)
+
+static uint32 Mur(uint32 a, uint32 h) {
+  // Helper from Murmur3 for combining two 32-bit values.
+  a *= c1;
+  a = Rotate32(a, 17);
+  a *= c2;
+  h ^= a;
+  h = Rotate32(h, 19);
+  return h * 5 + 0xe6546b64;
+}
+
+static uint32 Hash32Len13to24(const char *s, size_t len) {
+  uint32 a = Fetch32(s - 4 + (len >> 1));
+  uint32 b = Fetch32(s + 4);
+  uint32 c = Fetch32(s + len - 8);
+  uint32 d = Fetch32(s + (len >> 1));
+  uint32 e = Fetch32(s);
+  uint32 f = Fetch32(s + len - 4);
+  uint32 h = len;
+
+  return fmix(Mur(f, Mur(e, Mur(d, Mur(c, Mur(b, Mur(a, h)))))));
+}
+
+static uint32 Hash32Len0to4(const char *s, size_t len) {
+  uint32 b = 0;
+  uint32 c = 9;
+  for (size_t i = 0; i < len; i++) {
+    signed char v = s[i];
+    b = b * c1 + v;
+    c ^= b;
+  }
+  return fmix(Mur(b, Mur(len, c)));
+}
+
+static uint32 Hash32Len5to12(const char *s, size_t len) {
+  uint32 a = len, b = len * 5, c = 9, d = b;
+  a += Fetch32(s);
+  b += Fetch32(s + len - 4);
+  c += Fetch32(s + ((len >> 1) & 4));
+  return fmix(Mur(c, Mur(b, Mur(a, d))));
+}
+
+uint32 CityHash32(const char *s, size_t len) {
+  if (len <= 24) {
+    return len <= 12 ?
+        (len <= 4 ? Hash32Len0to4(s, len) : Hash32Len5to12(s, len)) :
+        Hash32Len13to24(s, len);
+  }
+
+  // len > 24
+  uint32 h = len, g = c1 * len, f = g;
+  uint32 a0 = Rotate32(Fetch32(s + len - 4) * c1, 17) * c2;
+  uint32 a1 = Rotate32(Fetch32(s + len - 8) * c1, 17) * c2;
+  uint32 a2 = Rotate32(Fetch32(s + len - 16) * c1, 17) * c2;
+  uint32 a3 = Rotate32(Fetch32(s + len - 12) * c1, 17) * c2;
+  uint32 a4 = Rotate32(Fetch32(s + len - 20) * c1, 17) * c2;
+  h ^= a0;
+  h = Rotate32(h, 19);
+  h = h * 5 + 0xe6546b64;
+  h ^= a2;
+  h = Rotate32(h, 19);
+  h = h * 5 + 0xe6546b64;
+  g ^= a1;
+  g = Rotate32(g, 19);
+  g = g * 5 + 0xe6546b64;
+  g ^= a3;
+  g = Rotate32(g, 19);
+  g = g * 5 + 0xe6546b64;
+  f += a4;
+  f = Rotate32(f, 19);
+  f = f * 5 + 0xe6546b64;
+  size_t iters = (len - 1) / 20;
+  do {
+    uint32 a0 = Rotate32(Fetch32(s) * c1, 17) * c2;
+    uint32 a1 = Fetch32(s + 4);
+    uint32 a2 = Rotate32(Fetch32(s + 8) * c1, 17) * c2;
+    uint32 a3 = Rotate32(Fetch32(s + 12) * c1, 17) * c2;
+    uint32 a4 = Fetch32(s + 16);
+    h ^= a0;
+    h = Rotate32(h, 18);
+    h = h * 5 + 0xe6546b64;
+    f += a1;
+    f = Rotate32(f, 19);
+    f = f * c1;
+    g += a2;
+    g = Rotate32(g, 18);
+    g = g * 5 + 0xe6546b64;
+    h ^= a3 + a1;
+    h = Rotate32(h, 19);
+    h = h * 5 + 0xe6546b64;
+    g ^= a4;
+    g = bswap_32(g) * 5;
+    h += a4 * 5;
+    h = bswap_32(h);
+    f += a0;
+    PERMUTE3(f, h, g);
+    s += 20;
+  } while (--iters != 0);
+  g = Rotate32(g, 11) * c1;
+  g = Rotate32(g, 17) * c1;
+  f = Rotate32(f, 11) * c1;
+  f = Rotate32(f, 17) * c1;
+  h = Rotate32(h + g, 19);
+  h = h * 5 + 0xe6546b64;
+  h = Rotate32(h, 17) * c1;
+  h = Rotate32(h + f, 19);
+  h = h * 5 + 0xe6546b64;
+  h = Rotate32(h, 17) * c1;
+  return h;
+}
+
+// Bitwise right rotate.  Normally this will compile to a single
+// instruction, especially if the shift is a manifest constant.
+static uint64 Rotate(uint64 val, int shift) {
+  // Avoid shifting by 64: doing so yields an undefined result.
+  return shift == 0 ? val : ((val >> shift) | (val << (64 - shift)));
+}
+
+static uint64 ShiftMix(uint64 val) {
+  return val ^ (val >> 47);
+}
+
+static uint64 HashLen16(uint64 u, uint64 v) {
+  return Hash128to64(uint128(u, v));
+}
+
+static uint64 HashLen16(uint64 u, uint64 v, uint64 mul) {
+  // Murmur-inspired hashing.
+  uint64 a = (u ^ v) * mul;
+  a ^= (a >> 47);
+  uint64 b = (v ^ a) * mul;
+  b ^= (b >> 47);
+  b *= mul;
+  return b;
+}
+
+static uint64 HashLen0to16(const char *s, size_t len) {
+  if (len >= 8) {
+    uint64 mul = k2 + len * 2;
+    uint64 a = Fetch64(s) + k2;
+    uint64 b = Fetch64(s + len - 8);
+    uint64 c = Rotate(b, 37) * mul + a;
+    uint64 d = (Rotate(a, 25) + b) * mul;
+    return HashLen16(c, d, mul);
+  }
+  if (len >= 4) {
+    uint64 mul = k2 + len * 2;
+    uint64 a = Fetch32(s);
+    return HashLen16(len + (a << 3), Fetch32(s + len - 4), mul);
+  }
+  if (len > 0) {
+    uint8 a = s[0];
+    uint8 b = s[len >> 1];
+    uint8 c = s[len - 1];
+    uint32 y = static_cast<uint32>(a) + (static_cast<uint32>(b) << 8);
+    uint32 z = len + (static_cast<uint32>(c) << 2);
+    return ShiftMix(y * k2 ^ z * k0) * k2;
+  }
+  return k2;
+}
+
+// This probably works well for 16-byte strings as well, but it may be overkill
+// in that case.
+static uint64 HashLen17to32(const char *s, size_t len) {
+  uint64 mul = k2 + len * 2;
+  uint64 a = Fetch64(s) * k1;
+  uint64 b = Fetch64(s + 8);
+  uint64 c = Fetch64(s + len - 8) * mul;
+  uint64 d = Fetch64(s + len - 16) * k2;
+  return HashLen16(Rotate(a + b, 43) + Rotate(c, 30) + d,
+                   a + Rotate(b + k2, 18) + c, mul);
+}
+
+// Return a 16-byte hash for 48 bytes.  Quick and dirty.
+// Callers do best to use "random-looking" values for a and b.
+static pair<uint64, uint64> WeakHashLen32WithSeeds(
+    uint64 w, uint64 x, uint64 y, uint64 z, uint64 a, uint64 b) {
+  a += w;
+  b = Rotate(b + a + z, 21);
+  uint64 c = a;
+  a += x;
+  a += y;
+  b += Rotate(a, 44);
+  return make_pair(a + z, b + c);
+}
+
+// Return a 16-byte hash for s[0] ... s[31], a, and b.  Quick and dirty.
+static pair<uint64, uint64> WeakHashLen32WithSeeds(
+    const char* s, uint64 a, uint64 b) {
+  return WeakHashLen32WithSeeds(Fetch64(s),
+                                Fetch64(s + 8),
+                                Fetch64(s + 16),
+                                Fetch64(s + 24),
+                                a,
+                                b);
+}
+
+// Return an 8-byte hash for 33 to 64 bytes.
+static uint64 HashLen33to64(const char *s, size_t len) {
+  uint64 mul = k2 + len * 2;
+  uint64 a = Fetch64(s) * k2;
+  uint64 b = Fetch64(s + 8);
+  uint64 c = Fetch64(s + len - 24);
+  uint64 d = Fetch64(s + len - 32);
+  uint64 e = Fetch64(s + 16) * k2;
+  uint64 f = Fetch64(s + 24) * 9;
+  uint64 g = Fetch64(s + len - 8);
+  uint64 h = Fetch64(s + len - 16) * mul;
+  uint64 u = Rotate(a + g, 43) + (Rotate(b, 30) + c) * 9;
+  uint64 v = ((a + g) ^ d) + f + 1;
+  uint64 w = bswap_64((u + v) * mul) + h;
+  uint64 x = Rotate(e + f, 42) + c;
+  uint64 y = (bswap_64((v + w) * mul) + g) * mul;
+  uint64 z = e + f + c;
+  a = bswap_64((x + z) * mul + y) + b;
+  b = ShiftMix((z + a) * mul + d + h) * mul;
+  return b + x;
+}
+
+uint64 CityHash64(const char *s, size_t len) {
+  if (len <= 32) {
+    if (len <= 16) {
+      return HashLen0to16(s, len);
+    } else {
+      return HashLen17to32(s, len);
+    }
+  } else if (len <= 64) {
+    return HashLen33to64(s, len);
+  }
+
+  // For strings over 64 bytes we hash the end first, and then as we
+  // loop we keep 56 bytes of state: v, w, x, y, and z.
+  uint64 x = Fetch64(s + len - 40);
+  uint64 y = Fetch64(s + len - 16) + Fetch64(s + len - 56);
+  uint64 z = HashLen16(Fetch64(s + len - 48) + len, Fetch64(s + len - 24));
+  pair<uint64, uint64> v = WeakHashLen32WithSeeds(s + len - 64, len, z);
+  pair<uint64, uint64> w = WeakHashLen32WithSeeds(s + len - 32, y + k1, x);
+  x = x * k1 + Fetch64(s);
+
+  // Decrease len to the nearest multiple of 64, and operate on 64-byte chunks.
+  len = (len - 1) & ~static_cast<size_t>(63);
+  do {
+    x = Rotate(x + y + v.first + Fetch64(s + 8), 37) * k1;
+    y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1;
+    x ^= w.second;
+    y += v.first + Fetch64(s + 40);
+    z = Rotate(z + w.first, 33) * k1;
+    v = WeakHashLen32WithSeeds(s, v.second * k1, x + w.first);
+    w = WeakHashLen32WithSeeds(s + 32, z + w.second, y + Fetch64(s + 16));
+    std::swap(z, x);
+    s += 64;
+    len -= 64;
+  } while (len != 0);
+  return HashLen16(HashLen16(v.first, w.first) + ShiftMix(y) * k1 + z,
+                   HashLen16(v.second, w.second) + x);
+}
+
+uint64 CityHash64WithSeed(const char *s, size_t len, uint64 seed) {
+  return CityHash64WithSeeds(s, len, k2, seed);
+}
+
+uint64 CityHash64WithSeeds(const char *s, size_t len,
+                           uint64 seed0, uint64 seed1) {
+  return HashLen16(CityHash64(s, len) - seed0, seed1);
+}
+
+// A subroutine for CityHash128().  Returns a decent 128-bit hash for strings
+// of any length representable in signed long.  Based on City and Murmur.
+static uint128 CityMurmur(const char *s, size_t len, uint128 seed) {
+  uint64 a = Uint128Low64(seed);
+  uint64 b = Uint128High64(seed);
+  uint64 c = 0;
+  uint64 d = 0;
+  signed long l = len - 16;
+  if (l <= 0) {  // len <= 16
+    a = ShiftMix(a * k1) * k1;
+    c = b * k1 + HashLen0to16(s, len);
+    d = ShiftMix(a + (len >= 8 ? Fetch64(s) : c));
+  } else {  // len > 16
+    c = HashLen16(Fetch64(s + len - 8) + k1, a);
+    d = HashLen16(b + len, c + Fetch64(s + len - 16));
+    a += d;
+    do {
+      a ^= ShiftMix(Fetch64(s) * k1) * k1;
+      a *= k1;
+      b ^= a;
+      c ^= ShiftMix(Fetch64(s + 8) * k1) * k1;
+      c *= k1;
+      d ^= c;
+      s += 16;
+      l -= 16;
+    } while (l > 0);
+  }
+  a = HashLen16(a, c);
+  b = HashLen16(d, b);
+  return uint128(a ^ b, HashLen16(b, a));
+}
+
+uint128 CityHash128WithSeed(const char *s, size_t len, uint128 seed) {
+  if (len < 128) {
+    return CityMurmur(s, len, seed);
+  }
+
+  // We expect len >= 128 to be the common case.  Keep 56 bytes of state:
+  // v, w, x, y, and z.
+  pair<uint64, uint64> v, w;
+  uint64 x = Uint128Low64(seed);
+  uint64 y = Uint128High64(seed);
+  uint64 z = len * k1;
+  v.first = Rotate(y ^ k1, 49) * k1 + Fetch64(s);
+  v.second = Rotate(v.first, 42) * k1 + Fetch64(s + 8);
+  w.first = Rotate(y + z, 35) * k1 + x;
+  w.second = Rotate(x + Fetch64(s + 88), 53) * k1;
+
+  // This is the same inner loop as CityHash64(), manually unrolled.
+  do {
+    x = Rotate(x + y + v.first + Fetch64(s + 8), 37) * k1;
+    y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1;
+    x ^= w.second;
+    y += v.first + Fetch64(s + 40);
+    z = Rotate(z + w.first, 33) * k1;
+    v = WeakHashLen32WithSeeds(s, v.second * k1, x + w.first);
+    w = WeakHashLen32WithSeeds(s + 32, z + w.second, y + Fetch64(s + 16));
+    std::swap(z, x);
+    s += 64;
+    x = Rotate(x + y + v.first + Fetch64(s + 8), 37) * k1;
+    y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1;
+    x ^= w.second;
+    y += v.first + Fetch64(s + 40);
+    z = Rotate(z + w.first, 33) * k1;
+    v = WeakHashLen32WithSeeds(s, v.second * k1, x + w.first);
+    w = WeakHashLen32WithSeeds(s + 32, z + w.second, y + Fetch64(s + 16));
+    std::swap(z, x);
+    s += 64;
+    len -= 128;
+  } while (LIKELY(len >= 128));
+  x += Rotate(v.first + z, 49) * k0;
+  y = y * k0 + Rotate(w.second, 37);
+  z = z * k0 + Rotate(w.first, 27);
+  w.first *= 9;
+  v.first *= k0;
+  // If 0 < len < 128, hash up to 4 chunks of 32 bytes each from the end of s.
+  for (size_t tail_done = 0; tail_done < len; ) {
+    tail_done += 32;
+    y = Rotate(x + y, 42) * k0 + v.second;
+    w.first += Fetch64(s + len - tail_done + 16);
+    x = x * k0 + w.first;
+    z += w.second + Fetch64(s + len - tail_done);
+    w.second += v.first;
+    v = WeakHashLen32WithSeeds(s + len - tail_done, v.first + z, v.second);
+    v.first *= k0;
+  }
+  // At this point our 56 bytes of state should contain more than
+  // enough information for a strong 128-bit hash.  We use two
+  // different 56-byte-to-8-byte hashes to get a 16-byte final result.
+  x = HashLen16(x, v.first);
+  y = HashLen16(y + z, w.first);
+  return uint128(HashLen16(x + v.second, w.second) + y,
+                 HashLen16(x + w.second, y + v.second));
+}
+
+uint128 CityHash128(const char *s, size_t len) {
+  return len >= 16 ?
+      CityHash128WithSeed(s + 16, len - 16,
+                          uint128(Fetch64(s), Fetch64(s + 8) + k0)) :
+      CityHash128WithSeed(s, len, uint128(k0, k1));
+}
+
+// NFD NOTE
+// The following code block is commented out due to the following reasons.
+// - It requires the "citycrc.h" header file, which is not included in the
+//   NFD code base.
+// - The functions defined below are not used by the current NFD
+//   implementation.
+// The header file "citycrc.h" is available at
+// https://code.google.com/p/cityhash/source/browse/trunk/src/citycrc.h
+
+/*
+#ifdef __SSE4_2__
+#include <citycrc.h>
+#include <nmmintrin.h>
+
+// Requires len >= 240.
+static void CityHashCrc256Long(const char *s, size_t len,
+                               uint32 seed, uint64 *result) {
+  uint64 a = Fetch64(s + 56) + k0;
+  uint64 b = Fetch64(s + 96) + k0;
+  uint64 c = result[0] = HashLen16(b, len);
+  uint64 d = result[1] = Fetch64(s + 120) * k0 + len;
+  uint64 e = Fetch64(s + 184) + seed;
+  uint64 f = 0;
+  uint64 g = 0;
+  uint64 h = c + d;
+  uint64 x = seed;
+  uint64 y = 0;
+  uint64 z = 0;
+
+  // 240 bytes of input per iter.
+  size_t iters = len / 240;
+  len -= iters * 240;
+  do {
+#undef CHUNK
+#define CHUNK(r)                                \
+    PERMUTE3(x, z, y);                          \
+    b += Fetch64(s);                            \
+    c += Fetch64(s + 8);                        \
+    d += Fetch64(s + 16);                       \
+    e += Fetch64(s + 24);                       \
+    f += Fetch64(s + 32);                       \
+    a += b;                                     \
+    h += f;                                     \
+    b += c;                                     \
+    f += d;                                     \
+    g += e;                                     \
+    e += z;                                     \
+    g += x;                                     \
+    z = _mm_crc32_u64(z, b + g);                \
+    y = _mm_crc32_u64(y, e + h);                \
+    x = _mm_crc32_u64(x, f + a);                \
+    e = Rotate(e, r);                           \
+    c += e;                                     \
+    s += 40
+
+    CHUNK(0); PERMUTE3(a, h, c);
+    CHUNK(33); PERMUTE3(a, h, f);
+    CHUNK(0); PERMUTE3(b, h, f);
+    CHUNK(42); PERMUTE3(b, h, d);
+    CHUNK(0); PERMUTE3(b, h, e);
+    CHUNK(33); PERMUTE3(a, h, e);
+  } while (--iters > 0);
+
+  while (len >= 40) {
+    CHUNK(29);
+    e ^= Rotate(a, 20);
+    h += Rotate(b, 30);
+    g ^= Rotate(c, 40);
+    f += Rotate(d, 34);
+    PERMUTE3(c, h, g);
+    len -= 40;
+  }
+  if (len > 0) {
+    s = s + len - 40;
+    CHUNK(33);
+    e ^= Rotate(a, 43);
+    h += Rotate(b, 42);
+    g ^= Rotate(c, 41);
+    f += Rotate(d, 40);
+  }
+  result[0] ^= h;
+  result[1] ^= g;
+  g += h;
+  a = HashLen16(a, g + z);
+  x += y << 32;
+  b += x;
+  c = HashLen16(c, z) + h;
+  d = HashLen16(d, e + result[0]);
+  g += e;
+  h += HashLen16(x, f);
+  e = HashLen16(a, d) + g;
+  z = HashLen16(b, c) + a;
+  y = HashLen16(g, h) + c;
+  result[0] = e + z + y + x;
+  a = ShiftMix((a + y) * k0) * k0 + b;
+  result[1] += a + result[0];
+  a = ShiftMix(a * k0) * k0 + c;
+  result[2] = a + result[1];
+  a = ShiftMix((a + e) * k0) * k0;
+  result[3] = a + result[2];
+}
+
+// Requires len < 240.
+static void CityHashCrc256Short(const char *s, size_t len, uint64 *result) {
+  char buf[240];
+  memcpy(buf, s, len);
+  memset(buf + len, 0, 240 - len);
+  CityHashCrc256Long(buf, 240, ~static_cast<uint32>(len), result);
+}
+
+void CityHashCrc256(const char *s, size_t len, uint64 *result) {
+  if (LIKELY(len >= 240)) {
+    CityHashCrc256Long(s, len, 0, result);
+  } else {
+    CityHashCrc256Short(s, len, result);
+  }
+}
+
+uint128 CityHashCrc128WithSeed(const char *s, size_t len, uint128 seed) {
+  if (len <= 900) {
+    return CityHash128WithSeed(s, len, seed);
+  } else {
+    uint64 result[4];
+    CityHashCrc256(s, len, result);
+    uint64 u = Uint128High64(seed) + result[0];
+    uint64 v = Uint128Low64(seed) + result[1];
+    return uint128(HashLen16(u, v + result[2]),
+                   HashLen16(Rotate(v, 32), u * k0 + result[3]));
+  }
+}
+
+uint128 CityHashCrc128(const char *s, size_t len) {
+  if (len <= 900) {
+    return CityHash128(s, len);
+  } else {
+    uint64 result[4];
+    CityHashCrc256(s, len, result);
+    return uint128(result[2], result[3]);
+  }
+}
+
+#endif
+*/
diff --git a/NFD/core/city-hash.hpp b/NFD/core/city-hash.hpp
new file mode 100644
index 0000000..54a90cb
--- /dev/null
+++ b/NFD/core/city-hash.hpp
@@ -0,0 +1,112 @@
+// Copyright (c) 2011 Google, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+// CityHash, by Geoff Pike and Jyrki Alakuijala
+//
+// http://code.google.com/p/cityhash/
+//
+// This file provides a few functions for hashing strings.  All of them are
+// high-quality functions in the sense that they pass standard tests such
+// as Austin Appleby's SMHasher.  They are also fast.
+//
+// For 64-bit x86 code, on short strings, we don't know of anything faster than
+// CityHash64 that is of comparable quality.  We believe our nearest competitor
+// is Murmur3.  For 64-bit x86 code, CityHash64 is an excellent choice for hash
+// tables and most other hashing (excluding cryptography).
+//
+// For 64-bit x86 code, on long strings, the picture is more complicated.
+// On many recent Intel CPUs, such as Nehalem, Westmere, Sandy Bridge, etc.,
+// CityHashCrc128 appears to be faster than all competitors of comparable
+// quality.  CityHash128 is also good but not quite as fast.  We believe our
+// nearest competitor is Bob Jenkins' Spooky.  We don't have great data for
+// other 64-bit CPUs, but for long strings we know that Spooky is slightly
+// faster than CityHash on some relatively recent AMD x86-64 CPUs, for example.
+// Note that CityHashCrc128 is declared in citycrc.h.
+//
+// For 32-bit x86 code, we don't know of anything faster than CityHash32 that
+// is of comparable quality.  We believe our nearest competitor is Murmur3A.
+// (On 64-bit CPUs, it is typically faster to use the other CityHash variants.)
+//
+// Functions in the CityHash family are not suitable for cryptography.
+//
+// Please see CityHash's README file for more details on our performance
+// measurements and so on.
+//
+// WARNING: This code has been only lightly tested on big-endian platforms!
+// It is known to work well on little-endian platforms that have a small penalty
+// for unaligned reads, such as current Intel and AMD moderate-to-high-end CPUs.
+// It should work on all 32-bit and 64-bit platforms that allow unaligned reads;
+// bug reports are welcome.
+//
+// By the way, for some hash functions, given strings a and b, the hash
+// of a+b is easily derived from the hashes of a and b.  This property
+// doesn't hold for any hash functions in this file.
+
+#ifndef CITY_HASH_HPP
+#define CITY_HASH_HPP
+
+#include <stdlib.h>  // for size_t.
+#include <stdint.h>
+#include <utility>
+
+typedef uint8_t uint8;
+typedef uint32_t uint32;
+typedef uint64_t uint64;
+typedef std::pair<uint64, uint64> uint128;
+
+inline uint64 Uint128Low64(const uint128& x) { return x.first; }
+inline uint64 Uint128High64(const uint128& x) { return x.second; }
+
+// Hash function for a byte array.
+uint64 CityHash64(const char *buf, size_t len);
+
+// Hash function for a byte array.  For convenience, a 64-bit seed is also
+// hashed into the result.
+uint64 CityHash64WithSeed(const char *buf, size_t len, uint64 seed);
+
+// Hash function for a byte array.  For convenience, two seeds are also
+// hashed into the result.
+uint64 CityHash64WithSeeds(const char *buf, size_t len,
+                           uint64 seed0, uint64 seed1);
+
+// Hash function for a byte array.
+uint128 CityHash128(const char *s, size_t len);
+
+// Hash function for a byte array.  For convenience, a 128-bit seed is also
+// hashed into the result.
+uint128 CityHash128WithSeed(const char *s, size_t len, uint128 seed);
+
+// Hash function for a byte array.  Most useful in 32-bit binaries.
+uint32 CityHash32(const char *buf, size_t len);
+
+// Hash 128 input bits down to 64 bits of output.
+// This is intended to be a reasonably good hash function.
+inline uint64 Hash128to64(const uint128& x) {
+  // Murmur-inspired hashing.
+  const uint64 kMul = 0x9ddfea08eb382d69ULL;
+  uint64 a = (Uint128Low64(x) ^ Uint128High64(x)) * kMul;
+  a ^= (a >> 47);
+  uint64 b = (Uint128High64(x) ^ a) * kMul;
+  b ^= (b >> 47);
+  b *= kMul;
+  return b;
+}
+
+#endif  // CITY_HASH_H_
diff --git a/NFD/core/config-file.cpp b/NFD/core/config-file.cpp
new file mode 100644
index 0000000..5887c2b
--- /dev/null
+++ b/NFD/core/config-file.cpp
@@ -0,0 +1,143 @@
+/* -*- 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
+ *
+ * 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 "config-file.hpp"
+#include "logger.hpp"
+
+#include <boost/property_tree/info_parser.hpp>
+#include <fstream>
+
+namespace nfd {
+
+NFD_LOG_INIT("ConfigFile");
+
+void
+ConfigFile::throwErrorOnUnknownSection(const std::string& filename,
+                                       const std::string& sectionName,
+                                       const ConfigSection& section,
+                                       bool isDryRun)
+{
+  std::string msg = "Error processing configuration file ";
+  msg += filename;
+  msg += ": no module subscribed for section \"" + sectionName + "\"";
+
+  throw ConfigFile::Error(msg);
+}
+
+void
+ConfigFile::ignoreUnknownSection(const std::string& filename,
+                                 const std::string& sectionName,
+                                 const ConfigSection& section,
+                                 bool isDryRun)
+{
+  // do nothing
+}
+
+ConfigFile::ConfigFile(UnknownConfigSectionHandler unknownSectionCallback)
+  : m_unknownSectionCallback(unknownSectionCallback)
+{
+}
+
+void
+ConfigFile::addSectionHandler(const std::string& sectionName,
+                              ConfigSectionHandler subscriber)
+{
+  m_subscriptions[sectionName] = subscriber;
+}
+
+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;
+      throw Error(msg);
+    }
+  parse(inputFile, isDryRun, filename);
+  inputFile.close();
+}
+
+void
+ConfigFile::parse(const std::string& input, bool isDryRun, const std::string& filename)
+{
+  std::istringstream inputStream(input);
+  parse(inputStream, isDryRun, filename);
+}
+
+
+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();
+      throw Error(msg.str());
+    }
+
+  process(isDryRun, filename);
+}
+
+void
+ConfigFile::process(bool isDryRun, const std::string& filename)
+{
+  BOOST_ASSERT(!filename.empty());
+  // NFD_LOG_DEBUG("processing..." << ((isDryRun)?("dry run"):("")));
+
+  if (m_global.begin() == m_global.end())
+    {
+      std::string msg = "Error processing configuration file: ";
+      msg += filename;
+      msg += " no data";
+      throw Error(msg);
+    }
+
+  for (ConfigSection::const_iterator i = m_global.begin(); i != m_global.end(); ++i)
+    {
+      const std::string& sectionName = i->first;
+      const ConfigSection& section = i->second;
+
+      SubscriptionTable::iterator subscriberIt = m_subscriptions.find(sectionName);
+      if (subscriberIt != m_subscriptions.end())
+        {
+          ConfigSectionHandler subscriber = subscriberIt->second;
+          subscriber(section, isDryRun, filename);
+        }
+      else
+        {
+          m_unknownSectionCallback(filename, sectionName, section, isDryRun);
+        }
+    }
+}
+
+}
diff --git a/NFD/core/config-file.hpp b/NFD/core/config-file.hpp
new file mode 100644
index 0000000..b7dd886
--- /dev/null
+++ b/NFD/core/config-file.hpp
@@ -0,0 +1,128 @@
+/* -*- 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
+ *
+ * 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_CONFIG_FILE_HPP
+#define NFD_CORE_CONFIG_FILE_HPP
+
+#include "common.hpp"
+
+#include <boost/property_tree/ptree.hpp>
+
+namespace nfd {
+
+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 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;
+
+class ConfigFile : noncopyable
+{
+public:
+
+  class Error : public std::runtime_error
+  {
+  public:
+    explicit
+    Error(const std::string& what)
+      : std::runtime_error(what)
+    {
+
+    }
+  };
+
+  ConfigFile(UnknownConfigSectionHandler unknownSectionCallback = throwErrorOnUnknownSection);
+
+  static void
+  throwErrorOnUnknownSection(const std::string& filename,
+                             const std::string& sectionName,
+                             const ConfigSection& section,
+                             bool isDryRun);
+
+  static void
+  ignoreUnknownSection(const std::string& filename,
+                       const std::string& sectionName,
+                       const ConfigSection& section,
+                       bool isDryRun);
+
+  /// \brief setup notification of configuration file sections
+  void
+  addSectionHandler(const std::string& sectionName,
+                    ConfigSectionHandler subscriber);
+
+
+  /**
+   * \param filename file to parse
+   * \param isDryRun true if performing a dry run of configuration, false otherwise
+   * \throws ConfigFile::Error if file not found
+   * \throws ConfigFile::Error if parse error
+   */
+  void
+  parse(const std::string& filename, bool isDryRun);
+
+  /**
+   * \param input configuration (as a string) to parse
+   * \param isDryRun true if performing a dry run of configuration, false otherwise
+   * \param filename optional convenience argument to provide more detailed error messages
+   * \throws ConfigFile::Error if file not found
+   * \throws ConfigFile::Error if parse error
+   */
+  void
+  parse(const std::string& input, bool isDryRun, const std::string& filename);
+
+  /**
+   * \param input stream to parse
+   * \param isDryRun true if performing a dry run of configuration, false otherwise
+   * \param filename optional convenience argument to provide more detailed error messages
+   * \throws ConfigFile::Error if parse error
+   */
+  void
+  parse(std::istream& input, bool isDryRun, const std::string& filename);
+
+private:
+
+  void
+  process(bool isDryRun, const std::string& filename);
+
+private:
+  UnknownConfigSectionHandler m_unknownSectionCallback;
+
+  typedef std::map<std::string, ConfigSectionHandler> SubscriptionTable;
+
+  SubscriptionTable m_subscriptions;
+
+  ConfigSection m_global;
+};
+
+} // namespace nfd
+
+
+#endif // NFD_CORE_CONFIG_FILE_HPP
diff --git a/NFD/core/global-io.cpp b/NFD/core/global-io.cpp
new file mode 100644
index 0000000..f8d8270
--- /dev/null
+++ b/NFD/core/global-io.cpp
@@ -0,0 +1,53 @@
+/* -*- 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
+ *
+ * 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 "global-io.hpp"
+
+namespace nfd {
+
+namespace scheduler {
+// defined in scheduler.cpp
+void
+resetGlobalScheduler();
+} // namespace scheduler
+
+static shared_ptr<boost::asio::io_service> g_ioService;
+
+boost::asio::io_service&
+getGlobalIoService()
+{
+  if (!static_cast<bool>(g_ioService)) {
+    g_ioService = make_shared<boost::asio::io_service>();
+  }
+  return *g_ioService;
+}
+
+void
+resetGlobalIoService()
+{
+  scheduler::resetGlobalScheduler();
+  g_ioService.reset();
+}
+
+} // namespace nfd
diff --git a/NFD/core/global-io.hpp b/NFD/core/global-io.hpp
new file mode 100644
index 0000000..d5030ab
--- /dev/null
+++ b/NFD/core/global-io.hpp
@@ -0,0 +1,48 @@
+/* -*- 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
+ *
+ * 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_GLOBAL_IO_HPP
+#define NFD_CORE_GLOBAL_IO_HPP
+
+#include "common.hpp"
+
+namespace nfd {
+
+/** \return the global io_service instance
+ */
+boost::asio::io_service&
+getGlobalIoService();
+
+#ifdef WITH_TESTS
+/** \brief delete the global io_service instance
+ *
+ *  It will be recreated at the next invocation of getGlobalIoService.
+ */
+void
+resetGlobalIoService();
+#endif
+
+} // namespace nfd
+
+#endif // NFD_CORE_GLOBAL_IO_HPP
diff --git a/NFD/core/logger-factory.cpp b/NFD/core/logger-factory.cpp
new file mode 100644
index 0000000..b0ef3cd
--- /dev/null
+++ b/NFD/core/logger-factory.cpp
@@ -0,0 +1,213 @@
+/* -*- 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
+ *
+ * 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 "logger-factory.hpp"
+
+namespace nfd {
+
+NFD_LOG_INIT("LoggerFactory");
+
+LoggerFactory&
+LoggerFactory::getInstance()
+{
+  static LoggerFactory globalLoggerFactory;
+
+  return globalLoggerFactory;
+}
+
+LoggerFactory::LoggerFactory()
+  : m_defaultLevel(LOG_INFO)
+{
+  m_levelNames["NONE"] = LOG_NONE;
+  m_levelNames["ERROR"] = LOG_ERROR;
+  m_levelNames["WARN"] = LOG_WARN;
+  m_levelNames["INFO"] = LOG_INFO;
+  m_levelNames["DEBUG"] = LOG_DEBUG;
+  m_levelNames["TRACE"] = LOG_TRACE;
+  m_levelNames["ALL"] = LOG_ALL;
+}
+
+void
+LoggerFactory::setConfigFile(ConfigFile& config)
+{
+  config.addSectionHandler("log", bind(&LoggerFactory::onConfig, this, _1, _2, _3));
+}
+
+LogLevel
+LoggerFactory::parseLevel(const std::string& level)
+{
+  std::string upperLevel = level;
+  boost::to_upper(upperLevel);
+
+  // std::cerr << "parsing level: " << upperLevel << std::endl;;
+  // std::cerr << "# levels: " << m_levelNames.size() << std::endl;
+  // std::cerr << m_levelNames.begin()->first << std::endl;
+
+  LevelMap::const_iterator levelIt = m_levelNames.find(upperLevel);
+  if (levelIt != m_levelNames.end())
+    {
+      return levelIt->second;
+    }
+  try
+    {
+      uint32_t levelNo = boost::lexical_cast<uint32_t>(level);
+
+      if ((boost::lexical_cast<uint32_t>(LOG_NONE) <= levelNo &&
+           levelNo <= boost::lexical_cast<uint32_t>(LOG_TRACE)) ||
+          levelNo == LOG_ALL)
+        {
+          return static_cast<LogLevel>(levelNo);
+        }
+    }
+  catch (const boost::bad_lexical_cast& error)
+    {
+    }
+  throw LoggerFactory::Error("Unsupported logging level \"" +
+                             level + "\"");
+}
+
+LogLevel
+LoggerFactory::extractLevel(const ConfigSection& item, const std::string& key)
+{
+  std::string levelString;
+  try
+    {
+      levelString = item.get_value<std::string>();
+    }
+  catch (const boost::property_tree::ptree_error& error)
+    {
+    }
+
+  if (levelString.empty())
+    {
+      throw LoggerFactory::Error("No logging level found for option \"" + key + "\"");
+    }
+
+  return parseLevel(levelString);
+}
+
+void
+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
+// }
+
+  if (!isDryRun)
+    {
+      ConfigSection::const_assoc_iterator item = section.find("default_level");
+      if (item != section.not_found())
+        {
+          LogLevel level = extractLevel(item->second, "default_level");
+          setDefaultLevel(level);
+        }
+      else
+        {
+          setDefaultLevel(LOG_INFO);
+        }
+    }
+
+  for (ConfigSection::const_iterator item = section.begin();
+       item != section.end();
+       ++item)
+    {
+      LogLevel level = extractLevel(item->second, item->first);
+
+      if (item->first == "default_level")
+        {
+          // do nothing
+        }
+      else
+        {
+          LoggerMap::iterator loggerIt = m_loggers.find(item->first);
+          if (loggerIt == m_loggers.end())
+            {
+              NFD_LOG_DEBUG("Failed to configure logging level for module \"" <<
+                            item->first << "\" (module not found)");
+            }
+          else if (!isDryRun)
+            {
+              // std::cerr << "changing level for module " << item->first << " to " << level << std::endl;
+              loggerIt->second.setLogLevel(level);
+            }
+        }
+    }
+}
+
+void
+LoggerFactory::setDefaultLevel(LogLevel level)
+{
+  // std::cerr << "changing to default_level " << level << std::endl;
+
+  m_defaultLevel = level;
+  for (LoggerMap::iterator i = m_loggers.begin(); i != m_loggers.end(); ++i)
+    {
+      // std::cerr << "changing " << i->first << " to default " << m_defaultLevel << std::endl;
+      i->second.setLogLevel(m_defaultLevel);
+    }
+}
+
+Logger&
+LoggerFactory::create(const std::string& moduleName)
+{
+  return LoggerFactory::getInstance().createLogger(moduleName);
+}
+
+Logger&
+LoggerFactory::createLogger(const std::string& moduleName)
+{
+  // std::cerr << "creating logger for " << moduleName
+  //           << " with level " << m_defaultLevel << std::endl;
+
+  std::pair<LoggerMap::iterator, bool> loggerIt =
+    m_loggers.insert(NameAndLogger(moduleName, Logger(moduleName, m_defaultLevel)));
+
+  return loggerIt.first->second;
+}
+
+std::list<std::string>
+LoggerFactory::getModules() const
+{
+  std::list<std::string> modules;
+  for (LoggerMap::const_iterator i = m_loggers.begin(); i != m_loggers.end(); ++i)
+    {
+      modules.push_back(i->first);
+    }
+
+  return modules;
+}
+
+} // namespace nfd
diff --git a/NFD/core/logger-factory.hpp b/NFD/core/logger-factory.hpp
new file mode 100644
index 0000000..cbf7424
--- /dev/null
+++ b/NFD/core/logger-factory.hpp
@@ -0,0 +1,110 @@
+/* -*- 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
+ *
+ * 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_LOGGER_FACTORY_HPP
+#define NFD_CORE_LOGGER_FACTORY_HPP
+
+#include "common.hpp"
+#include "config-file.hpp"
+#include "logger.hpp"
+
+namespace nfd {
+
+class LoggerFactory : noncopyable
+{
+public:
+
+  class Error : public std::runtime_error
+  {
+  public:
+    explicit
+    Error(const std::string& error)
+      : std::runtime_error(error)
+    {
+    }
+  };
+
+  static LoggerFactory&
+  getInstance();
+
+  void
+  setConfigFile(ConfigFile& config);
+
+  void
+  onConfig(const ConfigSection& section, bool isDryRun, const std::string& filename);
+
+  std::list<std::string>
+  getModules() const;
+
+  static Logger&
+  create(const std::string& moduleName);
+
+
+PUBLIC_WITH_TESTS_ELSE_PRIVATE:
+
+  // these methods are used during unit-testing
+
+  LogLevel
+  getDefaultLevel() const;
+
+  void
+  setDefaultLevel(LogLevel level);
+
+private:
+
+  LoggerFactory();
+
+  Logger&
+  createLogger(const std::string& moduleName);
+
+  LogLevel
+  parseLevel(const std::string& level);
+
+  LogLevel
+  extractLevel(const ConfigSection& item, const std::string& key);
+
+private:
+
+  typedef std::map<std::string, LogLevel> LevelMap;
+  typedef std::pair<std::string, LogLevel> NameAndLevel;
+
+  LevelMap m_levelNames;
+
+  typedef std::map<std::string, Logger> LoggerMap;
+  typedef std::pair<std::string, Logger> NameAndLogger;
+
+  LoggerMap m_loggers;
+
+  LogLevel m_defaultLevel;
+};
+
+inline LogLevel
+LoggerFactory::getDefaultLevel() const
+{
+  return m_defaultLevel;
+}
+
+} // namespace nfd
+
+#endif // NFD_CORE_LOGGER_FACTORY_HPP
diff --git a/NFD/core/logger.cpp b/NFD/core/logger.cpp
new file mode 100644
index 0000000..7d92aa3
--- /dev/null
+++ b/NFD/core/logger.cpp
@@ -0,0 +1,65 @@
+/* -*- 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/>.
+ */
+
+#include "logger.hpp"
+#include <ndn-cxx/util/time.hpp>
+#include <cinttypes>
+#include <cstdio>
+#include <type_traits>
+
+namespace nfd {
+
+Logger::Logger(const std::string& name, LogLevel level)
+  : m_moduleName(name)
+  , m_enabledLogLevel(level)
+{
+}
+
+const char*
+Logger::now()
+{
+  using namespace ndn::time;
+
+  static const microseconds::rep ONE_SECOND = 1000000;
+  microseconds::rep microsecondsSinceEpoch = duration_cast<microseconds>(
+                                             system_clock::now().time_since_epoch()).count();
+
+  // 10 (whole seconds) + '.' + 6 (fraction) + '\0'
+  static char buffer[10 + 1 + 6 + 1];
+  BOOST_ASSERT_MSG(microsecondsSinceEpoch / ONE_SECOND <= 9999999999L,
+                   "whole seconds cannot fit in 10 characters");
+
+  static_assert(std::is_same<microseconds::rep, int_least64_t>::value,
+                "PRIdLEAST64 is incompatible with microseconds::rep");
+  std::snprintf(buffer, sizeof(buffer), "%" PRIdLEAST64 ".%06" PRIdLEAST64,
+                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;
+}
+
+} // namespace nfd
diff --git a/NFD/core/logger.hpp b/NFD/core/logger.hpp
new file mode 100644
index 0000000..5efee3c
--- /dev/null
+++ b/NFD/core/logger.hpp
@@ -0,0 +1,152 @@
+/* -*- 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_LOGGER_HPP
+#define NFD_CORE_LOGGER_HPP
+
+#include "common.hpp"
+
+namespace nfd {
+
+/** \brief indicates a log level
+ *  \note This type is internal. Logger should be accessed through NFD_LOG_* macros.
+ */
+enum LogLevel {
+  LOG_NONE           = 0, // no messages
+  LOG_ERROR          = 1, // serious error messages
+  LOG_WARN           = 2, // warning messages
+  LOG_INFO           = 3, // informational messages
+  LOG_DEBUG          = 4, // debug messages
+  LOG_TRACE          = 5, // trace messages (most verbose)
+  // LOG_FATAL is not a level and is logged unconditionally
+  LOG_ALL            = 255 // all messages
+};
+
+/** \brief provides logging for a module
+ *  \note This type is internal. Logger should be accessed through NFD_LOG_* macros.
+ *  \note This type is copyable because logger can be declared as a field of
+ *        (usually template) classes, and shouldn't prevent those classes to be copyable.
+ */
+class Logger
+{
+public:
+  Logger(const std::string& name, LogLevel level);
+
+  bool
+  isEnabled(LogLevel level) const
+  {
+    // std::cerr << m_moduleName <<
+    //   " enabled = " << m_enabledLogLevel
+    //           << " level = " << level << std::endl;
+    return (m_enabledLogLevel >= level);
+  }
+
+  void
+  setLogLevel(LogLevel level)
+  {
+    m_enabledLogLevel = level;
+  }
+
+  const std::string&
+  getName() const
+  {
+    return m_moduleName;
+  }
+
+  void
+  setName(const std::string& name)
+  {
+    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();
+
+private:
+  std::string m_moduleName;
+  LogLevel    m_enabledLogLevel;
+};
+
+inline std::ostream&
+operator<<(std::ostream& output, const Logger& logger)
+{
+  output << logger.getName();
+  return output;
+}
+
+} // namespace nfd
+
+#include "core/logger-factory.hpp"
+
+namespace nfd {
+
+#define NFD_LOG_INIT(name) \
+static nfd::Logger& g_logger = nfd::LoggerFactory::create(name)
+
+#define NFD_LOG_INCLASS_DECLARE() \
+static nfd::Logger& g_logger
+
+#define NFD_LOG_INCLASS_DEFINE(cls, 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)
+
+#define NFD_LOG_INCLASS_TEMPLATE_SPECIALIZATION_DEFINE(cls, specialization, name) \
+template<>                                                                        \
+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)
+
+
+#define NFD_LOG(level, msg, expression)                          \
+do {                                                             \
+  if (g_logger.isEnabled(::nfd::LOG_##level))                    \
+    std::clog << ::nfd::Logger::now() << " "#msg": "             \
+              << "[" << g_logger << "] " << expression << "\n";  \
+} while (false)
+
+#define NFD_LOG_TRACE(expression) NFD_LOG(TRACE, TRACE,   expression)
+#define NFD_LOG_DEBUG(expression) NFD_LOG(DEBUG, DEBUG,   expression)
+#define NFD_LOG_INFO(expression)  NFD_LOG(INFO,  INFO,    expression)
+#define NFD_LOG_WARN(expression)  NFD_LOG(WARN,  WARNING, expression)
+#define NFD_LOG_ERROR(expression) NFD_LOG(ERROR, ERROR,   expression)
+
+#define NFD_LOG_FATAL(expression)                             \
+do {                                                          \
+  std::clog << ::nfd::Logger::now() << " FATAL: "             \
+            << "[" << g_logger << "] " << expression << "\n"; \
+} while (false)
+
+} // namespace nfd
+
+#endif // NFD_CORE_LOGGER_HPP
diff --git a/NFD/core/network-interface.cpp b/NFD/core/network-interface.cpp
new file mode 100644
index 0000000..1aad63e
--- /dev/null
+++ b/NFD/core/network-interface.cpp
@@ -0,0 +1,175 @@
+/* -*- 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/>.
+ */
+
+#include "network-interface.hpp"
+#include "core/logger.hpp"
+
+#include <cerrno>
+#include <cstring>
+#include <type_traits>
+#include <unordered_map>
+
+#include <arpa/inet.h>   // for inet_ntop()
+#include <netinet/in.h>  // for struct sockaddr_in{,6}
+#include <ifaddrs.h>     // for getifaddrs()
+
+#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
+
+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
+
+  using namespace boost::asio::ip;
+  using std::strerror;
+
+  std::unordered_map<std::string, NetworkInterfaceInfo> ifmap;
+  ifaddrs* ifa_list = nullptr;
+
+  if (::getifaddrs(&ifa_list) < 0)
+    throw 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;
+}
+
+} // namespace nfd
diff --git a/NFD/core/network-interface.hpp b/NFD/core/network-interface.hpp
new file mode 100644
index 0000000..0034052
--- /dev/null
+++ b/NFD/core/network-interface.hpp
@@ -0,0 +1,107 @@
+/* -*- 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
diff --git a/NFD/core/network.cpp b/NFD/core/network.cpp
new file mode 100644
index 0000000..b9a6c36
--- /dev/null
+++ b/NFD/core/network.cpp
@@ -0,0 +1,133 @@
+/* -*- 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/>.
+ **/
+
+#include "network.hpp"
+
+namespace nfd {
+
+void
+Network::print(std::ostream& os) const
+{
+  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));
+  return range;
+}
+
+const Network&
+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));
+  return range;
+}
+
+//////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////
+
+std::ostream&
+operator<<(std::ostream& os, const Network& network)
+{
+  network.print(os);
+  return os;
+}
+
+std::istream&
+operator>>(std::istream& is, Network& network)
+{
+  using namespace boost::asio;
+
+  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);
+    }
+  else
+    {
+      ip::address address = ip::address::from_string(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_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);
+        }
+    }
+  return is;
+}
+
+} // namespace nfd
diff --git a/NFD/core/network.hpp b/NFD/core/network.hpp
new file mode 100644
index 0000000..1c0f6d3
--- /dev/null
+++ b/NFD/core/network.hpp
@@ -0,0 +1,95 @@
+/* -*- 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_HPP
+#define NFD_CORE_NETWORK_HPP
+
+#include <boost/asio.hpp>
+#include <boost/utility/value_init.hpp>
+#include <boost/lexical_cast.hpp>
+
+namespace nfd {
+
+class Network
+{
+public:
+  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;
+
+  bool
+  doesContain(const boost::asio::ip::address& address) const
+  {
+    return (m_minAddress <= address && address <= m_maxAddress);
+  }
+
+  static const Network&
+  getMaxRangeV4();
+
+  static const Network&
+  getMaxRangeV6();
+
+  bool
+  operator==(const Network& rhs) const
+  {
+    return m_minAddress == rhs.m_minAddress && m_maxAddress == rhs.m_maxAddress;
+  }
+
+  bool
+  operator!=(const Network& rhs) const
+  {
+    return !(*this == rhs);
+  }
+
+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);
+};
+
+std::ostream&
+operator<<(std::ostream& os, const Network& network);
+
+std::istream&
+operator>>(std::istream& is, Network& network);
+
+} // namespace nfd
+
+#endif // NFD_CORE_NETWORK_HPP
diff --git a/NFD/core/notification-stream.hpp b/NFD/core/notification-stream.hpp
new file mode 100644
index 0000000..9a89b4c
--- /dev/null
+++ b/NFD/core/notification-stream.hpp
@@ -0,0 +1,81 @@
+/* -*- 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_NOTIFICATION_STREAM_HPP
+#define NFD_CORE_NOTIFICATION_STREAM_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 Notification Stream
+ *  \sa http://redmine.named-data.net/projects/nfd/wiki/Notification
+ */
+template <class FaceBase>
+class NotificationStream : noncopyable
+{
+public:
+  NotificationStream(FaceBase& face, const Name& prefix, ndn::KeyChain& keyChain)
+    : m_face(face)
+    , m_prefix(prefix)
+    , m_keyChain(keyChain)
+    , m_sequenceNo(0)
+  {
+  }
+
+  virtual
+  ~NotificationStream()
+  {
+  }
+
+  template<typename T> void
+  postNotification(const T& notification)
+  {
+    Name dataName = m_prefix;
+    dataName.appendSequenceNumber(m_sequenceNo);
+
+    shared_ptr<Data> data = make_shared<Data>(dataName);
+    data->setContent(notification.wireEncode());
+    data->setFreshnessPeriod(time::seconds(1));
+
+    m_keyChain.sign(*data);
+    m_face.put(*data);
+
+    ++m_sequenceNo;
+  }
+
+private:
+  FaceBase& m_face;
+  const Name m_prefix;
+  ndn::KeyChain& m_keyChain;
+  uint64_t m_sequenceNo;
+};
+
+} // namespace nfd
+
+#endif // NFD_CORE_NOTIFICATION_STREAM_HPP
diff --git a/NFD/core/privilege-helper.cpp b/NFD/core/privilege-helper.cpp
new file mode 100644
index 0000000..6f87f43
--- /dev/null
+++ b/NFD/core/privilege-helper.cpp
@@ -0,0 +1,195 @@
+/* -*- 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
+ *
+ * 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 "privilege-helper.hpp"
+#include "core/logger.hpp"
+
+#include <pwd.h>
+#include <grp.h>
+
+namespace nfd {
+
+NFD_LOG_INIT("PrivilegeHelper");
+
+uid_t PrivilegeHelper::s_normalUid = ::geteuid();
+gid_t PrivilegeHelper::s_normalGid = ::getegid();
+
+uid_t PrivilegeHelper::s_privilegedUid = ::geteuid();
+gid_t PrivilegeHelper::s_privilegedGid = ::getegid();
+
+void
+PrivilegeHelper::initialize(const std::string& userName, const std::string& groupName)
+{
+  static const size_t MAX_GROUP_BUFFER_SIZE = 16384; // 16kB
+  static const size_t MAX_PASSWD_BUFFER_SIZE = 16384;
+
+  static const size_t FALLBACK_GROUP_BUFFER_SIZE = 1024;
+  static const size_t FALLBACK_PASSWD_BUFFER_SIZE = 1024;
+
+  NFD_LOG_TRACE("initializing privilege helper with user \"" << userName << "\""
+                << " group \"" << groupName << "\"");
+
+  // workflow from man getpwnam_r
+
+  if (!groupName.empty())
+    {
+      static int groupSize = ::sysconf(_SC_GETGR_R_SIZE_MAX);
+
+      if (groupSize == -1)
+        {
+          groupSize = FALLBACK_GROUP_BUFFER_SIZE;
+        }
+
+      std::vector<char> groupBuffer(groupSize);
+      struct group group;
+      struct group* groupResult = 0;
+
+      int errorCode = getgrnam_r(groupName.c_str(), &group,
+                                 &groupBuffer[0], groupSize, &groupResult);
+
+      while (errorCode == ERANGE)
+        {
+          if (groupBuffer.size() * 2 > MAX_GROUP_BUFFER_SIZE)
+            {
+              throw PrivilegeHelper::Error("Cannot allocate large enough buffer for struct group");
+            }
+
+          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;
+    }
+
+  if (!userName.empty())
+    {
+      static int passwdSize = ::sysconf(_SC_GETPW_R_SIZE_MAX);
+
+      if (passwdSize == -1)
+        {
+          passwdSize = FALLBACK_PASSWD_BUFFER_SIZE;
+        }
+
+      std::vector<char> passwdBuffer(passwdSize);
+      struct passwd passwd;
+      struct passwd* passwdResult = 0;
+
+      int errorCode =
+        getpwnam_r(userName.c_str(), &passwd,
+                   &passwdBuffer[0], passwdBuffer.size(), &passwdResult);
+
+      while (errorCode == ERANGE)
+        {
+          if (passwdBuffer.size() * 2 > MAX_PASSWD_BUFFER_SIZE)
+            {
+              throw PrivilegeHelper::Error("Cannot allocate large enough buffer for struct passwd");
+            }
+
+          passwdBuffer.resize(passwdBuffer.size() * 2);
+
+          errorCode = getpwnam_r(userName.c_str(), &passwd,
+                                 &passwdBuffer[0], passwdBuffer.size(), &passwdResult);
+        }
+
+      if (errorCode != 0 || !passwdResult)
+        {
+          throw PrivilegeHelper::Error("Failed to get uid for \"" + userName + "\"");
+        }
+
+      s_normalUid = passwd.pw_uid;
+    }
+}
+
+void
+PrivilegeHelper::drop()
+{
+  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());
+    }
+
+  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());
+    }
+
+  NFD_LOG_INFO("dropped to effective uid=" << ::geteuid() << " gid=" << ::getegid());
+}
+
+void
+PrivilegeHelper::raise()
+{
+  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());
+    }
+
+  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 PrivilegeHelper::Error(error.str());
+    }
+  NFD_LOG_INFO("elevated to effective uid=" << ::geteuid() << " gid=" << ::getegid());
+}
+
+void
+PrivilegeHelper::runElevated(function<void()> f)
+{
+  raise();
+
+  try
+    {
+      f();
+    }
+  catch (...)
+    {
+      drop();
+      throw;
+    }
+  drop();
+}
+
+} // namespace nfd
diff --git a/NFD/core/privilege-helper.hpp b/NFD/core/privilege-helper.hpp
new file mode 100644
index 0000000..92bd53d
--- /dev/null
+++ b/NFD/core/privilege-helper.hpp
@@ -0,0 +1,84 @@
+/* -*- 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
+ *
+ * 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_PRIVILEGE_HELPER_HPP
+#define NFD_CORE_PRIVILEGE_HELPER_HPP
+
+#include "common.hpp"
+
+#include <unistd.h>
+
+namespace nfd {
+
+class PrivilegeHelper
+{
+public:
+
+  /// \brief PrivilegeHelper::Error represents a serious seteuid/gid failure and
+  ///        should only be caught by main in as part of a graceful program termination.
+  class Error
+  {
+  public:
+    explicit
+    Error(const std::string& what)
+      : m_whatMessage(what)
+    {
+    }
+
+    const char*
+    what() const
+    {
+      return m_whatMessage.c_str();
+    }
+
+  private:
+    const std::string m_whatMessage;
+  };
+
+  static void
+  initialize(const std::string& userName, const std::string& groupName);
+
+  static void
+  drop();
+
+  static void
+  runElevated(function<void()> f);
+
+private:
+
+  static void
+  raise();
+
+private:
+
+  static uid_t s_normalUid;
+  static gid_t s_normalGid;
+
+  static uid_t s_privilegedUid;
+  static gid_t s_privilegedGid;
+};
+
+} // namespace nfd
+
+#endif // NFD_CORE_PRIVILEGE_HELPER_HPP
diff --git a/NFD/core/random.cpp b/NFD/core/random.cpp
new file mode 100644
index 0000000..51b137e
--- /dev/null
+++ b/NFD/core/random.cpp
@@ -0,0 +1,38 @@
+/* -*- 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/>.
+ */
+
+#include "random.hpp"
+
+namespace nfd {
+
+static boost::random::mt19937 g_rng;
+
+boost::random::mt19937&
+getGlobalRng()
+{
+  return g_rng;
+}
+
+} // namespace nfd
diff --git a/NFD/core/random.hpp b/NFD/core/random.hpp
new file mode 100644
index 0000000..1515b72
--- /dev/null
+++ b/NFD/core/random.hpp
@@ -0,0 +1,49 @@
+/* -*- 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/>.
+ */
+
+/**
+ * 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.
+ *
+ * This random number generator is not suitable for security purposes;
+ * security-critical code should use CryptoPP random number generator instead.
+ */
+
+#ifndef NFD_CORE_RANDOM_HPP
+#define NFD_CORE_RANDOM_HPP
+
+#include <boost/random/mersenne_twister.hpp>
+
+namespace nfd {
+
+/** \return the global random number generator instance
+ */
+boost::random::mt19937&
+getGlobalRng();
+
+} // namespace nfd
+
+#endif // NFD_CORE_RANDOM_HPP
diff --git a/NFD/core/resolver.hpp b/NFD/core/resolver.hpp
new file mode 100644
index 0000000..d4dc31c
--- /dev/null
+++ b/NFD/core/resolver.hpp
@@ -0,0 +1,214 @@
+/* -*- 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
+ *
+ * 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_RESOLVER_H
+#define NFD_CORE_RESOLVER_H
+
+#include "common.hpp"
+#include "core/global-io.hpp"
+#include "core/scheduler.hpp"
+
+namespace nfd {
+namespace resolver {
+
+typedef function<bool (const boost::asio::ip::address& address)> AddressSelector;
+
+struct AnyAddress {
+  bool
+  operator()(const boost::asio::ip::address& address)
+  {
+    return true;
+  }
+};
+
+struct Ipv4Address {
+  bool
+  operator()(const boost::asio::ip::address& address)
+  {
+    return address.is_v4();
+  }
+};
+
+struct Ipv6Address {
+  bool
+  operator()(const boost::asio::ip::address& address)
+  {
+    return address.is_v6();
+  }
+};
+
+} // namespace resolver
+
+template<class Protocol>
+class Resolver
+{
+public:
+  struct Error : public std::runtime_error
+  {
+    Error(const std::string& what) : std::runtime_error(what) {}
+  };
+
+  typedef function<void (const typename Protocol::endpoint& endpoint)> SuccessCallback;
+  typedef function<void (const std::string& reason)> ErrorCallback;
+
+  typedef boost::asio::ip::basic_resolver< Protocol > resolver;
+
+  /** \brief Asynchronously resolve host and port
+   *
+   * If an address selector predicate is specified, then each resolved IP address
+   * is checked against the predicate.
+   *
+   * Available address selector predicates:
+   *
+   * - resolver::AnyAddress()
+   * - resolver::Ipv4Address()
+   * - resolver::Ipv6Address()
+   */
+  static void
+  asyncResolve(const std::string& host, const std::string& port,
+               const SuccessCallback& onSuccess,
+               const ErrorCallback& onError,
+               const nfd::resolver::AddressSelector& addressSelector = nfd::resolver::AnyAddress(),
+               const time::seconds& timeout = time::seconds(4))
+  {
+    shared_ptr<Resolver> resolver =
+      shared_ptr<Resolver>(new Resolver(onSuccess, onError,
+                                        addressSelector));
+
+    resolver->asyncResolve(host, port, timeout, resolver);
+    // resolver will be destroyed when async operation finishes or global IO service stops
+  }
+
+  /** \brief Synchronously resolve host and port
+   *
+   * If an address selector predicate is specified, then each resolved IP address
+   * is checked against the predicate.
+   *
+   * Available address selector predicates:
+   *
+   * - resolver::AnyAddress()
+   * - resolver::Ipv4Address()
+   * - resolver::Ipv6Address()
+   */
+  static typename Protocol::endpoint
+  syncResolve(const std::string& host, const std::string& port,
+              const nfd::resolver::AddressSelector& addressSelector = nfd::resolver::AnyAddress())
+  {
+    Resolver resolver(SuccessCallback(), ErrorCallback(), addressSelector);
+
+    typename resolver::query query(host, port
+#if not defined(__FreeBSD__)
+                                   , resolver::query::all_matching
+#endif
+                                   );
+
+    typename resolver::iterator remoteEndpoint = resolver.m_resolver.resolve(query);
+    typename resolver::iterator end;
+    for (; remoteEndpoint != end; ++remoteEndpoint)
+      {
+        if (addressSelector(typename Protocol::endpoint(*remoteEndpoint).address()))
+          return *remoteEndpoint;
+      }
+    throw Error("No endpoint matching the specified address selector found");
+  }
+
+private:
+  Resolver(const SuccessCallback& onSuccess,
+           const ErrorCallback& onError,
+           const nfd::resolver::AddressSelector& addressSelector)
+    : m_resolver(getGlobalIoService())
+    , m_addressSelector(addressSelector)
+    , m_onSuccess(onSuccess)
+    , m_onError(onError)
+  {
+  }
+
+  void
+  asyncResolve(const std::string& host, const std::string& port,
+               const time::seconds& timeout,
+               const shared_ptr<Resolver>& self)
+  {
+    typename resolver::query query(host, port
+#if not defined(__FreeBSD__)
+                                   , resolver::query::all_matching
+#endif
+                                   );
+
+    m_resolver.async_resolve(query,
+                             bind(&Resolver::onResolveSuccess, this, _1, _2, self));
+
+    m_resolveTimeout = scheduler::schedule(timeout,
+                                           bind(&Resolver::onResolveError, this,
+                                                "Timeout", self));
+  }
+
+  void
+  onResolveSuccess(const boost::system::error_code& error,
+                   typename resolver::iterator remoteEndpoint,
+                   const shared_ptr<Resolver>& self)
+  {
+    scheduler::cancel(m_resolveTimeout);
+
+    if (error)
+      {
+        if (error == boost::system::errc::operation_canceled)
+          return;
+
+        return m_onError("Remote endpoint hostname or port cannot be resolved: " +
+                         error.category().message(error.value()));
+      }
+
+    typename resolver::iterator end;
+    for (; remoteEndpoint != end; ++remoteEndpoint)
+      {
+        if (m_addressSelector(typename Protocol::endpoint(*remoteEndpoint).address()))
+          return m_onSuccess(*remoteEndpoint);
+      }
+
+    m_onError("No endpoint matching the specified address selector found");
+  }
+
+  void
+  onResolveError(const std::string& errorInfo,
+                 const shared_ptr<Resolver>& self)
+  {
+    m_resolver.cancel();
+    m_onError(errorInfo);
+  }
+
+private:
+  resolver m_resolver;
+  EventId m_resolveTimeout;
+
+  nfd::resolver::AddressSelector m_addressSelector;
+  SuccessCallback m_onSuccess;
+  ErrorCallback m_onError;
+};
+
+typedef Resolver<boost::asio::ip::tcp> TcpResolver;
+typedef Resolver<boost::asio::ip::udp> UdpResolver;
+
+} // namespace nfd
+
+#endif // NFD_CORE_RESOLVER_H
diff --git a/NFD/core/scheduler.cpp b/NFD/core/scheduler.cpp
new file mode 100644
index 0000000..0979f97
--- /dev/null
+++ b/NFD/core/scheduler.cpp
@@ -0,0 +1,104 @@
+/* -*- 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/>.
+ */
+
+#include "scheduler.hpp"
+#include "global-io.hpp"
+
+namespace nfd {
+namespace scheduler {
+
+static shared_ptr<Scheduler> g_scheduler;
+
+inline Scheduler&
+getGlobalScheduler()
+{
+  if (!static_cast<bool>(g_scheduler)) {
+    g_scheduler = make_shared<Scheduler>(ref(getGlobalIoService()));
+  }
+  return *g_scheduler;
+}
+
+EventId
+schedule(const time::nanoseconds& after, const Scheduler::Event& event)
+{
+  return getGlobalScheduler().scheduleEvent(after, event);
+}
+
+void
+cancel(const EventId& eventId)
+{
+  getGlobalScheduler().cancelEvent(eventId);
+}
+
+void
+resetGlobalScheduler()
+{
+  g_scheduler.reset();
+}
+
+ScopedEventId::ScopedEventId()
+{
+}
+
+ScopedEventId::ScopedEventId(const EventId& event)
+  : m_event(event)
+{
+}
+
+ScopedEventId::ScopedEventId(ScopedEventId&& other)
+  : m_event(other.m_event)
+{
+  other.release();
+}
+
+ScopedEventId&
+ScopedEventId::operator=(const EventId& event)
+{
+  if (m_event != event) {
+    scheduler::cancel(m_event);
+    m_event = event;
+  }
+  return *this;
+}
+
+ScopedEventId::~ScopedEventId()
+{
+  scheduler::cancel(m_event);
+}
+
+void
+ScopedEventId::cancel()
+{
+  scheduler::cancel(m_event);
+}
+
+void
+ScopedEventId::release()
+{
+  m_event.reset();
+}
+
+} // namespace scheduler
+} // namespace nfd
diff --git a/NFD/core/scheduler.hpp b/NFD/core/scheduler.hpp
new file mode 100644
index 0000000..a4b43c1
--- /dev/null
+++ b/NFD/core/scheduler.hpp
@@ -0,0 +1,101 @@
+/* -*- 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_SCHEDULER_HPP
+#define NFD_CORE_SCHEDULER_HPP
+
+#include "common.hpp"
+#include <ndn-cxx/util/scheduler.hpp>
+
+namespace nfd {
+namespace scheduler {
+
+using ndn::Scheduler;
+
+/** \class EventId
+ *  \brief Opaque type (shared_ptr) representing ID of a scheduled event
+ */
+using ndn::EventId;
+
+/** \brief schedule an event
+ */
+EventId
+schedule(const time::nanoseconds& after, const Scheduler::Event& event);
+
+/** \brief cancel a scheduled event
+ */
+void
+cancel(const EventId& eventId);
+
+/** \brief cancels an event automatically upon destruction
+ */
+class ScopedEventId : noncopyable
+{
+public:
+  ScopedEventId();
+
+  /** \brief implicit constructor from EventId
+   *  \param event the event to be cancelled upon destruction
+   */
+  ScopedEventId(const EventId& event);
+
+  /** \brief move constructor
+   */
+  ScopedEventId(ScopedEventId&& other);
+
+  /** \brief assigns an event
+   *
+   *  If a different event has been assigned to this instance previously,
+   *  that event will be cancelled immediately.
+   */
+  ScopedEventId&
+  operator=(const EventId& event);
+
+  /** \brief cancels the event
+   */
+  ~ScopedEventId();
+
+  /** \brief cancels the event manually
+   */
+  void
+  cancel();
+
+  /** \brief releases the event so that it won't be disconnected
+   *         when this ScopedEventId is destructed
+   */
+  void
+  release();
+
+private:
+  EventId m_event;
+};
+
+} // namespace scheduler
+
+using scheduler::EventId;
+
+} // namespace nfd
+
+#endif // NFD_CORE_SCHEDULER_HPP
diff --git a/NFD/core/segment-publisher.hpp b/NFD/core/segment-publisher.hpp
new file mode 100644
index 0000000..ccb3426
--- /dev/null
+++ b/NFD/core/segment-publisher.hpp
@@ -0,0 +1,120 @@
+/* -*- 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_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)
+    : m_face(face)
+    , m_prefix(prefix)
+    , m_keyChain(keyChain)
+  {
+  }
+
+  virtual
+  ~SegmentPublisher()
+  {
+  }
+
+  static size_t
+  getMaxSegmentSize()
+  {
+    static const size_t MAX_SEGMENT_SIZE = ndn::MAX_NDN_PACKET_SIZE >> 1;
+    return MAX_SEGMENT_SIZE;
+  }
+
+  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);
+
+      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;
+};
+
+} // namespace nfd
+
+#endif // NFD_CORE_SEGMENT_PUBLISHER_HPP
diff --git a/NFD/daemon/face/channel.cpp b/NFD/daemon/face/channel.cpp
new file mode 100644
index 0000000..d161504
--- /dev/null
+++ b/NFD/daemon/face/channel.cpp
@@ -0,0 +1,39 @@
+/* -*- 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
+ *
+ * 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 "channel.hpp"
+
+namespace nfd {
+
+Channel::~Channel()
+{
+}
+
+void
+Channel::setUri(const FaceUri& uri)
+{
+  m_uri = uri;
+}
+
+} // namespace nfd
diff --git a/NFD/daemon/face/channel.hpp b/NFD/daemon/face/channel.hpp
new file mode 100644
index 0000000..810d726
--- /dev/null
+++ b/NFD/daemon/face/channel.hpp
@@ -0,0 +1,68 @@
+/* -*- 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
+ *
+ * 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_HPP
+#define NFD_DAEMON_FACE_CHANNEL_HPP
+
+#include "face.hpp"
+
+namespace nfd {
+
+class Channel : noncopyable
+{
+public:
+  /** \brief Prototype for the callback called when face is created
+   *         (as a response to incoming connection or after connection
+   *         is established)
+   */
+  typedef function<void(const shared_ptr<Face>& newFace)> FaceCreatedCallback;
+
+  /** \brief Prototype for the callback that is called when face is failed to
+   *         get created
+   */
+  typedef function<void(const std::string& reason)> ConnectFailedCallback;
+
+  virtual
+  ~Channel();
+
+  const FaceUri&
+  getUri() const;
+
+protected:
+  void
+  setUri(const FaceUri& uri);
+
+private:
+  FaceUri m_uri;
+};
+
+inline const FaceUri&
+Channel::getUri() const
+{
+  return m_uri;
+}
+
+} // namespace nfd
+
+#endif // NFD_DAEMON_FACE_CHANNEL_HPP
diff --git a/NFD/daemon/face/datagram-face.hpp b/NFD/daemon/face/datagram-face.hpp
new file mode 100644
index 0000000..5b1b3d9
--- /dev/null
+++ b/NFD/daemon/face/datagram-face.hpp
@@ -0,0 +1,342 @@
+/* -*- 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_DAEMON_FACE_DATAGRAM_FACE_HPP
+#define NFD_DAEMON_FACE_DATAGRAM_FACE_HPP
+
+#include "face.hpp"
+#include "core/logger.hpp"
+
+namespace nfd {
+
+class Unicast {};
+class Multicast {};
+
+template<class Protocol, class Type = Unicast>
+class DatagramFace : public Face
+{
+public:
+  typedef Protocol protocol;
+
+  /** \brief Construct datagram face
+   *
+   * \param socket      Protocol-specific socket for the created face
+   * \param isOnDemand  If true, the face can be closed after it remains
+   *                    unused for a certain amount of time
+   */
+  DatagramFace(const FaceUri& remoteUri, const FaceUri& localUri,
+               const shared_ptr<typename protocol::socket>& socket,
+               bool isOnDemand);
+
+  virtual
+  ~DatagramFace();
+
+  // from Face
+  virtual void
+  sendInterest(const Interest& interest);
+
+  virtual void
+  sendData(const Data& data);
+
+  virtual void
+  close();
+
+  void
+  receiveDatagram(const uint8_t* buffer,
+                  size_t nBytesReceived,
+                  const boost::system::error_code& error);
+
+  /**
+   * \brief Set m_hasBeenUsedRecently to false
+   */
+  void
+  resetRecentUsage();
+
+  bool
+  hasBeenUsedRecently() const;
+
+  void
+  setOnDemand(bool isOnDemand);
+
+protected:
+  void
+  handleSend(const boost::system::error_code& error,
+             size_t nBytesSent,
+             const Block& payload);
+
+  void
+  handleReceive(const boost::system::error_code& error,
+                size_t nBytesReceived);
+
+  void
+  keepFaceAliveUntilAllHandlersExecuted(const shared_ptr<Face>& face);
+
+  void
+  closeSocket();
+
+protected:
+  shared_ptr<typename protocol::socket> m_socket;
+  uint8_t m_inputBuffer[ndn::MAX_NDN_PACKET_SIZE];
+  bool m_hasBeenUsedRecently;
+
+  NFD_LOG_INCLASS_DECLARE();
+};
+
+
+template<class T, class U>
+inline
+DatagramFace<T, U>::DatagramFace(const FaceUri& remoteUri, const FaceUri& localUri,
+                                 const shared_ptr<typename DatagramFace::protocol::socket>& socket,
+                                 bool isOnDemand)
+  : Face(remoteUri, localUri)
+  , m_socket(socket)
+{
+  setOnDemand(isOnDemand);
+
+  m_socket->async_receive(boost::asio::buffer(m_inputBuffer, ndn::MAX_NDN_PACKET_SIZE), 0,
+                          bind(&DatagramFace<T, U>::handleReceive, this, _1, _2));
+}
+
+template<class T, class U>
+inline
+DatagramFace<T, U>::~DatagramFace()
+{
+}
+
+template<class T, class U>
+inline void
+DatagramFace<T, U>::sendInterest(const Interest& interest)
+{
+  this->onSendInterest(interest);
+  const Block& payload = interest.wireEncode();
+  m_socket->async_send(boost::asio::buffer(payload.wire(), payload.size()),
+                       bind(&DatagramFace<T, U>::handleSend, this, _1, _2, payload));
+
+  // anything else should be done here?
+}
+
+template<class T, class U>
+inline void
+DatagramFace<T, U>::sendData(const Data& data)
+{
+  this->onSendData(data);
+  const Block& payload = data.wireEncode();
+  m_socket->async_send(boost::asio::buffer(payload.wire(), payload.size()),
+                       bind(&DatagramFace<T, U>::handleSend, this, _1, _2, payload));
+
+  // anything else should be done here?
+}
+
+template<class T, class U>
+inline void
+DatagramFace<T, U>::handleSend(const boost::system::error_code& error,
+                               size_t nBytesSent,
+                               const Block& payload)
+// 'payload' is unused; it's needed to retain the underlying Buffer
+{
+  if (error != 0) {
+    if (error == boost::system::errc::operation_canceled) // when socket is closed by someone
+      return;
+
+    if (!m_socket->is_open()) {
+      fail("Tunnel closed");
+      return;
+    }
+
+    NFD_LOG_WARN("[id:" << this->getId()
+                  << ",uri:" << this->getRemoteUri()
+                  << "] Send operation failed, closing socket: "
+                  << error.category().message(error.value()));
+
+    closeSocket();
+
+    if (error == boost::asio::error::eof) {
+      fail("Tunnel closed");
+    }
+    else {
+      fail("Send operation failed, closing socket: " +
+             error.category().message(error.value()));
+    }
+    return;
+  }
+
+  NFD_LOG_TRACE("[id:" << this->getId()
+                << ",uri:" << this->getRemoteUri()
+                << "] Successfully sent: " << nBytesSent << " bytes");
+  this->getMutableCounters().getNOutBytes() += nBytesSent;
+}
+
+template<class T, class U>
+inline void
+DatagramFace<T, U>::close()
+{
+  if (!m_socket->is_open())
+    return;
+
+  NFD_LOG_INFO("[id:" << this->getId()
+               << ",uri:" << this->getRemoteUri()
+               << "] Close tunnel");
+
+  closeSocket();
+  fail("Close tunnel");
+}
+
+template<class T, class U>
+inline void
+DatagramFace<T, U>::handleReceive(const boost::system::error_code& error,
+                                  size_t nBytesReceived)
+{
+  NFD_LOG_DEBUG("handleReceive: " << nBytesReceived);
+  receiveDatagram(m_inputBuffer, nBytesReceived, error);
+  if (m_socket->is_open())
+    m_socket->async_receive(boost::asio::buffer(m_inputBuffer, ndn::MAX_NDN_PACKET_SIZE), 0,
+                            bind(&DatagramFace<T, U>::handleReceive, this, _1, _2));
+}
+
+template<class T, class U>
+inline void
+DatagramFace<T, U>::receiveDatagram(const uint8_t* buffer,
+                                    size_t nBytesReceived,
+                                    const boost::system::error_code& error)
+{
+  if (error != 0 || nBytesReceived == 0) {
+    if (error == boost::system::errc::operation_canceled) // when socket is closed by someone
+      return;
+
+    // this should be unnecessary, but just in case
+    if (!m_socket->is_open()) {
+      fail("Tunnel closed");
+      return;
+    }
+
+    NFD_LOG_WARN("[id:" << this->getId()
+                 << ",uri:" << this->getRemoteUri()
+                 << "] Receive operation failed: "
+                 << error.category().message(error.value()));
+
+    closeSocket();
+
+    if (error == boost::asio::error::eof) {
+      fail("Tunnel closed");
+    }
+    else {
+      fail("Receive operation failed, closing socket: " +
+             error.category().message(error.value()));
+    }
+    return;
+  }
+
+  NFD_LOG_TRACE("[id:" << this->getId()
+                << ",uri:" << this->getRemoteUri()
+                << "] Received: " << nBytesReceived << " bytes");
+  this->getMutableCounters().getNInBytes() += nBytesReceived;
+
+  Block element;
+  bool isOk = Block::fromBuffer(buffer, nBytesReceived, element);
+  if (!isOk)
+    {
+      NFD_LOG_WARN("[id:" << this->getId()
+                   << ",uri:" << this->getRemoteUri()
+                   << "] Failed to parse incoming packet");
+      // This message won't extend the face lifetime
+      return;
+    }
+
+  if (element.size() != nBytesReceived)
+    {
+      NFD_LOG_WARN("[id:" << this->getId()
+                   << ",uri:" << this->getRemoteUri()
+                   << "] Received datagram size and decoded "
+                   << "element size don't match");
+      // This message won't extend the face lifetime
+      return;
+    }
+
+  if (!this->decodeAndDispatchInput(element))
+    {
+      NFD_LOG_WARN("[id:" << this->getId()
+                   << ",uri:" << this->getRemoteUri()
+                   << "] Received unrecognized block of type ["
+                   << element.type() << "]");
+      // This message won't extend the face lifetime
+      return;
+    }
+
+  m_hasBeenUsedRecently = true;
+}
+
+
+template<class T, class U>
+inline void
+DatagramFace<T, U>::keepFaceAliveUntilAllHandlersExecuted(const shared_ptr<Face>& face)
+{
+}
+
+template<class T, class U>
+inline void
+DatagramFace<T, U>::closeSocket()
+{
+  NFD_LOG_DEBUG("[id:" << this->getId()
+                << ",uri:" << this->getRemoteUri()
+                << "] closeSocket");
+
+  boost::asio::io_service& io = m_socket->get_io_service();
+
+  // use the non-throwing variants and ignore errors, if any
+  boost::system::error_code error;
+  m_socket->shutdown(protocol::socket::shutdown_both, error);
+  m_socket->close(error);
+  // after this, handlers will be called with an error code
+
+  // ensure that the Face object is alive at least until all pending
+  // handlers are dispatched
+  io.post(bind(&DatagramFace<T, U>::keepFaceAliveUntilAllHandlersExecuted,
+               this, this->shared_from_this()));
+}
+
+template<class T, class U>
+inline void
+DatagramFace<T, U>::setOnDemand(bool isOnDemand)
+{
+  Face::setOnDemand(isOnDemand);
+}
+
+template<class T, class U>
+inline void
+DatagramFace<T, U>::resetRecentUsage()
+{
+  m_hasBeenUsedRecently = false;
+}
+
+template<class T, class U>
+inline bool
+DatagramFace<T, U>::hasBeenUsedRecently() const
+{
+  return m_hasBeenUsedRecently;
+}
+
+} // namespace nfd
+
+#endif // NFD_DAEMON_FACE_DATAGRAM_FACE_HPP
diff --git a/NFD/daemon/face/ethernet-face.cpp b/NFD/daemon/face/ethernet-face.cpp
new file mode 100644
index 0000000..c63e38e
--- /dev/null
+++ b/NFD/daemon/face/ethernet-face.cpp
@@ -0,0 +1,464 @@
+/* -*- 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/>.
+ */
+
+#include "ethernet-face.hpp"
+#include "core/global-io.hpp"
+#include "core/logger.hpp"
+
+#include <pcap/pcap.h>
+
+#include <cerrno>         // for errno
+#include <cstdio>         // for std::snprintf()
+#include <cstring>        // for std::strerror() and std::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 <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
+
+namespace nfd {
+
+NFD_LOG_INIT("EthernetFace");
+
+const time::nanoseconds EthernetFace::REASSEMBLER_LIFETIME = time::seconds(60);
+
+EthernetFace::EthernetFace(const shared_ptr<boost::asio::posix::stream_descriptor>& socket,
+                           const NetworkInterfaceInfo& interface,
+                           const ethernet::Address& address)
+  : Face(FaceUri(address), FaceUri::fromDev(interface.name))
+  , m_pcap(nullptr, pcap_close)
+  , m_socket(socket)
+#if defined(__linux__)
+  , m_interfaceIndex(interface.index)
+#endif
+  , m_interfaceName(interface.name)
+  , m_srcAddress(interface.etherAddress)
+  , m_destAddress(address)
+{
+  NFD_LOG_INFO("Creating ethernet face on " << m_interfaceName << ": "
+               << m_srcAddress << " <--> " << m_destAddress);
+  pcapInit();
+
+  int fd = pcap_get_selectable_fd(m_pcap.get());
+  if (fd < 0)
+    throw 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));
+
+  m_interfaceMtu = getInterfaceMtu();
+  NFD_LOG_DEBUG("[id:" << getId() << ",endpoint:" << m_interfaceName
+                << "] Interface MTU is: " << m_interfaceMtu);
+
+  m_slicer.reset(new ndnlp::Slicer(m_interfaceMtu));
+
+  char filter[100];
+  std::snprintf(filter, sizeof(filter),
+                "(ether proto 0x%x) && (ether dst %s) && (not ether src %s)",
+                ethernet::ETHERTYPE_NDN,
+                m_destAddress.toString().c_str(),
+                m_srcAddress.toString().c_str());
+  setPacketFilter(filter);
+
+  if (!m_destAddress.isBroadcast() && !joinMulticastGroup())
+    {
+      NFD_LOG_WARN("[id:" << getId() << ",endpoint:" << m_interfaceName
+                   << "] Falling back to promiscuous mode");
+      pcap_set_promisc(m_pcap.get(), 1);
+    }
+
+  m_socket->async_read_some(boost::asio::null_buffers(),
+                            bind(&EthernetFace::handleRead, this,
+                                 boost::asio::placeholders::error,
+                                 boost::asio::placeholders::bytes_transferred));
+}
+
+EthernetFace::~EthernetFace()
+{
+}
+
+void
+EthernetFace::sendInterest(const Interest& interest)
+{
+  onSendInterest(interest);
+  ndnlp::PacketArray pa = m_slicer->slice(interest.wireEncode());
+  for (const auto& packet : *pa) {
+    sendPacket(packet);
+  }
+}
+
+void
+EthernetFace::sendData(const Data& data)
+{
+  onSendData(data);
+  ndnlp::PacketArray pa = m_slicer->slice(data.wireEncode());
+  for (const auto& packet : *pa) {
+    sendPacket(packet);
+  }
+}
+
+void
+EthernetFace::close()
+{
+  if (!m_pcap)
+    return;
+
+  NFD_LOG_INFO("[id:" << getId() << ",endpoint:" << m_interfaceName
+               << "] Closing face");
+
+  boost::system::error_code error;
+  m_socket->cancel(error); // ignore errors
+  m_socket->close(error);  // ignore errors
+  m_pcap.reset(nullptr);
+
+  fail("Face closed");
+}
+
+void
+EthernetFace::pcapInit()
+{
+  char errbuf[PCAP_ERRBUF_SIZE] = {};
+  m_pcap.reset(pcap_create(m_interfaceName.c_str(), errbuf));
+  if (!m_pcap)
+    throw 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 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)
+    throw Error("pcap_activate failed");
+
+  if (pcap_set_datalink(m_pcap.get(), DLT_EN10MB) < 0)
+    throw 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_WARN("pcap_setdirection: " << pcap_geterr(m_pcap.get()));
+}
+
+void
+EthernetFace::setPacketFilter(const char* filterString)
+{
+  bpf_program filter;
+  if (pcap_compile(m_pcap.get(), &filter, filterString, 1, PCAP_NETMASK_UNKNOWN) < 0)
+    throw Error("pcap_compile: " + std::string(pcap_geterr(m_pcap.get())));
+
+  int ret = pcap_setfilter(m_pcap.get(), &filter);
+  pcap_freecode(&filter);
+  if (ret < 0)
+    throw Error("pcap_setfilter: " + std::string(pcap_geterr(m_pcap.get())));
+}
+
+bool
+EthernetFace::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::copy(m_destAddress.begin(), m_destAddress.end(), mr.mr_address);
+
+  if (::setsockopt(m_socket->native_handle(), SOL_PACKET,
+                   PACKET_ADD_MEMBERSHIP, &mr, sizeof(mr)) == 0)
+    return true; // success
+
+  NFD_LOG_WARN("[id:" << getId() << ",endpoint:" << m_interfaceName
+               << "] 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(ref(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::copy(m_destAddress.begin(), m_destAddress.end(), LLADDR(sdl));
+
+  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::copy(m_destAddress.begin(), m_destAddress.end(), ifr.ifr_hwaddr.sa_data);
+
+  static_assert(sizeof(ifr.ifr_hwaddr) >= offsetof(sockaddr, 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_WARN("[id:" << getId() << ",endpoint:" << m_interfaceName
+               << "] ioctl(SIOCADDMULTI) failed: " << std::strerror(errno));
+#endif
+
+  return false;
+}
+
+void
+EthernetFace::sendPacket(const ndn::Block& block)
+{
+  if (!m_pcap)
+    {
+      NFD_LOG_WARN("[id:" << getId() << ",endpoint:" << m_interfaceName
+                   << "] Trying to send on closed face");
+      return fail("Face closed");
+    }
+
+  BOOST_ASSERT(block.size() <= m_interfaceMtu);
+
+  /// \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
+  if (block.size() < ethernet::MIN_DATA_LEN)
+    {
+      static const uint8_t padding[ethernet::MIN_DATA_LEN] = {};
+      buffer.appendByteArray(padding, ethernet::MIN_DATA_LEN - block.size());
+    }
+
+  // construct and prepend the ethernet header
+  static uint16_t ethertype = htons(ethernet::ETHERTYPE_NDN);
+  buffer.prependByteArray(reinterpret_cast<const uint8_t*>(&ethertype), ethernet::TYPE_LEN);
+  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());
+  if (sent < 0)
+    {
+      return fail("pcap_inject: " + std::string(pcap_geterr(m_pcap.get())));
+    }
+  else if (static_cast<size_t>(sent) < buffer.size())
+    {
+      return fail("Failed to inject frame");
+    }
+
+  NFD_LOG_TRACE("[id:" << getId() << ",endpoint:" << m_interfaceName
+                << "] Successfully sent: " << block.size() << " bytes");
+  this->getMutableCounters().getNOutBytes() += block.size();
+}
+
+void
+EthernetFace::handleRead(const boost::system::error_code& error, size_t)
+{
+  if (!m_pcap)
+    return fail("Face closed");
+
+  if (error)
+    return processErrorCode(error);
+
+  pcap_pkthdr* header;
+  const uint8_t* packet;
+  int ret = pcap_next_ex(m_pcap.get(), &header, &packet);
+  if (ret < 0)
+    {
+      return fail("pcap_next_ex: " + std::string(pcap_geterr(m_pcap.get())));
+    }
+  else if (ret == 0)
+    {
+      NFD_LOG_WARN("[id:" << getId() << ",endpoint:" << m_interfaceName << "] Read timeout");
+    }
+  else
+    {
+      processIncomingPacket(header, packet);
+    }
+
+  m_socket->async_read_some(boost::asio::null_buffers(),
+                            bind(&EthernetFace::handleRead, this,
+                                 boost::asio::placeholders::error,
+                                 boost::asio::placeholders::bytes_transferred));
+}
+
+void
+EthernetFace::processIncomingPacket(const pcap_pkthdr* header, const uint8_t* packet)
+{
+  size_t length = header->caplen;
+  if (length < ethernet::HDR_LEN + ethernet::MIN_DATA_LEN)
+    {
+      NFD_LOG_WARN("[id:" << getId() << ",endpoint:" << m_interfaceName
+                   << "] Received frame is too short (" << length << " bytes)");
+      return;
+    }
+
+  const ether_header* eh = reinterpret_cast<const ether_header*>(packet);
+  const ethernet::Address sourceAddress(eh->ether_shost);
+
+  // assert in case BPF fails to filter unwanted frames
+  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");
+  BOOST_ASSERT_MSG(ntohs(eh->ether_type) == ethernet::ETHERTYPE_NDN,
+                   "Received frame with unrecognized ethertype");
+
+  packet += ethernet::HDR_LEN;
+  length -= ethernet::HDR_LEN;
+
+  /// \todo Reserve space in front and at the back of the underlying buffer
+  Block fragment;
+  bool isOk = Block::fromBuffer(packet, length, fragment);
+  if (!isOk)
+    {
+      NFD_LOG_WARN("[id:" << getId() << ",endpoint:" << m_interfaceName
+                   << "] Block received from " << sourceAddress.toString()
+                   << " is invalid or too large to process");
+      return;
+    }
+
+  NFD_LOG_TRACE("[id:" << getId() << ",endpoint:" << m_interfaceName
+                << "] Received: " << fragment.size() << " bytes from "
+                << sourceAddress.toString());
+  this->getMutableCounters().getNInBytes() += fragment.size();
+
+  Reassembler& reassembler = m_reassemblers[sourceAddress];
+  if (!reassembler.pms)
+    {
+      // new sender, setup a PartialMessageStore for it
+      reassembler.pms.reset(new ndnlp::PartialMessageStore);
+      reassembler.pms->onReceive +=
+        [this, sourceAddress] (const Block& block) {
+          NFD_LOG_TRACE("[id:" << getId() << ",endpoint:" << m_interfaceName
+                        << "] All fragments received from " << sourceAddress.toString());
+          if (!decodeAndDispatchInput(block))
+            NFD_LOG_WARN("[id:" << getId() << ",endpoint:" << m_interfaceName
+                         << "] Received unrecognized TLV block of type " << block.type()
+                         << " from " << sourceAddress.toString());
+        };
+    }
+
+  scheduler::cancel(reassembler.expireEvent);
+  reassembler.expireEvent = scheduler::schedule(REASSEMBLER_LIFETIME,
+    [this, sourceAddress] {
+      BOOST_VERIFY(m_reassemblers.erase(sourceAddress) == 1);
+    });
+
+  try {
+    reassembler.pms->receiveNdnlpData(fragment);
+  }
+  catch (const ndnlp::ParseError& e) {
+    NFD_LOG_WARN("[id:" << getId() << ",endpoint:" << m_interfaceName
+                 << "] Received invalid NDNLP fragment from "
+                 << sourceAddress.toString() << " : " << e.what());
+  }
+}
+
+void
+EthernetFace::processErrorCode(const boost::system::error_code& error)
+{
+  if (error == boost::asio::error::operation_aborted)
+    // cancel() has been called on the socket
+    return;
+
+  std::string msg;
+  if (error == boost::asio::error::eof)
+    {
+      msg = "Face closed";
+      NFD_LOG_DEBUG("[id:" << getId() << ",endpoint:" << m_interfaceName << "] " << msg);
+    }
+  else
+    {
+      msg = "Receive operation failed: " + error.message();
+      NFD_LOG_WARN("[id:" << getId() << ",endpoint:" << m_interfaceName << "] " << msg);
+    }
+  fail(msg);
+}
+
+size_t
+EthernetFace::getInterfaceMtu() const
+{
+#ifdef SIOCGIFMTU
+#if defined(__APPLE__) || defined(__FreeBSD__)
+  // see bug #2328
+  using boost::asio::ip::udp;
+  udp::socket sock(ref(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)
+    return static_cast<size_t>(ifr.ifr_mtu);
+
+  NFD_LOG_WARN("[id:" << getId() << ",endpoint:" << m_interfaceName
+               << "] Failed to get interface MTU: " << std::strerror(errno));
+#endif
+
+  return ethernet::MAX_DATA_LEN;
+}
+
+} // namespace nfd
diff --git a/NFD/daemon/face/ethernet-face.hpp b/NFD/daemon/face/ethernet-face.hpp
new file mode 100644
index 0000000..ce72a62
--- /dev/null
+++ b/NFD/daemon/face/ethernet-face.hpp
@@ -0,0 +1,169 @@
+/* -*- 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_DAEMON_FACE_ETHERNET_FACE_HPP
+#define NFD_DAEMON_FACE_ETHERNET_FACE_HPP
+
+#include "common.hpp"
+#include "face.hpp"
+#include "ndnlp-partial-message-store.hpp"
+#include "ndnlp-slicer.hpp"
+#include "core/network-interface.hpp"
+
+#include <unordered_map>
+
+#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;
+
+namespace nfd {
+
+/**
+ * @brief Implementation of Face abstraction that uses raw
+ *        Ethernet frames as underlying transport mechanism
+ */
+class EthernetFace : public Face
+{
+public:
+  /**
+   * @brief EthernetFace-related error
+   */
+  struct Error : public Face::Error
+  {
+    Error(const std::string& what) : Face::Error(what) {}
+  };
+
+  EthernetFace(const shared_ptr<boost::asio::posix::stream_descriptor>& socket,
+               const NetworkInterfaceInfo& interface,
+               const ethernet::Address& address);
+
+  virtual
+  ~EthernetFace();
+
+  /// send an Interest
+  virtual void
+  sendInterest(const Interest& interest);
+
+  /// send a Data
+  virtual void
+  sendData(const Data& data);
+
+  /**
+   * @brief Closes the face
+   *
+   * This terminates all communication on the face and triggers the onFail() event.
+   */
+  virtual void
+  close();
+
+private:
+  /**
+   * @brief Allocates and initializes a libpcap context for live capture
+   */
+  void
+  pcapInit();
+
+  /**
+   * @brief Installs a BPF filter on the receiving socket
+   *
+   * @param filterString string containing the source BPF program
+   */
+  void
+  setPacketFilter(const char* filterString);
+
+  /**
+   * @brief Enables receiving frames addressed to our MAC multicast group
+   *
+   * @return true if successful, false otherwise
+   */
+  bool
+  joinMulticastGroup();
+
+  /**
+   * @brief Sends the specified TLV block on the network wrapped in an Ethernet frame
+   */
+  void
+  sendPacket(const ndn::Block& block);
+
+  /**
+   * @brief Receive callback
+   */
+  void
+  handleRead(const boost::system::error_code& error, size_t nBytesRead);
+
+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);
+
+private:
+  /**
+   * @brief Handles errors encountered by Boost.Asio on the receive path
+   */
+  void
+  processErrorCode(const boost::system::error_code& error);
+
+  /**
+   * @brief Returns the MTU of the underlying network interface
+   */
+  size_t
+  getInterfaceMtu() const;
+
+private:
+  struct Reassembler
+  {
+    unique_ptr<ndnlp::PartialMessageStore> pms;
+    EventId expireEvent;
+  };
+
+  unique_ptr<pcap_t, void(*)(pcap_t*)> m_pcap;
+  shared_ptr<boost::asio::posix::stream_descriptor> m_socket;
+
+#if defined(__linux__)
+  int m_interfaceIndex;
+#endif
+  std::string m_interfaceName;
+  ethernet::Address m_srcAddress;
+  ethernet::Address m_destAddress;
+
+  size_t m_interfaceMtu;
+  unique_ptr<ndnlp::Slicer> m_slicer;
+  std::unordered_map<ethernet::Address, Reassembler> m_reassemblers;
+  static const time::nanoseconds REASSEMBLER_LIFETIME;
+};
+
+} // namespace nfd
+
+#endif // NFD_DAEMON_FACE_ETHERNET_FACE_HPP
diff --git a/NFD/daemon/face/ethernet-factory.cpp b/NFD/daemon/face/ethernet-factory.cpp
new file mode 100644
index 0000000..a4579f4
--- /dev/null
+++ b/NFD/daemon/face/ethernet-factory.cpp
@@ -0,0 +1,84 @@
+/* -*- 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/>.
+ */
+
+#include "ethernet-factory.hpp"
+#include "face/ethernet-face.hpp"
+
+#include "core/logger.hpp"
+#include "core/global-io.hpp"
+
+namespace nfd {
+
+NFD_LOG_INIT("EthernetFactory");
+
+shared_ptr<EthernetFace>
+EthernetFactory::createMulticastFace(const NetworkInterfaceInfo& interface,
+                                     const ethernet::Address &address)
+{
+  if (!address.isMulticast())
+    throw Error(address.toString() + " is not a multicast address");
+
+  shared_ptr<EthernetFace> face = findMulticastFace(interface.name, address);
+  if (face)
+    return face;
+
+  auto socket = make_shared<boost::asio::posix::stream_descriptor>(ref(getGlobalIoService()));
+  face = make_shared<EthernetFace>(socket, interface, address);
+
+  auto key = std::make_pair(interface.name, address);
+  face->onFail += [this, key] (const std::string& reason) {
+    m_multicastFaces.erase(key);
+  };
+  m_multicastFaces.insert({key, face});
+
+  return face;
+}
+
+shared_ptr<EthernetFace>
+EthernetFactory::findMulticastFace(const std::string& interfaceName,
+                                   const ethernet::Address& address) const
+{
+  auto it = m_multicastFaces.find({interfaceName, address});
+  if (it != m_multicastFaces.end())
+    return it->second;
+  else
+    return {};
+}
+
+void
+EthernetFactory::createFace(const FaceUri& uri,
+                            const FaceCreatedCallback& onCreated,
+                            const FaceConnectFailedCallback& onConnectFailed)
+{
+  throw Error("EthernetFactory does not support 'createFace' operation");
+}
+
+std::list<shared_ptr<const Channel>>
+EthernetFactory::getChannels() const
+{
+  return {};
+}
+
+} // namespace nfd
diff --git a/NFD/daemon/face/ethernet-factory.hpp b/NFD/daemon/face/ethernet-factory.hpp
new file mode 100644
index 0000000..ed81f5f
--- /dev/null
+++ b/NFD/daemon/face/ethernet-factory.hpp
@@ -0,0 +1,114 @@
+/* -*- 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_DAEMON_FACE_ETHERNET_FACTORY_HPP
+#define NFD_DAEMON_FACE_ETHERNET_FACTORY_HPP
+
+#include "protocol-factory.hpp"
+#include "core/network-interface.hpp"
+
+namespace nfd {
+
+class EthernetFace;
+
+class EthernetFactory : public ProtocolFactory
+{
+public:
+  /**
+   * \brief Exception of EthernetFactory
+   */
+  class Error : public ProtocolFactory::Error
+  {
+  public:
+    explicit
+    Error(const std::string& what)
+      : ProtocolFactory::Error(what)
+    {
+    }
+  };
+
+  typedef std::map<std::pair<std::string, ethernet::Address>,
+                   shared_ptr<EthernetFace>> MulticastFaceMap;
+
+  // from ProtocolFactory
+  virtual void
+  createFace(const FaceUri& uri,
+             const FaceCreatedCallback& onCreated,
+             const FaceConnectFailedCallback& onConnectFailed);
+
+  /**
+   * \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 EthernetFace::Error
+   */
+  shared_ptr<EthernetFace>
+  createMulticastFace(const NetworkInterfaceInfo& interface,
+                      const ethernet::Address& address);
+
+  /**
+   * \brief Get map of configured multicast faces
+   */
+  const MulticastFaceMap&
+  getMulticastFaces() const;
+
+  virtual std::list<shared_ptr<const Channel>>
+  getChannels() const;
+
+private:
+  /**
+   * \brief Look up EthernetFace using specified interface and address
+   *
+   * \returns shared pointer to the existing EthernetFace object or
+   *          empty shared pointer when such face does not exist
+   *
+   * \throws never
+   */
+  shared_ptr<EthernetFace>
+  findMulticastFace(const std::string& interfaceName,
+                    const ethernet::Address& address) const;
+
+private:
+  MulticastFaceMap m_multicastFaces;
+};
+
+inline const EthernetFactory::MulticastFaceMap&
+EthernetFactory::getMulticastFaces() const
+{
+  return m_multicastFaces;
+}
+
+} // namespace nfd
+
+#endif // NFD_DAEMON_FACE_ETHERNET_FACTORY_HPP
diff --git a/NFD/daemon/face/face-counters.hpp b/NFD/daemon/face/face-counters.hpp
new file mode 100644
index 0000000..6dc1d6a
--- /dev/null
+++ b/NFD/daemon/face/face-counters.hpp
@@ -0,0 +1,251 @@
+/* -*- 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_DAEMON_FACE_FACE_COUNTERS_HPP
+#define NFD_DAEMON_FACE_FACE_COUNTERS_HPP
+
+#include "common.hpp"
+
+namespace nfd {
+
+/** \brief represents a counter of number of packets
+ */
+// PacketCounter is noncopyable, because increment should be called on the counter,
+// not a copy of it; it's implicitly convertible to uint64_t to be observed
+class PacketCounter : noncopyable
+{
+public:
+  typedef uint64_t rep;
+
+  PacketCounter()
+    : m_value(0)
+  {
+  }
+
+  operator rep() const
+  {
+    return m_value;
+  }
+
+  PacketCounter&
+  operator++()
+  {
+    ++m_value;
+    return *this;
+  }
+  // postfix ++ operator is not provided because it's not needed
+
+  void
+  set(rep value)
+  {
+    m_value = value;
+  }
+
+private:
+  rep m_value;
+};
+
+/** \brief represents a counter of number of bytes
+ */
+// ByteCounter is noncopyable, because increment should be called on the counter,
+// not a copy of it; it's implicitly convertible to uint64_t to be observed
+class ByteCounter : noncopyable
+{
+public:
+  typedef uint64_t rep;
+
+  ByteCounter()
+    : m_value(0)
+  {
+  }
+
+  operator rep() const
+  {
+    return m_value;
+  }
+
+  ByteCounter&
+  operator+=(rep n)
+  {
+    m_value += n;
+    return *this;
+  }
+
+  void
+  set(rep value)
+  {
+    m_value = value;
+  }
+
+private:
+  rep m_value;
+};
+
+/** \brief contains network layer packet counters
+ */
+class NetworkLayerCounters : noncopyable
+{
+public:
+  /// incoming Interest
+  const PacketCounter&
+  getNInInterests() const
+  {
+    return m_nInInterests;
+  }
+
+  PacketCounter&
+  getNInInterests()
+  {
+    return m_nInInterests;
+  }
+
+  /// incoming Data
+  const PacketCounter&
+  getNInDatas() const
+  {
+    return m_nInDatas;
+  }
+
+  PacketCounter&
+  getNInDatas()
+  {
+    return m_nInDatas;
+  }
+
+  /// outgoing Interest
+  const PacketCounter&
+  getNOutInterests() const
+  {
+    return m_nOutInterests;
+  }
+
+  PacketCounter&
+  getNOutInterests()
+  {
+    return m_nOutInterests;
+  }
+
+  /// outgoing Data
+  const PacketCounter&
+  getNOutDatas() const
+  {
+    return m_nOutDatas;
+  }
+
+  PacketCounter&
+  getNOutDatas()
+  {
+    return m_nOutDatas;
+  }
+
+protected:
+  /** \brief copy current obseverations to a struct
+   *  \param recipient an object with set methods for counters
+   */
+  template<typename R>
+  void
+  copyTo(R& recipient) const
+  {
+    recipient.setNInInterests(this->getNInInterests());
+    recipient.setNInDatas(this->getNInDatas());
+    recipient.setNOutInterests(this->getNOutInterests());
+    recipient.setNOutDatas(this->getNOutDatas());
+  }
+
+private:
+  PacketCounter m_nInInterests;
+  PacketCounter m_nInDatas;
+  PacketCounter m_nOutInterests;
+  PacketCounter m_nOutDatas;
+};
+
+/** \brief contains link layer byte counters
+ */
+class LinkLayerCounters : noncopyable
+{
+public:
+  /// received bytes
+  const ByteCounter&
+  getNInBytes() const
+  {
+    return m_nInBytes;
+  }
+
+  ByteCounter&
+  getNInBytes()
+  {
+    return m_nInBytes;
+  }
+
+  /// sent bytes
+  const ByteCounter&
+  getNOutBytes() const
+  {
+    return m_nOutBytes;
+  }
+
+  ByteCounter&
+  getNOutBytes()
+  {
+    return m_nOutBytes;
+  }
+
+protected:
+  /** \brief copy current obseverations to a struct
+   *  \param recipient an object with set methods for counters
+   */
+  template<typename R>
+  void
+  copyTo(R& recipient) const
+  {
+    recipient.setNInBytes(this->getNInBytes());
+    recipient.setNOutBytes(this->getNOutBytes());
+  }
+
+private:
+  ByteCounter m_nInBytes;
+  ByteCounter m_nOutBytes;
+};
+
+/** \brief contains counters on face
+ */
+class FaceCounters : public NetworkLayerCounters, public LinkLayerCounters
+{
+public:
+  /** \brief copy current obseverations to a struct
+   *  \param recipient an object with set methods for counters
+   */
+  template<typename R>
+  void
+  copyTo(R& recipient) const
+  {
+    this->NetworkLayerCounters::copyTo(recipient);
+    this->LinkLayerCounters::copyTo(recipient);
+  }
+};
+
+} // namespace nfd
+
+#endif // NFD_DAEMON_FACE_FACE_COUNTERS_HPP
diff --git a/NFD/daemon/face/face.cpp b/NFD/daemon/face/face.cpp
new file mode 100644
index 0000000..7d3c1b7
--- /dev/null
+++ b/NFD/daemon/face/face.cpp
@@ -0,0 +1,167 @@
+/* -*- 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/>.
+ */
+
+#include "face.hpp"
+#include "core/logger.hpp"
+
+namespace nfd {
+
+Face::Face(const FaceUri& remoteUri, const FaceUri& localUri, bool isLocal)
+  : m_id(INVALID_FACEID)
+  , m_isLocal(isLocal)
+  , m_remoteUri(remoteUri)
+  , m_localUri(localUri)
+  , m_isOnDemand(false)
+  , m_isFailed(false)
+{
+  onReceiveInterest += [this](const ndn::Interest&) { ++m_counters.getNInInterests(); };
+  onReceiveData     += [this](const ndn::Data&) {     ++m_counters.getNInDatas(); };
+  onSendInterest    += [this](const ndn::Interest&) { ++m_counters.getNOutInterests(); };
+  onSendData        += [this](const ndn::Data&) {     ++m_counters.getNOutDatas(); };
+}
+
+Face::~Face()
+{
+}
+
+FaceId
+Face::getId() const
+{
+  return m_id;
+}
+
+// this method is private and should be used only by the FaceTable
+void
+Face::setId(FaceId faceId)
+{
+  m_id = faceId;
+}
+
+void
+Face::setDescription(const std::string& description)
+{
+  m_description = description;
+}
+
+const std::string&
+Face::getDescription() const
+{
+  return m_description;
+}
+
+bool
+Face::isMultiAccess() const
+{
+  return false;
+}
+
+bool
+Face::isUp() const
+{
+  return true;
+}
+
+bool
+Face::decodeAndDispatchInput(const Block& element)
+{
+  try {
+    /// \todo Ensure lazy field decoding process
+
+    if (element.type() == tlv::Interest)
+      {
+        shared_ptr<Interest> i = make_shared<Interest>();
+        i->wireDecode(element);
+        this->onReceiveInterest(*i);
+      }
+    else if (element.type() == tlv::Data)
+      {
+        shared_ptr<Data> d = make_shared<Data>();
+        d->wireDecode(element);
+        this->onReceiveData(*d);
+      }
+    else
+      return false;
+
+    return true;
+  }
+  catch (tlv::Error&) {
+    return false;
+  }
+}
+
+void
+Face::fail(const std::string& reason)
+{
+  if (m_isFailed) {
+    return;
+  }
+
+  m_isFailed = true;
+  this->onFail(reason);
+
+  this->onFail.clear();
+}
+
+template<typename FaceTraits>
+void
+Face::copyStatusTo(FaceTraits& traits) const
+{
+  traits.setFaceId(getId())
+    .setRemoteUri(getRemoteUri().toString())
+    .setLocalUri(getLocalUri().toString());
+
+  if (isLocal()) {
+    traits.setFaceScope(ndn::nfd::FACE_SCOPE_LOCAL);
+  }
+  else {
+    traits.setFaceScope(ndn::nfd::FACE_SCOPE_NON_LOCAL);
+  }
+
+  if (isOnDemand()) {
+    traits.setFacePersistency(ndn::nfd::FACE_PERSISTENCY_ON_DEMAND);
+  }
+  else {
+    traits.setFacePersistency(ndn::nfd::FACE_PERSISTENCY_PERSISTENT);
+  }
+}
+
+template void
+Face::copyStatusTo<ndn::nfd::FaceStatus>(ndn::nfd::FaceStatus&) const;
+
+template void
+Face::copyStatusTo<ndn::nfd::FaceEventNotification>(ndn::nfd::FaceEventNotification&) const;
+
+ndn::nfd::FaceStatus
+Face::getFaceStatus() const
+{
+  ndn::nfd::FaceStatus status;
+  copyStatusTo(status);
+
+  this->getCounters().copyTo(status);
+
+  return status;
+}
+
+} //namespace nfd
diff --git a/NFD/daemon/face/face.hpp b/NFD/daemon/face/face.hpp
new file mode 100644
index 0000000..40a77a1
--- /dev/null
+++ b/NFD/daemon/face/face.hpp
@@ -0,0 +1,252 @@
+/* -*- 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_DAEMON_FACE_FACE_HPP
+#define NFD_DAEMON_FACE_FACE_HPP
+
+#include "common.hpp"
+#include "face-counters.hpp"
+
+#include <ndn-cxx/util/face-uri.hpp>
+#include <ndn-cxx/management/nfd-face-status.hpp>
+#include <ndn-cxx/management/nfd-face-event-notification.hpp>
+
+namespace nfd {
+
+/** \class FaceId
+ *  \brief identifies a face
+ */
+typedef int FaceId;
+
+/// indicates an invalid FaceId
+const FaceId INVALID_FACEID = -1;
+
+/// identifies the InternalFace used in management
+const FaceId FACEID_INTERNAL_FACE = 1;
+/// identifies a packet comes from the ContentStore, in LocalControlHeader incomingFaceId
+const FaceId FACEID_CONTENT_STORE = 254;
+/// identifies the NullFace that drops every packet
+const FaceId FACEID_NULL = 255;
+/// upper bound of reserved FaceIds
+const FaceId FACEID_RESERVED_MAX = 255;
+
+using ndn::util::FaceUri;
+
+/** \brief represents a face
+ */
+class Face : noncopyable, public enable_shared_from_this<Face>
+{
+public:
+  /**
+   * \brief Face-related error
+   */
+  class Error : public std::runtime_error
+  {
+  public:
+    explicit
+    Error(const std::string& what)
+      : std::runtime_error(what)
+    {
+    }
+  };
+
+  Face(const FaceUri& remoteUri, const FaceUri& localUri, bool isLocal = false);
+
+  virtual
+  ~Face();
+
+  /// fires when an Interest is received
+  EventEmitter<Interest> onReceiveInterest;
+
+  /// fires when a Data is received
+  EventEmitter<Data> onReceiveData;
+
+  /// fires when an Interest is sent out
+  EventEmitter<Interest> onSendInterest;
+
+  /// fires when a Data is sent out
+  EventEmitter<Data> onSendData;
+
+  /// fires when face disconnects or fails to perform properly
+  EventEmitter<std::string/*reason*/> onFail;
+
+  /// send an Interest
+  virtual void
+  sendInterest(const Interest& interest) = 0;
+
+  /// send a Data
+  virtual void
+  sendData(const Data& data) = 0;
+
+  /** \brief Close the face
+   *
+   *  This terminates all communication on the face and cause
+   *  onFail() method event to be invoked
+   */
+  virtual void
+  close() = 0;
+
+public: // attributes
+  FaceId
+  getId() const;
+
+  /** \brief Set the description
+   *
+   *  This is typically invoked by mgmt on set description command
+   */
+  virtual void
+  setDescription(const std::string& description);
+
+  /// Get the description
+  virtual const std::string&
+  getDescription() const;
+
+  /** \brief Get whether face is connected to a local app
+   */
+  bool
+  isLocal() const;
+
+  /** \brief Get whether packets sent this Face may reach multiple peers
+   *
+   *  In this base class this property is always false.
+   */
+  virtual bool
+  isMultiAccess() const;
+
+  /** \brief Get whether underlying communication is up
+   *
+   *  In this base class this property is always true.
+   */
+  virtual bool
+  isUp() const;
+
+  /** \brief Get whether face is created on demand or explicitly via FaceManagement protocol
+   */
+  bool
+  isOnDemand() const;
+
+  const FaceCounters&
+  getCounters() const;
+
+  /** \return a FaceUri that represents the remote endpoint
+   */
+  const FaceUri&
+  getRemoteUri() const;
+
+  /** \return a FaceUri that represents the local endpoint (NFD side)
+   */
+  const FaceUri&
+  getLocalUri() const;
+
+  /** \return FaceTraits data structure filled with the current FaceTraits status
+   */
+  template<typename FaceTraits>
+  void
+  copyStatusTo(FaceTraits& traits) const;
+
+  /** \return FaceStatus data structure filled with the current Face status
+   */
+  virtual ndn::nfd::FaceStatus
+  getFaceStatus() const;
+
+protected:
+  // this is a non-virtual method
+  bool
+  decodeAndDispatchInput(const Block& element);
+
+  FaceCounters&
+  getMutableCounters();
+
+  void
+  setOnDemand(bool isOnDemand);
+
+  /** \brief fail the face and raise onFail event if it's UP; otherwise do nothing
+   */
+  void
+  fail(const std::string& reason);
+
+private:
+  void
+  setId(FaceId faceId);
+
+private:
+  FaceId m_id;
+  std::string m_description;
+  bool m_isLocal; // for scoping purposes
+  FaceCounters m_counters;
+  FaceUri m_remoteUri;
+  FaceUri m_localUri;
+  bool m_isOnDemand;
+  bool m_isFailed;
+
+  // allow setting FaceId
+  friend class FaceTable;
+};
+
+inline bool
+Face::isLocal() const
+{
+  return m_isLocal;
+}
+
+inline const FaceCounters&
+Face::getCounters() const
+{
+  return m_counters;
+}
+
+inline FaceCounters&
+Face::getMutableCounters()
+{
+  return m_counters;
+}
+
+inline const FaceUri&
+Face::getRemoteUri() const
+{
+  return m_remoteUri;
+}
+
+inline const FaceUri&
+Face::getLocalUri() const
+{
+  return m_localUri;
+}
+
+inline void
+Face::setOnDemand(bool isOnDemand)
+{
+  m_isOnDemand = isOnDemand;
+}
+
+inline bool
+Face::isOnDemand() const
+{
+  return m_isOnDemand;
+}
+
+} // namespace nfd
+
+#endif // NFD_DAEMON_FACE_FACE_HPP
diff --git a/NFD/daemon/face/local-face.hpp b/NFD/daemon/face/local-face.hpp
new file mode 100644
index 0000000..e1c34c3
--- /dev/null
+++ b/NFD/daemon/face/local-face.hpp
@@ -0,0 +1,210 @@
+/* -*- 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
+ *
+ * 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_LOCAL_FACE_HPP
+#define NFD_DAEMON_FACE_LOCAL_FACE_HPP
+
+#include "face.hpp"
+#include <ndn-cxx/management/nfd-control-parameters.hpp>
+
+namespace nfd {
+
+using ndn::nfd::LocalControlFeature;
+using ndn::nfd::LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID;
+using ndn::nfd::LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID;
+
+/** \brief represents a face
+ */
+class LocalFace : public Face
+{
+public:
+  LocalFace(const FaceUri& remoteUri, const FaceUri& localUri);
+
+  /** \brief get whether any LocalControlHeader feature is enabled
+   *
+   * \returns true if any feature is enabled.
+   */
+  bool
+  isLocalControlHeaderEnabled() const;
+
+  /** \brief get whether a specific LocalControlHeader feature is enabled
+   *
+   *  \param feature The feature.
+   *  \returns true if the specified feature is enabled.
+   */
+  bool
+  isLocalControlHeaderEnabled(LocalControlFeature feature) const;
+
+  /** \brief enable or disable a LocalControlHeader feature
+   *
+   *  \param feature The feature. Cannot be LOCAL_CONTROL_FEATURE_ANY
+   *                                     or LOCAL_CONTROL_FEATURE_MAX
+   */
+  void
+  setLocalControlHeaderFeature(LocalControlFeature feature, bool enabled = true);
+
+public:
+
+  static const size_t LOCAL_CONTROL_FEATURE_MAX = 3; /// upper bound of LocalControlFeature enum
+  static const size_t LOCAL_CONTROL_FEATURE_ANY = 0; /// any feature
+
+protected:
+  // statically overridden from Face
+
+  /** \brief Decode block into Interest/Data, considering potential LocalControlHeader
+   *
+   *  If LocalControlHeader is present, the encoded data is filtered out, based
+   *  on enabled features on the face.
+   */
+  bool
+  decodeAndDispatchInput(const Block& element);
+
+  // LocalFace-specific methods
+
+  /** \brief Check if LocalControlHeader needs to be included, taking into account
+   *         both set parameters in supplied LocalControlHeader and features
+   *         enabled on the local face.
+   */
+  bool
+  isEmptyFilteredLocalControlHeader(const ndn::nfd::LocalControlHeader& header) const;
+
+  /** \brief Create LocalControlHeader, considering enabled features
+   */
+  template<class Packet>
+  Block
+  filterAndEncodeLocalControlHeader(const Packet& packet);
+
+private:
+  std::vector<bool> m_localControlHeaderFeatures;
+};
+
+inline
+LocalFace::LocalFace(const FaceUri& remoteUri, const FaceUri& localUri)
+  : Face(remoteUri, localUri, true)
+  , m_localControlHeaderFeatures(LocalFace::LOCAL_CONTROL_FEATURE_MAX)
+{
+}
+
+inline bool
+LocalFace::isLocalControlHeaderEnabled() const
+{
+  return m_localControlHeaderFeatures[LOCAL_CONTROL_FEATURE_ANY];
+}
+
+inline bool
+LocalFace::isLocalControlHeaderEnabled(LocalControlFeature feature) const
+{
+  BOOST_ASSERT(0 < feature &&
+               static_cast<size_t>(feature) < m_localControlHeaderFeatures.size());
+  return m_localControlHeaderFeatures[feature];
+}
+
+inline void
+LocalFace::setLocalControlHeaderFeature(LocalControlFeature feature, bool enabled/* = true*/)
+{
+  BOOST_ASSERT(0 < feature &&
+               static_cast<size_t>(feature) < m_localControlHeaderFeatures.size());
+
+  m_localControlHeaderFeatures[feature] = enabled;
+
+  m_localControlHeaderFeatures[LOCAL_CONTROL_FEATURE_ANY] =
+    std::find(m_localControlHeaderFeatures.begin() + 1,
+              m_localControlHeaderFeatures.end(), true) <
+              m_localControlHeaderFeatures.end();
+  // 'find(..) < .end()' instead of 'find(..) != .end()' due to LLVM Bug 16816
+}
+
+inline bool
+LocalFace::decodeAndDispatchInput(const Block& element)
+{
+  try {
+    const Block& payload = ndn::nfd::LocalControlHeader::getPayload(element);
+
+    // If received LocalControlHeader, but it is not enabled on the face
+    if ((&payload != &element) && !this->isLocalControlHeaderEnabled())
+      return false;
+
+    if (payload.type() == tlv::Interest)
+      {
+        shared_ptr<Interest> i = make_shared<Interest>();
+        i->wireDecode(payload);
+        if (&payload != &element)
+          {
+            i->getLocalControlHeader().wireDecode(element,
+              false,
+              this->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID));
+          }
+
+        this->onReceiveInterest(*i);
+      }
+    else if (payload.type() == tlv::Data)
+      {
+        shared_ptr<Data> d = make_shared<Data>();
+        d->wireDecode(payload);
+
+        /// \todo Uncomment and correct the following when we have more
+        ///       options in LocalControlHeader that apply for incoming
+        ///       Data packets (if ever)
+        // if (&payload != &element)
+        //   {
+        //
+        //     d->getLocalControlHeader().wireDecode(element,
+        //       false,
+        //       false);
+        //   }
+
+        this->onReceiveData(*d);
+      }
+    else
+      return false;
+
+    return true;
+  }
+  catch (tlv::Error&) {
+    return false;
+  }
+}
+
+inline bool
+LocalFace::isEmptyFilteredLocalControlHeader(const ndn::nfd::LocalControlHeader& header) const
+{
+  if (!this->isLocalControlHeaderEnabled())
+    return true;
+
+  return header.empty(this->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID),
+                      false);
+}
+
+template<class Packet>
+inline Block
+LocalFace::filterAndEncodeLocalControlHeader(const Packet& packet)
+{
+  return packet.getLocalControlHeader().wireEncode(packet,
+           this->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID),
+           false);
+}
+
+} // namespace nfd
+
+#endif // NFD_DAEMON_FACE_LOCAL_FACE_HPP
diff --git a/NFD/daemon/face/multicast-udp-face.cpp b/NFD/daemon/face/multicast-udp-face.cpp
new file mode 100644
index 0000000..34c6cd2
--- /dev/null
+++ b/NFD/daemon/face/multicast-udp-face.cpp
@@ -0,0 +1,87 @@
+/* -*- 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/>.
+ */
+
+#include "multicast-udp-face.hpp"
+
+namespace nfd {
+
+NFD_LOG_INCLASS_2TEMPLATE_SPECIALIZATION_DEFINE(DatagramFace,
+                                                MulticastUdpFace::protocol, Multicast,
+                                                "MulticastUdpFace");
+
+MulticastUdpFace::MulticastUdpFace(const shared_ptr<MulticastUdpFace::protocol::socket>& recvSocket,
+                                   const shared_ptr<MulticastUdpFace::protocol::socket>& sendSocket,
+                                   const MulticastUdpFace::protocol::endpoint& localEndpoint,
+                                   const MulticastUdpFace::protocol::endpoint& multicastEndpoint)
+  : DatagramFace<protocol, Multicast>(FaceUri(multicastEndpoint),
+                                      FaceUri(localEndpoint),
+                                      recvSocket, false)
+  , m_multicastGroup(multicastEndpoint)
+  , m_sendSocket(sendSocket)
+{
+  NFD_LOG_INFO("Creating multicast UDP face for group " << m_multicastGroup);
+}
+
+const MulticastUdpFace::protocol::endpoint&
+MulticastUdpFace::getMulticastGroup() const
+{
+  return m_multicastGroup;
+}
+
+void
+MulticastUdpFace::sendBlock(const Block& block)
+{
+  m_sendSocket->async_send_to(boost::asio::buffer(block.wire(), block.size()),
+                              m_multicastGroup,
+                              bind(&MulticastUdpFace::handleSend, this, _1, _2, block));
+}
+
+void
+MulticastUdpFace::sendInterest(const Interest& interest)
+{
+  onSendInterest(interest);
+
+  NFD_LOG_DEBUG("Sending interest");
+  sendBlock(interest.wireEncode());
+}
+
+void
+MulticastUdpFace::sendData(const Data& data)
+{
+  /// \todo After this method implements duplicate suppression, onSendData event should
+  ///       be triggered only when data is actually sent out
+  onSendData(data);
+
+  NFD_LOG_DEBUG("Sending data");
+  sendBlock(data.wireEncode());
+}
+
+bool
+MulticastUdpFace::isMultiAccess() const
+{
+  return true;
+}
+
+} // namespace nfd
diff --git a/NFD/daemon/face/multicast-udp-face.hpp b/NFD/daemon/face/multicast-udp-face.hpp
new file mode 100644
index 0000000..0ac857e
--- /dev/null
+++ b/NFD/daemon/face/multicast-udp-face.hpp
@@ -0,0 +1,72 @@
+/* -*- 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_DAEMON_FACE_MULTICAST_UDP_FACE_HPP
+#define NFD_DAEMON_FACE_MULTICAST_UDP_FACE_HPP
+
+#include "datagram-face.hpp"
+
+namespace nfd {
+
+/**
+ * \brief Implementation of Face abstraction that uses
+ *        multicast UDP as underlying transport mechanism
+ */
+class MulticastUdpFace : public DatagramFace<boost::asio::ip::udp, Multicast>
+{
+public:
+  /**
+   * \brief Creates a UDP-based face for multicast communication
+   */
+  MulticastUdpFace(const shared_ptr<protocol::socket>& recvSocket,
+                   const shared_ptr<protocol::socket>& sendSocket,
+                   const protocol::endpoint& localEndpoint,
+                   const protocol::endpoint& multicastEndpoint);
+
+  const protocol::endpoint&
+  getMulticastGroup() const;
+
+  // from Face
+  virtual void
+  sendInterest(const Interest& interest);
+
+  virtual void
+  sendData(const Data& data);
+
+  virtual bool
+  isMultiAccess() const;
+
+private:
+  void
+  sendBlock(const Block& block);
+
+private:
+  protocol::endpoint m_multicastGroup;
+  shared_ptr<protocol::socket> m_sendSocket;
+};
+
+} // namespace nfd
+
+#endif // NFD_DAEMON_FACE_MULTICAST_UDP_FACE_HPP
diff --git a/NFD/daemon/face/ndnlp-parse.cpp b/NFD/daemon/face/ndnlp-parse.cpp
new file mode 100644
index 0000000..21d959f
--- /dev/null
+++ b/NFD/daemon/face/ndnlp-parse.cpp
@@ -0,0 +1,92 @@
+/* -*- 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
+ *
+ * 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 "ndnlp-parse.hpp"
+
+namespace nfd {
+namespace ndnlp {
+
+void
+NdnlpData::wireDecode(const Block& wire)
+{
+  if (wire.type() != tlv::NdnlpData) {
+    throw ParseError("top element is not NdnlpData");
+  }
+  wire.parse();
+  const Block::element_container& elements = wire.elements();
+  if (elements.size() < 2) {
+    throw ParseError("NdnlpData element has incorrect number of children");
+  }
+
+  const Block& sequenceElement = elements.front();
+  if (sequenceElement.type() != tlv::NdnlpSequence) {
+    throw ParseError("NdnlpSequence element is missing");
+  }
+  if (sequenceElement.value_size() != sizeof(uint64_t)) {
+    throw ParseError("NdnlpSequence element has incorrect length");
+  }
+  m_seq = be64toh(*reinterpret_cast<const uint64_t*>(&*sequenceElement.value_begin()));
+
+  const Block& payloadElement = elements.back();
+  if (payloadElement.type() != tlv::NdnlpPayload) {
+    throw ParseError("NdnlpPayload element is missing");
+  }
+  m_payload = payloadElement;
+
+  if (elements.size() == 2) { // single wire packet
+    m_fragIndex = 0;
+    m_fragCount = 1;
+    return;
+  }
+  if (elements.size() != 4) {
+    throw ParseError("NdnlpData element has incorrect number of children");
+  }
+
+  const Block& fragIndexElement = elements.at(1);
+  if (fragIndexElement.type() != tlv::NdnlpFragIndex) {
+    throw ParseError("NdnlpFragIndex element is missing");
+  }
+  uint64_t fragIndex = ndn::readNonNegativeInteger(fragIndexElement);
+  if (fragIndex > std::numeric_limits<uint16_t>::max()) {
+    throw ParseError("NdnlpFragIndex is too large");
+  }
+  m_fragIndex = static_cast<uint16_t>(fragIndex);
+
+  const Block& fragCountElement = elements.at(2);
+  if (fragCountElement.type() != tlv::NdnlpFragCount) {
+    throw ParseError("NdnlpFragCount element is missing");
+  }
+  uint64_t fragCount = ndn::readNonNegativeInteger(fragCountElement);
+  if (fragCount > std::numeric_limits<uint16_t>::max()) {
+    throw ParseError("NdnlpFragCount is too large");
+  }
+  m_fragCount = static_cast<uint16_t>(fragCount);
+
+  if (m_fragIndex >= m_fragCount) {
+    throw ParseError("NdnlpFragIndex must be less than NdnlpFragCount");
+  }
+}
+
+} // namespace ndnlp
+} // namespace nfd
diff --git a/NFD/daemon/face/ndnlp-parse.hpp b/NFD/daemon/face/ndnlp-parse.hpp
new file mode 100644
index 0000000..f40c910
--- /dev/null
+++ b/NFD/daemon/face/ndnlp-parse.hpp
@@ -0,0 +1,70 @@
+/* -*- 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
+ *
+ * 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_NDNLP_PARSE_HPP
+#define NFD_DAEMON_FACE_NDNLP_PARSE_HPP
+
+#include "common.hpp"
+#include "ndnlp-tlv.hpp"
+
+namespace nfd {
+namespace ndnlp {
+
+struct ParseError : public std::runtime_error
+{
+  ParseError(const std::string& what)
+    : std::runtime_error(what)
+  {
+  }
+};
+
+/** \brief represents a NdnlpData packet
+ *
+ *  NdnlpData ::= NDNLP-DATA-TYPE TLV-LENGTH
+ *                  NdnlpSequence
+ *                  NdnlpFragIndex?
+ *                  NdnlpFragCount?
+ *                  NdnlpPayload
+ */
+class NdnlpData
+{
+public:
+  /** \brief parse a NdnlpData packet
+   *
+   *  \exception ParseError packet is malformated
+   */
+  void
+  wireDecode(const Block& wire);
+
+public:
+  uint64_t m_seq;
+  uint16_t m_fragIndex;
+  uint16_t m_fragCount;
+  Block m_payload;
+};
+
+} // namespace ndnlp
+} // namespace nfd
+
+#endif // NFD_DAEMON_FACE_NDNLP_PARSE_HPP
diff --git a/NFD/daemon/face/ndnlp-partial-message-store.cpp b/NFD/daemon/face/ndnlp-partial-message-store.cpp
new file mode 100644
index 0000000..54a5537
--- /dev/null
+++ b/NFD/daemon/face/ndnlp-partial-message-store.cpp
@@ -0,0 +1,137 @@
+/* -*- 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
+ *
+ * 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 "ndnlp-partial-message-store.hpp"
+
+namespace nfd {
+namespace ndnlp {
+
+PartialMessage::PartialMessage()
+  : m_fragCount(0)
+  , m_received(0)
+  , m_totalLength(0)
+{
+}
+
+bool
+PartialMessage::add(uint16_t fragIndex, uint16_t fragCount, const Block& payload)
+{
+  if (m_received == 0) { // first packet
+    m_fragCount = fragCount;
+    m_payloads.resize(fragCount);
+  }
+
+  if (m_fragCount != fragCount || fragIndex >= m_fragCount) {
+    return false;
+  }
+
+  if (!m_payloads[fragIndex].empty()) { // duplicate
+    return false;
+  }
+
+  m_payloads[fragIndex] = payload;
+  ++m_received;
+  m_totalLength += payload.value_size();
+  return true;
+}
+
+bool
+PartialMessage::isComplete() const
+{
+  return m_received == m_fragCount;
+}
+
+Block
+PartialMessage::reassemble()
+{
+  BOOST_ASSERT(this->isComplete());
+
+  ndn::BufferPtr buffer = make_shared<ndn::Buffer>(m_totalLength);
+  uint8_t* buf = buffer->get();
+  for (std::vector<Block>::const_iterator it = m_payloads.begin();
+       it != m_payloads.end(); ++it) {
+    const Block& payload = *it;
+    memcpy(buf, payload.value(), payload.value_size());
+    buf += payload.value_size();
+  }
+
+  return Block(buffer);
+}
+
+PartialMessageStore::PartialMessageStore(const time::nanoseconds& idleDuration)
+  : m_idleDuration(idleDuration)
+{
+}
+
+PartialMessageStore::~PartialMessageStore()
+{
+}
+
+void
+PartialMessageStore::receiveNdnlpData(const Block& pkt)
+{
+  NdnlpData parsed;
+  parsed.wireDecode(pkt);
+  if (parsed.m_fragCount == 1) { // single fragment
+    this->onReceive(parsed.m_payload.blockFromValue());
+    return;
+  }
+
+  uint64_t messageIdentifier = parsed.m_seq - parsed.m_fragIndex;
+  shared_ptr<PartialMessage> pm = m_partialMessages[messageIdentifier];
+  if (!static_cast<bool>(pm)) {
+    m_partialMessages[messageIdentifier] = pm = make_shared<PartialMessage>();
+  }
+  this->scheduleCleanup(messageIdentifier, pm);
+
+  pm->add(parsed.m_fragIndex, parsed.m_fragCount, parsed.m_payload);
+  if (pm->isComplete()) {
+    this->onReceive(pm->reassemble());
+    this->cleanup(messageIdentifier);
+  }
+}
+
+void
+PartialMessageStore::scheduleCleanup(uint64_t messageIdentifier,
+                                     shared_ptr<PartialMessage> partialMessage)
+{
+  partialMessage->m_expiry = scheduler::schedule(m_idleDuration,
+    bind(&PartialMessageStore::cleanup, this, messageIdentifier));
+}
+
+void
+PartialMessageStore::cleanup(uint64_t messageIdentifier)
+{
+  std::map<uint64_t, shared_ptr<PartialMessage> >::iterator it =
+    m_partialMessages.find(messageIdentifier);
+  if (it == m_partialMessages.end()) {
+    return;
+  }
+
+  scheduler::cancel(it->second->m_expiry);
+  m_partialMessages.erase(it);
+}
+
+} // namespace ndnlp
+} // namespace nfd
diff --git a/NFD/daemon/face/ndnlp-partial-message-store.hpp b/NFD/daemon/face/ndnlp-partial-message-store.hpp
new file mode 100644
index 0000000..c707d92
--- /dev/null
+++ b/NFD/daemon/face/ndnlp-partial-message-store.hpp
@@ -0,0 +1,105 @@
+/* -*- 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
+ *
+ * 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_NDNLP_PARTIAL_MESSAGE_STORE_HPP
+#define NFD_DAEMON_FACE_NDNLP_PARTIAL_MESSAGE_STORE_HPP
+
+#include "ndnlp-parse.hpp"
+#include "core/scheduler.hpp"
+
+namespace nfd {
+namespace ndnlp {
+
+/** \brief represents a partially received message
+ */
+class PartialMessage : noncopyable
+{
+public:
+  PartialMessage();
+
+  bool
+  add(uint16_t fragIndex, uint16_t fragCount, const Block& payload);
+
+  bool
+  isComplete() const;
+
+  /** \brief reassemble network layer packet
+   *
+   *  isComplete() must be true before calling this method
+   *
+   *  \exception ndn::Block::Error packet is malformated
+   *  \return network layer packet
+   */
+  Block
+  reassemble();
+
+public:
+  EventId m_expiry;
+
+private:
+  size_t m_fragCount;
+  size_t m_received;
+  std::vector<Block> m_payloads;
+  size_t m_totalLength;
+};
+
+/** \brief provides reassembly feature at receiver
+ */
+class PartialMessageStore : noncopyable
+{
+public:
+  explicit
+  PartialMessageStore(const time::nanoseconds& idleDuration = time::milliseconds(100));
+
+  virtual
+  ~PartialMessageStore();
+
+  /** \brief receive a NdnlpData packet
+   *
+   *  \exception ParseError NDNLP packet is malformated
+   *  \exception ndn::Block::Error network layer packet is malformated
+   */
+  void
+  receiveNdnlpData(const Block& pkt);
+
+  /// fires when network layer packet is received
+  EventEmitter<Block> onReceive;
+
+private:
+  void
+  scheduleCleanup(uint64_t messageIdentifier, shared_ptr<PartialMessage> partialMessage);
+
+  void
+  cleanup(uint64_t messageIdentifier);
+
+private:
+  std::map<uint64_t, shared_ptr<PartialMessage> > m_partialMessages;
+
+  time::nanoseconds m_idleDuration;
+};
+
+} // namespace ndnlp
+} // namespace nfd
+
+#endif // NFD_DAEMON_FACE_NDNLP_PARTIAL_MESSAGE_STORE_HPP
diff --git a/NFD/daemon/face/ndnlp-sequence-generator.cpp b/NFD/daemon/face/ndnlp-sequence-generator.cpp
new file mode 100644
index 0000000..842a319
--- /dev/null
+++ b/NFD/daemon/face/ndnlp-sequence-generator.cpp
@@ -0,0 +1,50 @@
+/* -*- 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
+ *
+ * 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 "ndnlp-sequence-generator.hpp"
+
+namespace nfd {
+namespace ndnlp {
+
+SequenceBlock::SequenceBlock(uint64_t start, size_t count)
+  : m_start(start)
+  , m_count(count)
+{
+}
+
+SequenceGenerator::SequenceGenerator()
+  : m_next(0)
+{
+}
+
+SequenceBlock
+SequenceGenerator::nextBlock(size_t count)
+{
+  SequenceBlock sb(m_next, count);
+  m_next += count;
+  return sb;
+}
+
+} // namespace ndnlp
+} // namespace nfd
diff --git a/NFD/daemon/face/ndnlp-sequence-generator.hpp b/NFD/daemon/face/ndnlp-sequence-generator.hpp
new file mode 100644
index 0000000..a3baa6b
--- /dev/null
+++ b/NFD/daemon/face/ndnlp-sequence-generator.hpp
@@ -0,0 +1,90 @@
+/* -*- 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
+ *
+ * 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_NDNLP_SEQUENCE_GENERATOR_HPP
+#define NFD_DAEMON_FACE_NDNLP_SEQUENCE_GENERATOR_HPP
+
+#include "common.hpp"
+
+namespace nfd {
+namespace ndnlp {
+
+/** \brief represents a block of sequence numbers
+ */
+class SequenceBlock
+{
+public:
+  SequenceBlock(uint64_t start, size_t count);
+
+  /** \return{ the pos-th sequence number }
+   */
+  uint64_t
+  operator[](size_t pos) const;
+
+  size_t
+  count() const;
+
+private:
+  uint64_t m_start;
+  size_t m_count;
+};
+
+inline uint64_t
+SequenceBlock::operator[](size_t pos) const
+{
+  if (pos >= m_count)
+    throw std::out_of_range("pos");
+  return m_start + static_cast<uint64_t>(pos);
+}
+
+inline size_t
+SequenceBlock::count() const
+{
+  return m_count;
+}
+
+/** \class SequenceGenerator
+ *  \brief generates sequence numbers
+ */
+class SequenceGenerator : noncopyable
+{
+public:
+  SequenceGenerator();
+
+  /** \brief generates a block of consecutive sequence numbers
+   *
+   *  This block must not overlap with a recent block.
+   */
+  SequenceBlock
+  nextBlock(size_t count);
+
+private:
+  /// next sequence number
+  uint64_t m_next;
+};
+
+} // namespace ndnlp
+} // namespace nfd
+
+#endif // NFD_DAEMON_FACE_NDNLP_SEQUENCE_GENERATOR_HPP
diff --git a/NFD/daemon/face/ndnlp-slicer.cpp b/NFD/daemon/face/ndnlp-slicer.cpp
new file mode 100644
index 0000000..285f79f
--- /dev/null
+++ b/NFD/daemon/face/ndnlp-slicer.cpp
@@ -0,0 +1,135 @@
+/* -*- 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/>.
+ */
+
+#include "ndnlp-slicer.hpp"
+
+#include <ndn-cxx/encoding/encoding-buffer.hpp>
+
+namespace nfd {
+namespace ndnlp {
+
+Slicer::Slicer(size_t mtu)
+  : m_mtu(mtu)
+{
+  this->estimateOverhead();
+}
+
+Slicer::~Slicer()
+{
+}
+
+template<bool T>
+size_t
+Slicer::encodeFragment(ndn::EncodingImpl<T>& blk,
+                       uint64_t seq, uint16_t fragIndex, uint16_t fragCount,
+                       const uint8_t* payload, size_t payloadSize)
+{
+  size_t totalLength = 0;
+
+  // NdnlpPayload
+  size_t payloadLength = blk.prependByteArray(payload, payloadSize);
+  totalLength += payloadLength;
+  totalLength += blk.prependVarNumber(payloadLength);
+  totalLength += blk.prependVarNumber(tlv::NdnlpPayload);
+
+  bool needFragIndexAndCount = fragCount > 1;
+  if (needFragIndexAndCount) {
+    // NdnlpFragCount
+    size_t fragCountLength = blk.prependNonNegativeInteger(fragCount);
+    totalLength += fragCountLength;
+    totalLength += blk.prependVarNumber(fragCountLength);
+    totalLength += blk.prependVarNumber(tlv::NdnlpFragCount);
+
+    // NdnlpFragIndex
+    size_t fragIndexLength = blk.prependNonNegativeInteger(fragIndex);
+    totalLength += fragIndexLength;
+    totalLength += blk.prependVarNumber(fragIndexLength);
+    totalLength += blk.prependVarNumber(tlv::NdnlpFragIndex);
+  }
+
+  // NdnlpSequence
+  uint64_t sequenceBE = htobe64(seq);
+  size_t sequenceLength = blk.prependByteArray(
+    reinterpret_cast<uint8_t*>(&sequenceBE), sizeof(sequenceBE));
+  totalLength += sequenceLength;
+  totalLength += blk.prependVarNumber(sequenceLength);
+  totalLength += blk.prependVarNumber(tlv::NdnlpSequence);
+
+  // NdnlpData
+  totalLength += blk.prependVarNumber(totalLength);
+  totalLength += blk.prependVarNumber(tlv::NdnlpData);
+
+  return totalLength;
+}
+
+void
+Slicer::estimateOverhead()
+{
+  // estimate fragment size with all header fields at largest possible size
+  ndn::EncodingEstimator estimator;
+  size_t estimatedSize = this->encodeFragment(estimator,
+                                              std::numeric_limits<uint64_t>::max(),
+                                              std::numeric_limits<uint16_t>::max() - 1,
+                                              std::numeric_limits<uint16_t>::max(),
+                                              nullptr, m_mtu);
+
+  size_t overhead = estimatedSize - m_mtu; // minus payload length in estimation
+  m_maxPayload = m_mtu - overhead;
+}
+
+PacketArray
+Slicer::slice(const Block& block)
+{
+  BOOST_ASSERT(block.hasWire());
+  const uint8_t* networkPacket = block.wire();
+  size_t networkPacketSize = block.size();
+
+  uint16_t fragCount = static_cast<uint16_t>(
+                         (networkPacketSize / m_maxPayload) +
+                         (networkPacketSize % m_maxPayload == 0 ? 0 : 1)
+                       );
+  PacketArray pa = make_shared<std::vector<Block>>();
+  pa->reserve(fragCount);
+  SequenceBlock seqBlock = m_seqgen.nextBlock(fragCount);
+
+  for (uint16_t fragIndex = 0; fragIndex < fragCount; ++fragIndex) {
+    size_t payloadOffset = fragIndex * m_maxPayload;
+    const uint8_t* payload = networkPacket + payloadOffset;
+    size_t payloadSize = std::min(m_maxPayload, networkPacketSize - payloadOffset);
+
+    ndn::EncodingBuffer buffer(m_mtu, 0);
+    size_t pktSize = this->encodeFragment(buffer,
+      seqBlock[fragIndex], fragIndex, fragCount, payload, payloadSize);
+
+    BOOST_VERIFY(pktSize <= m_mtu);
+
+    pa->push_back(buffer.block());
+  }
+
+  return pa;
+}
+
+} // namespace ndnlp
+} // namespace nfd
diff --git a/NFD/daemon/face/ndnlp-slicer.hpp b/NFD/daemon/face/ndnlp-slicer.hpp
new file mode 100644
index 0000000..f5d9056
--- /dev/null
+++ b/NFD/daemon/face/ndnlp-slicer.hpp
@@ -0,0 +1,79 @@
+/* -*- 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_DAEMON_FACE_NDNLP_SLICER_HPP
+#define NFD_DAEMON_FACE_NDNLP_SLICER_HPP
+
+#include "ndnlp-tlv.hpp"
+#include "ndnlp-sequence-generator.hpp"
+
+namespace nfd {
+namespace ndnlp {
+
+typedef shared_ptr<std::vector<Block>> PacketArray;
+
+/** \brief provides fragmentation feature at sender
+ */
+class Slicer : noncopyable
+{
+public:
+  /** \param mtu maximum size of NDNLP header and payload
+   *  \note If NDNLP packets are to be encapsulated in an additional header
+   *        (eg. in UDP packets), the caller must deduct such overhead.
+   */
+  explicit
+  Slicer(size_t mtu);
+
+  virtual
+  ~Slicer();
+
+  PacketArray
+  slice(const Block& block);
+
+private:
+  template<bool T>
+  size_t
+  encodeFragment(ndn::EncodingImpl<T>& blk,
+                 uint64_t seq, uint16_t fragIndex, uint16_t fragCount,
+                 const uint8_t* payload, size_t payloadSize);
+
+  /// estimate the size of NDNLP header and maximum payload size per packet
+  void
+  estimateOverhead();
+
+private:
+  SequenceGenerator m_seqgen;
+
+  /// maximum packet size
+  size_t m_mtu;
+
+  /// maximum payload size
+  size_t m_maxPayload;
+};
+
+} // namespace ndnlp
+} // namespace nfd
+
+#endif // NFD_DAEMON_FACE_NDNLP_SLICER_HPP
diff --git a/NFD/daemon/face/ndnlp-tlv.hpp b/NFD/daemon/face/ndnlp-tlv.hpp
new file mode 100644
index 0000000..0c41f52
--- /dev/null
+++ b/NFD/daemon/face/ndnlp-tlv.hpp
@@ -0,0 +1,43 @@
+/* -*- 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
+ *
+ * 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_NDNLP_TLV_HPP
+#define NFD_DAEMON_FACE_NDNLP_TLV_HPP
+
+namespace nfd {
+namespace tlv {
+
+enum
+{
+  NdnlpData      = 80,
+  NdnlpSequence  = 81,
+  NdnlpFragIndex = 82,
+  NdnlpFragCount = 83,
+  NdnlpPayload   = 84
+};
+
+} // namespace tlv
+} // namespace nfd
+
+#endif // NFD_DAEMON_FACE_NDNLP_TLV_HPP
diff --git a/NFD/daemon/face/null-face.cpp b/NFD/daemon/face/null-face.cpp
new file mode 100644
index 0000000..5d55693
--- /dev/null
+++ b/NFD/daemon/face/null-face.cpp
@@ -0,0 +1,54 @@
+/* -*- 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/>.
+ */
+
+#include "null-face.hpp"
+
+namespace nfd {
+
+// FIB might restrict creating a nexthop record toward non-local face in /localhost namespace.
+// NullFace has isLocal=true to enable creating a "blackhole" FIB entry under /localhost.
+
+NullFace::NullFace(const FaceUri& uri)
+  : Face(uri, uri, true)
+{
+}
+
+void
+NullFace::sendInterest(const Interest& interest)
+{
+}
+
+void
+NullFace::sendData(const Data& data)
+{
+}
+
+void
+NullFace::close()
+{
+  this->fail("close");
+}
+
+} // namespace nfd
diff --git a/NFD/daemon/face/null-face.hpp b/NFD/daemon/face/null-face.hpp
new file mode 100644
index 0000000..3dae571
--- /dev/null
+++ b/NFD/daemon/face/null-face.hpp
@@ -0,0 +1,60 @@
+/* -*- 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_DAEMON_FACE_NULL_FACE_HPP
+#define NFD_DAEMON_FACE_NULL_FACE_HPP
+
+#include "face.hpp"
+
+namespace nfd {
+
+/**
+ * \brief a Face that has no underlying transport and drops every packet
+ */
+class NullFace : public Face
+{
+public:
+  explicit
+  NullFace(const FaceUri& uri = FaceUri("null://"));
+
+  virtual void
+  sendInterest(const Interest& interest);
+
+  /// send a Data
+  virtual void
+  sendData(const Data& data);
+
+  /** \brief Close the face
+   *
+   *  This terminates all communication on the face and cause
+   *  onFail() method event to be invoked
+   */
+  virtual void
+  close();
+};
+
+} // namespace nfd
+
+#endif // NFD_DAEMON_FACE_NULL_FACE_HPP
diff --git a/NFD/daemon/face/protocol-factory.hpp b/NFD/daemon/face/protocol-factory.hpp
new file mode 100644
index 0000000..0542017
--- /dev/null
+++ b/NFD/daemon/face/protocol-factory.hpp
@@ -0,0 +1,79 @@
+/* -*- 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_DAEMON_FACE_PROTOCOL_FACTORY_HPP
+#define NFD_DAEMON_FACE_PROTOCOL_FACTORY_HPP
+
+#include "face.hpp"
+
+namespace nfd {
+
+class Channel;
+
+/**
+ * \brief Prototype for the callback called when face is created
+ *        (as a response to incoming connection or after connection
+ *        is established)
+ */
+typedef function<void(const shared_ptr<Face>& newFace)> FaceCreatedCallback;
+
+/**
+ * \brief Prototype for the callback that is called when face is failed to
+ *        get created
+ */
+typedef function<void(const std::string& reason)> FaceConnectFailedCallback;
+
+
+class ProtocolFactory
+{
+public:
+  /**
+   * \brief Base class for all exceptions thrown by channel factories
+   */
+  struct Error : public std::runtime_error
+  {
+    Error(const std::string& what) : std::runtime_error(what) {}
+  };
+
+  /** \brief Try to create Face using the supplied Face URI
+   *
+   * This method should automatically choose channel, based on supplied Face URI
+   * and create face.
+   *
+   * \throws Factory::Error if Factory does not support connect operation
+   */
+  virtual void
+  createFace(const FaceUri& uri,
+             const FaceCreatedCallback& onCreated,
+             const FaceConnectFailedCallback& onConnectFailed) = 0;
+
+  virtual std::list<shared_ptr<const Channel> >
+  getChannels() const = 0;
+
+};
+
+} // namespace nfd
+
+#endif // NFD_DAEMON_FACE_PROTOCOL_FACTORY_HPP
diff --git a/NFD/daemon/face/stream-face.hpp b/NFD/daemon/face/stream-face.hpp
new file mode 100644
index 0000000..e719da3
--- /dev/null
+++ b/NFD/daemon/face/stream-face.hpp
@@ -0,0 +1,394 @@
+/* -*- 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_DAEMON_FACE_STREAM_FACE_HPP
+#define NFD_DAEMON_FACE_STREAM_FACE_HPP
+
+#include "face.hpp"
+#include "local-face.hpp"
+#include "core/logger.hpp"
+
+namespace nfd {
+
+// forward declaration
+template<class T, class U, class V> struct StreamFaceSenderImpl;
+
+template<class Protocol, class FaceBase = Face>
+class StreamFace : public FaceBase
+{
+public:
+  typedef Protocol protocol;
+
+  /**
+   * \brief Create instance of StreamFace
+   */
+  StreamFace(const FaceUri& remoteUri, const FaceUri& localUri,
+             const shared_ptr<typename protocol::socket>& socket,
+             bool isOnDemand);
+
+  virtual
+  ~StreamFace();
+
+  // from Face
+  virtual void
+  sendInterest(const Interest& interest);
+
+  virtual void
+  sendData(const Data& data);
+
+  virtual void
+  close();
+
+protected:
+  void
+  processErrorCode(const boost::system::error_code& error);
+
+  void
+  sendFromQueue();
+
+  void
+  handleSend(const boost::system::error_code& error,
+             size_t nBytesSent);
+
+  void
+  handleReceive(const boost::system::error_code& error,
+                size_t nBytesReceived);
+
+  void
+  shutdownSocket();
+
+  void
+  deferredClose(const shared_ptr<Face>& face);
+
+protected:
+  shared_ptr<typename protocol::socket> m_socket;
+
+private:
+  uint8_t m_inputBuffer[ndn::MAX_NDN_PACKET_SIZE];
+  size_t m_inputBufferSize;
+  std::queue<Block> m_sendQueue;
+
+  friend struct StreamFaceSenderImpl<Protocol, FaceBase, Interest>;
+  friend struct StreamFaceSenderImpl<Protocol, FaceBase, Data>;
+
+  NFD_LOG_INCLASS_DECLARE();
+};
+
+// All inherited classes must use
+// NFD_LOG_INCLASS_TEMPLATE_SPECIALIZATION_DEFINE(StreamFace, <specialization-parameter>, "Name");
+
+
+/** \brief Class allowing validation of the StreamFace use
+ *
+ *  For example, partial specialization based on boost::asio::ip::tcp should check
+ *  that local endpoint is loopback
+ *
+ *  @throws Face::Error if validation failed
+ */
+template<class Protocol, class U>
+struct StreamFaceValidator
+{
+  static void
+  validateSocket(typename Protocol::socket& socket)
+  {
+  }
+};
+
+
+template<class T, class FaceBase>
+inline
+StreamFace<T, FaceBase>::StreamFace(const FaceUri& remoteUri, const FaceUri& localUri,
+                const shared_ptr<typename StreamFace::protocol::socket>& socket,
+                bool isOnDemand)
+  : FaceBase(remoteUri, localUri)
+  , m_socket(socket)
+  , m_inputBufferSize(0)
+{
+  FaceBase::setOnDemand(isOnDemand);
+  StreamFaceValidator<T, FaceBase>::validateSocket(*socket);
+  m_socket->async_receive(boost::asio::buffer(m_inputBuffer, ndn::MAX_NDN_PACKET_SIZE), 0,
+                          bind(&StreamFace<T, FaceBase>::handleReceive, this, _1, _2));
+}
+
+template<class T, class U>
+inline
+StreamFace<T, U>::~StreamFace()
+{
+}
+
+
+template<class Protocol, class FaceBase, class Packet>
+struct StreamFaceSenderImpl
+{
+  static void
+  send(StreamFace<Protocol, FaceBase>& face, const Packet& packet)
+  {
+    bool wasQueueEmpty = face.m_sendQueue.empty();
+    face.m_sendQueue.push(packet.wireEncode());
+
+    if (wasQueueEmpty)
+      face.sendFromQueue();
+  }
+};
+
+// partial specialization (only classes can be partially specialized)
+template<class Protocol, class Packet>
+struct StreamFaceSenderImpl<Protocol, LocalFace, Packet>
+{
+  static void
+  send(StreamFace<Protocol, LocalFace>& face, const Packet& packet)
+  {
+    bool wasQueueEmpty = face.m_sendQueue.empty();
+
+    if (!face.isEmptyFilteredLocalControlHeader(packet.getLocalControlHeader()))
+      {
+        face.m_sendQueue.push(face.filterAndEncodeLocalControlHeader(packet));
+      }
+    face.m_sendQueue.push(packet.wireEncode());
+
+    if (wasQueueEmpty)
+      face.sendFromQueue();
+  }
+};
+
+
+template<class T, class U>
+inline void
+StreamFace<T, U>::sendInterest(const Interest& interest)
+{
+  this->onSendInterest(interest);
+  StreamFaceSenderImpl<T, U, Interest>::send(*this, interest);
+}
+
+template<class T, class U>
+inline void
+StreamFace<T, U>::sendData(const Data& data)
+{
+  this->onSendData(data);
+  StreamFaceSenderImpl<T, U, Data>::send(*this, data);
+}
+
+template<class T, class U>
+inline void
+StreamFace<T, U>::close()
+{
+  if (!m_socket->is_open())
+    return;
+
+  NFD_LOG_INFO("[id:" << this->getId()
+               << ",uri:" << this->getRemoteUri()
+               << "] Close connection");
+
+  shutdownSocket();
+  this->fail("Close connection");
+}
+
+template<class T, class U>
+inline void
+StreamFace<T, U>::processErrorCode(const boost::system::error_code& error)
+{
+  if (error == boost::asio::error::operation_aborted ||   // when cancel() is called
+      error == boost::asio::error::shut_down)             // after shutdown() is called
+    return;
+
+  if (!m_socket->is_open())
+    {
+      this->fail("Connection closed");
+      return;
+    }
+
+  if (error == boost::asio::error::eof)
+    {
+      NFD_LOG_INFO("[id:" << this->getId()
+                   << ",uri:" << this->getRemoteUri()
+                   << "] Connection closed");
+    }
+  else
+    {
+      NFD_LOG_WARN("[id:" << this->getId()
+                   << ",uri:" << this->getRemoteUri()
+                   << "] Send or receive operation failed, closing face: "
+                   << error.message());
+    }
+
+  shutdownSocket();
+
+  if (error == boost::asio::error::eof)
+    {
+      this->fail("Connection closed");
+    }
+  else
+    {
+      this->fail("Send or receive operation failed, closing face: " + error.message());
+    }
+}
+
+template<class T, class U>
+inline void
+StreamFace<T, U>::sendFromQueue()
+{
+  const Block& block = this->m_sendQueue.front();
+  boost::asio::async_write(*this->m_socket, boost::asio::buffer(block),
+                           bind(&StreamFace<T, U>::handleSend, this, _1, _2));
+}
+
+template<class T, class U>
+inline void
+StreamFace<T, U>::handleSend(const boost::system::error_code& error,
+                             size_t nBytesSent)
+{
+  if (error)
+    return processErrorCode(error);
+
+  BOOST_ASSERT(!m_sendQueue.empty());
+
+  NFD_LOG_TRACE("[id:" << this->getId()
+                << ",uri:" << this->getRemoteUri()
+                << "] Successfully sent: " << nBytesSent << " bytes");
+  this->getMutableCounters().getNOutBytes() += nBytesSent;
+
+  m_sendQueue.pop();
+  if (!m_sendQueue.empty())
+    sendFromQueue();
+}
+
+template<class T, class U>
+inline void
+StreamFace<T, U>::handleReceive(const boost::system::error_code& error,
+                                size_t nBytesReceived)
+{
+  if (error)
+    return processErrorCode(error);
+
+  NFD_LOG_TRACE("[id:" << this->getId()
+                << ",uri:" << this->getRemoteUri()
+                << "] Received: " << nBytesReceived << " bytes");
+  this->getMutableCounters().getNInBytes() += nBytesReceived;
+
+  m_inputBufferSize += nBytesReceived;
+
+  size_t offset = 0;
+
+  bool isOk = true;
+  Block element;
+  while (m_inputBufferSize - offset > 0)
+    {
+      isOk = Block::fromBuffer(m_inputBuffer + offset, m_inputBufferSize - offset, element);
+      if (!isOk)
+        break;
+
+      offset += element.size();
+
+      BOOST_ASSERT(offset <= m_inputBufferSize);
+
+      if (!this->decodeAndDispatchInput(element))
+        {
+          NFD_LOG_WARN("[id:" << this->getId()
+                       << ",uri:" << this->getRemoteUri()
+                       << "] Received unrecognized block of type ["
+                       << element.type() << "]");
+          // ignore unknown packet and proceed
+        }
+    }
+  if (!isOk && m_inputBufferSize == ndn::MAX_NDN_PACKET_SIZE && offset == 0)
+    {
+      NFD_LOG_WARN("[id:" << this->getId()
+                   << ",uri:" << this->getRemoteUri()
+                   << "] Failed to parse incoming packet or packet too large to process, "
+                   << "closing down the face");
+      shutdownSocket();
+      this->fail("Failed to parse incoming packet or packet too large to process, "
+                 "closing down the face");
+      return;
+    }
+
+  if (offset > 0)
+    {
+      if (offset != m_inputBufferSize)
+        {
+          std::copy(m_inputBuffer + offset, m_inputBuffer + m_inputBufferSize,
+                    m_inputBuffer);
+          m_inputBufferSize -= offset;
+        }
+      else
+        {
+          m_inputBufferSize = 0;
+        }
+    }
+
+  m_socket->async_receive(boost::asio::buffer(m_inputBuffer + m_inputBufferSize,
+                                              ndn::MAX_NDN_PACKET_SIZE - m_inputBufferSize), 0,
+                          bind(&StreamFace<T, U>::handleReceive, this, _1, _2));
+}
+
+template<class T, class U>
+inline void
+StreamFace<T, U>::shutdownSocket()
+{
+  // Cancel all outstanding operations and shutdown the socket
+  // so that no further sends or receives are possible.
+  // Use the non-throwing variants and ignore errors, if any.
+  boost::system::error_code error;
+  m_socket->cancel(error);
+  m_socket->shutdown(protocol::socket::shutdown_both, error);
+
+  boost::asio::io_service& io = m_socket->get_io_service();
+  // ensure that the Face object is alive at least until all pending
+  // handlers are dispatched
+  io.post(bind(&StreamFace<T, U>::deferredClose, this, this->shared_from_this()));
+
+  // Some bug or feature of Boost.Asio (see http://redmine.named-data.net/issues/1856):
+  //
+  // When shutdownSocket is called from within a socket event (e.g., from handleReceive),
+  // m_socket->shutdown() does not trigger the cancellation of the handleSend callback.
+  // Instead, handleSend is invoked as nothing bad happened.
+  //
+  // In order to prevent the assertion in handleSend from failing, we clear the queue
+  // and close the socket in deferredClose, i.e., after all callbacks scheduled up to
+  // this point have been executed.  If more send operations are scheduled after this
+  // point, they will fail because the socket has been shutdown, and their callbacks
+  // will be invoked with error code == asio::error::shut_down.
+}
+
+template<class T, class U>
+inline void
+StreamFace<T, U>::deferredClose(const shared_ptr<Face>& face)
+{
+  NFD_LOG_DEBUG("[id:" << this->getId()
+                << ",uri:" << this->getRemoteUri()
+                << "] Clearing send queue");
+
+  // clear send queue
+  std::queue<Block> emptyQueue;
+  std::swap(emptyQueue, m_sendQueue);
+
+  // use the non-throwing variant and ignore errors, if any
+  boost::system::error_code error;
+  m_socket->close(error);
+}
+
+} // namespace nfd
+
+#endif // NFD_DAEMON_FACE_STREAM_FACE_HPP
diff --git a/NFD/daemon/face/tcp-channel.cpp b/NFD/daemon/face/tcp-channel.cpp
new file mode 100644
index 0000000..cd2744d
--- /dev/null
+++ b/NFD/daemon/face/tcp-channel.cpp
@@ -0,0 +1,281 @@
+/* -*- 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/>.
+ */
+
+#include "tcp-channel.hpp"
+#include "core/global-io.hpp"
+
+namespace nfd {
+
+NFD_LOG_INIT("TcpChannel");
+
+using namespace boost::asio;
+
+TcpChannel::TcpChannel(const tcp::Endpoint& localEndpoint)
+  : m_localEndpoint(localEndpoint)
+  , m_isListening(false)
+{
+  this->setUri(FaceUri(localEndpoint));
+}
+
+TcpChannel::~TcpChannel()
+{
+}
+
+void
+TcpChannel::listen(const FaceCreatedCallback& onFaceCreated,
+                   const ConnectFailedCallback& onAcceptFailed,
+                   int backlog/* = tcp::acceptor::max_connections*/)
+{
+  m_acceptor = make_shared<ip::tcp::acceptor>(ref(getGlobalIoService()));
+  m_acceptor->open(m_localEndpoint.protocol());
+  m_acceptor->set_option(ip::tcp::acceptor::reuse_address(true));
+  if (m_localEndpoint.address().is_v6())
+    {
+      m_acceptor->set_option(ip::v6_only(true));
+    }
+  m_acceptor->bind(m_localEndpoint);
+  m_acceptor->listen(backlog);
+
+  shared_ptr<ip::tcp::socket> clientSocket =
+    make_shared<ip::tcp::socket>(ref(getGlobalIoService()));
+  m_acceptor->async_accept(*clientSocket,
+                           bind(&TcpChannel::handleSuccessfulAccept, this, _1,
+                                clientSocket,
+                                onFaceCreated, onAcceptFailed));
+
+  m_isListening = true;
+}
+
+void
+TcpChannel::connect(const tcp::Endpoint& remoteEndpoint,
+                    const TcpChannel::FaceCreatedCallback& onFaceCreated,
+                    const TcpChannel::ConnectFailedCallback& onConnectFailed,
+                    const time::seconds& timeout/* = time::seconds(4)*/)
+{
+  ChannelFaceMap::iterator i = m_channelFaces.find(remoteEndpoint);
+  if (i != m_channelFaces.end()) {
+    onFaceCreated(i->second);
+    return;
+  }
+
+  shared_ptr<ip::tcp::socket> clientSocket =
+    make_shared<ip::tcp::socket>(ref(getGlobalIoService()));
+
+  EventId connectTimeoutEvent = scheduler::schedule(timeout,
+                                                    bind(&TcpChannel::handleFailedConnect, this,
+                                                         clientSocket, onConnectFailed));
+
+  clientSocket->async_connect(remoteEndpoint,
+                              bind(&TcpChannel::handleSuccessfulConnect, this, _1,
+                                   clientSocket, connectTimeoutEvent,
+                                   onFaceCreated, onConnectFailed));
+}
+
+void
+TcpChannel::connect(const std::string& remoteHost, const std::string& remotePort,
+                    const TcpChannel::FaceCreatedCallback& onFaceCreated,
+                    const TcpChannel::ConnectFailedCallback& onConnectFailed,
+                    const time::seconds& timeout/* = time::seconds(4)*/)
+{
+  shared_ptr<ip::tcp::socket> clientSocket =
+    make_shared<ip::tcp::socket>(ref(getGlobalIoService()));
+
+  ip::tcp::resolver::query query(remoteHost, remotePort);
+  shared_ptr<ip::tcp::resolver> resolver =
+    make_shared<ip::tcp::resolver>(ref(getGlobalIoService()));
+
+  EventId connectTimeoutEvent = scheduler::schedule(timeout,
+                                                    bind(&TcpChannel::handleFailedConnect, this,
+                                                         clientSocket, onConnectFailed));
+
+  resolver->async_resolve(query,
+                          bind(&TcpChannel::handleEndpointResolution, this, _1, _2,
+                               clientSocket, connectTimeoutEvent,
+                               onFaceCreated, onConnectFailed,
+                               resolver));
+}
+
+size_t
+TcpChannel::size() const
+{
+  return m_channelFaces.size();
+}
+
+void
+TcpChannel::createFace(const shared_ptr<ip::tcp::socket>& socket,
+                       const FaceCreatedCallback& onFaceCreated,
+                       bool isOnDemand)
+{
+  tcp::Endpoint remoteEndpoint = socket->remote_endpoint();
+
+  shared_ptr<Face> face;
+
+  ChannelFaceMap::iterator faceMapPos = m_channelFaces.find(remoteEndpoint);
+  if (faceMapPos == m_channelFaces.end())
+    {
+      if (socket->local_endpoint().address().is_loopback())
+        face = make_shared<TcpLocalFace>(socket, isOnDemand);
+      else
+        face = make_shared<TcpFace>(socket, isOnDemand);
+
+      face->onFail += bind(&TcpChannel::afterFaceFailed, this, remoteEndpoint);
+
+      m_channelFaces[remoteEndpoint] = face;
+    }
+  else
+    {
+      // we've already created a a face for this endpoint, just reuse it
+      face = faceMapPos->second;
+
+      boost::system::error_code error;
+      socket->shutdown(ip::tcp::socket::shutdown_both, error);
+      socket->close(error);
+    }
+
+  // Need to invoke the callback regardless of whether or not we have already created
+  // the face so that control responses and such can be sent.
+  onFaceCreated(face);
+}
+
+void
+TcpChannel::afterFaceFailed(tcp::Endpoint &remoteEndpoint)
+{
+  NFD_LOG_DEBUG("afterFaceFailed: " << remoteEndpoint);
+  m_channelFaces.erase(remoteEndpoint);
+}
+
+void
+TcpChannel::handleSuccessfulAccept(const boost::system::error_code& error,
+                                   const shared_ptr<boost::asio::ip::tcp::socket>& socket,
+                                   const FaceCreatedCallback& onFaceCreated,
+                                   const ConnectFailedCallback& onAcceptFailed)
+{
+  if (error) {
+    if (error == boost::system::errc::operation_canceled) // when socket is closed by someone
+      return;
+
+    NFD_LOG_DEBUG("Connect to remote endpoint failed: "
+                  << error.category().message(error.value()));
+
+    if (static_cast<bool>(onAcceptFailed))
+      onAcceptFailed("Connect to remote endpoint failed: " +
+                     error.category().message(error.value()));
+    return;
+  }
+
+  // prepare accepting the next connection
+  shared_ptr<ip::tcp::socket> clientSocket =
+    make_shared<ip::tcp::socket>(ref(getGlobalIoService()));
+  m_acceptor->async_accept(*clientSocket,
+                           bind(&TcpChannel::handleSuccessfulAccept, this, _1,
+                                clientSocket,
+                                onFaceCreated, onAcceptFailed));
+
+  NFD_LOG_DEBUG("[" << m_localEndpoint << "] "
+                "<< Connection from " << socket->remote_endpoint());
+  createFace(socket, onFaceCreated, true);
+}
+
+void
+TcpChannel::handleSuccessfulConnect(const boost::system::error_code& error,
+                                    const shared_ptr<ip::tcp::socket>& socket,
+                                    const EventId& connectTimeoutEvent,
+                                    const FaceCreatedCallback& onFaceCreated,
+                                    const ConnectFailedCallback& onConnectFailed)
+{
+  scheduler::cancel(connectTimeoutEvent);
+
+#if (BOOST_VERSION == 105400)
+  // To handle regression in Boost 1.54
+  // https://svn.boost.org/trac/boost/ticket/8795
+  boost::system::error_code anotherErrorCode;
+  socket->remote_endpoint(anotherErrorCode);
+  if (error || anotherErrorCode) {
+#else
+  if (error) {
+#endif
+
+    if (error == boost::system::errc::operation_canceled) // when socket is closed by someone
+      return;
+
+    socket->close();
+
+    NFD_LOG_DEBUG("Connect to remote endpoint failed: "
+                  << error.category().message(error.value()));
+
+    onConnectFailed("Connect to remote endpoint failed: " +
+                    error.category().message(error.value()));
+    return;
+  }
+
+  NFD_LOG_DEBUG("[" << m_localEndpoint << "] "
+                ">> Connection to " << socket->remote_endpoint());
+
+  createFace(socket, onFaceCreated, false);
+}
+
+void
+TcpChannel::handleFailedConnect(const shared_ptr<ip::tcp::socket>& socket,
+                                const ConnectFailedCallback& onConnectFailed)
+{
+  NFD_LOG_DEBUG("Connect to remote endpoint timed out");
+  onConnectFailed("Connect to remote endpoint timed out");
+  socket->close(); // abort the connection
+}
+
+void
+TcpChannel::handleEndpointResolution(const boost::system::error_code& error,
+                                     ip::tcp::resolver::iterator remoteEndpoint,
+                                     const shared_ptr<boost::asio::ip::tcp::socket>& socket,
+                                     const EventId& connectTimeoutEvent,
+                                     const FaceCreatedCallback& onFaceCreated,
+                                     const ConnectFailedCallback& onConnectFailed,
+                                     const shared_ptr<ip::tcp::resolver>& resolver)
+{
+  if (error ||
+      remoteEndpoint == ip::tcp::resolver::iterator())
+    {
+      if (error == boost::system::errc::operation_canceled) // when socket is closed by someone
+        return;
+
+      socket->close();
+      scheduler::cancel(connectTimeoutEvent);
+
+      NFD_LOG_DEBUG("Remote endpoint hostname or port cannot be resolved: "
+                    << error.category().message(error.value()));
+
+      onConnectFailed("Remote endpoint hostname or port cannot be resolved: " +
+                      error.category().message(error.value()));
+      return;
+    }
+
+  // got endpoint, now trying to connect (only try the first resolution option)
+  socket->async_connect(*remoteEndpoint,
+                        bind(&TcpChannel::handleSuccessfulConnect, this, _1,
+                             socket, connectTimeoutEvent,
+                             onFaceCreated, onConnectFailed));
+}
+
+} // namespace nfd
diff --git a/NFD/daemon/face/tcp-channel.hpp b/NFD/daemon/face/tcp-channel.hpp
new file mode 100644
index 0000000..f126a47
--- /dev/null
+++ b/NFD/daemon/face/tcp-channel.hpp
@@ -0,0 +1,162 @@
+/* -*- 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
+ *
+ * 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_TCP_CHANNEL_HPP
+#define NFD_DAEMON_FACE_TCP_CHANNEL_HPP
+
+#include "channel.hpp"
+#include "tcp-face.hpp"
+#include "core/scheduler.hpp"
+
+namespace nfd {
+
+namespace tcp {
+typedef boost::asio::ip::tcp::endpoint Endpoint;
+} // namespace tcp
+
+/**
+ * \brief Class implementing TCP-based channel to create faces
+ *
+ * Channel can create faces as a response to incoming TCP
+ * connections (TcpChannel::listen needs to be called for that
+ * to work) or explicitly after using TcpChannel::connect method.
+ */
+class TcpChannel : public Channel
+{
+public:
+  /**
+   * \brief Create TCP channel for the local endpoint
+   *
+   * To enable creation faces upon incoming connections,
+   * one needs to explicitly call TcpChannel::listen method.
+   */
+  explicit
+  TcpChannel(const tcp::Endpoint& localEndpoint);
+
+  virtual
+  ~TcpChannel();
+
+  /**
+   * \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 onAcceptFailed Callback to notify when channel fails (accept call
+   *                       returns an error)
+   * \param backlog        The maximum length of the queue of pending incoming
+   *                       connections
+   */
+  void
+  listen(const FaceCreatedCallback& onFaceCreated,
+         const ConnectFailedCallback& onAcceptFailed,
+         int backlog = boost::asio::ip::tcp::acceptor::max_connections);
+
+  /**
+   * \brief Create a face by establishing connection to remote endpoint
+   */
+  void
+  connect(const tcp::Endpoint& remoteEndpoint,
+          const FaceCreatedCallback& onFaceCreated,
+          const ConnectFailedCallback& onConnectFailed,
+          const time::seconds& timeout = time::seconds(4));
+
+  /**
+   * \brief Create a face by establishing connection to the specified
+   *        remote host and remote port
+   *
+   * This method will never block and will return immediately. All
+   * necessary hostname and port resolution and connection will happen
+   * in asynchronous mode.
+   *
+   * If connection cannot be established within specified timeout, it
+   * will be aborted.
+   */
+  void
+  connect(const std::string& remoteHost, const std::string& remotePort,
+          const FaceCreatedCallback& onFaceCreated,
+          const ConnectFailedCallback& onConnectFailed,
+          const time::seconds& timeout = time::seconds(4));
+
+  /**
+   * \brief Get number of faces in the channel
+   */
+  size_t
+  size() const;
+
+  bool
+  isListening() const;
+
+private:
+  void
+  createFace(const shared_ptr<boost::asio::ip::tcp::socket>& socket,
+             const FaceCreatedCallback& onFaceCreated,
+             bool isOnDemand);
+
+  void
+  afterFaceFailed(tcp::Endpoint &endpoint);
+
+  void
+  handleSuccessfulAccept(const boost::system::error_code& error,
+                         const shared_ptr<boost::asio::ip::tcp::socket>& socket,
+                         const FaceCreatedCallback& onFaceCreated,
+                         const ConnectFailedCallback& onConnectFailed);
+
+  void
+  handleSuccessfulConnect(const boost::system::error_code& error,
+                          const shared_ptr<boost::asio::ip::tcp::socket>& socket,
+                          const EventId& connectTimeoutEvent,
+                          const FaceCreatedCallback& onFaceCreated,
+                          const ConnectFailedCallback& onConnectFailed);
+
+  void
+  handleFailedConnect(const shared_ptr<boost::asio::ip::tcp::socket>& socket,
+                      const ConnectFailedCallback& onConnectFailed);
+
+  void
+  handleEndpointResolution(const boost::system::error_code& error,
+                           boost::asio::ip::tcp::resolver::iterator remoteEndpoint,
+                           const shared_ptr<boost::asio::ip::tcp::socket>& socket,
+                           const EventId& connectTimeoutEvent,
+                           const FaceCreatedCallback& onFaceCreated,
+                           const ConnectFailedCallback& onConnectFailed,
+                           const shared_ptr<boost::asio::ip::tcp::resolver>& resolver);
+
+private:
+  tcp::Endpoint m_localEndpoint;
+
+  typedef std::map< tcp::Endpoint, shared_ptr<Face> > ChannelFaceMap;
+  ChannelFaceMap m_channelFaces;
+
+  bool m_isListening;
+  shared_ptr<boost::asio::ip::tcp::acceptor> m_acceptor;
+};
+
+inline bool
+TcpChannel::isListening() const
+{
+  return m_isListening;
+}
+
+} // namespace nfd
+
+#endif // NFD_DAEMON_FACE_TCP_CHANNEL_HPP
diff --git a/NFD/daemon/face/tcp-face.cpp b/NFD/daemon/face/tcp-face.cpp
new file mode 100644
index 0000000..6428b03
--- /dev/null
+++ b/NFD/daemon/face/tcp-face.cpp
@@ -0,0 +1,50 @@
+/* -*- 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
+ *
+ * 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 "tcp-face.hpp"
+
+namespace nfd {
+
+NFD_LOG_INCLASS_TEMPLATE_SPECIALIZATION_DEFINE(StreamFace, TcpFace::protocol, "TcpFace");
+
+NFD_LOG_INCLASS_2TEMPLATE_SPECIALIZATION_DEFINE(StreamFace,
+                                                TcpLocalFace::protocol, LocalFace,
+                                                "TcpLocalFace");
+
+TcpFace::TcpFace(const shared_ptr<TcpFace::protocol::socket>& socket, bool isOnDemand)
+  : StreamFace<protocol>(FaceUri(socket->remote_endpoint()),
+                         FaceUri(socket->local_endpoint()),
+                         socket, isOnDemand)
+{
+}
+
+TcpLocalFace::TcpLocalFace(const shared_ptr<TcpLocalFace::protocol::socket>& socket,
+                           bool isOnDemand)
+  : StreamFace<protocol, LocalFace>(FaceUri(socket->remote_endpoint()),
+                                    FaceUri(socket->local_endpoint()),
+                                    socket, isOnDemand)
+{
+}
+
+} // namespace nfd
diff --git a/NFD/daemon/face/tcp-face.hpp b/NFD/daemon/face/tcp-face.hpp
new file mode 100644
index 0000000..3eddde1
--- /dev/null
+++ b/NFD/daemon/face/tcp-face.hpp
@@ -0,0 +1,80 @@
+/* -*- 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
+ *
+ * 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_TCP_FACE_HPP
+#define NFD_DAEMON_FACE_TCP_FACE_HPP
+
+#include "stream-face.hpp"
+
+namespace nfd {
+
+/**
+ * \brief Implementation of Face abstraction that uses TCP
+ *        as underlying transport mechanism
+ */
+class TcpFace : public StreamFace<boost::asio::ip::tcp>
+{
+public:
+  TcpFace(const shared_ptr<protocol::socket>& socket,
+          bool isOnDemand);
+};
+
+//
+
+/**
+ * \brief Implementation of Face abstraction that uses TCP
+ *        as underlying transport mechanism and is used for
+ *        local communication (can enable LocalControlHeader)
+ */
+class TcpLocalFace : public StreamFace<boost::asio::ip::tcp, LocalFace>
+{
+public:
+  TcpLocalFace(const shared_ptr<protocol::socket>& socket,
+               bool isOnDemand);
+};
+
+
+/** \brief Class validating use of TcpLocalFace
+ */
+template<>
+struct StreamFaceValidator<boost::asio::ip::tcp, LocalFace>
+{
+  /** Check that local endpoint is loopback
+   *
+   *  @throws Face::Error if validation failed
+   */
+  static void
+  validateSocket(boost::asio::ip::tcp::socket& socket)
+  {
+    if (!socket.local_endpoint().address().is_loopback() ||
+        !socket.remote_endpoint().address().is_loopback())
+      {
+        throw Face::Error("TcpLocalFace can be created only on loopback interface");
+      }
+  }
+};
+
+} // namespace nfd
+
+#endif // NFD_DAEMON_FACE_TCP_FACE_HPP
diff --git a/NFD/daemon/face/tcp-factory.cpp b/NFD/daemon/face/tcp-factory.cpp
new file mode 100644
index 0000000..598a2c4
--- /dev/null
+++ b/NFD/daemon/face/tcp-factory.cpp
@@ -0,0 +1,191 @@
+/* -*- 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/>.
+ */
+
+#include "tcp-factory.hpp"
+#include "core/resolver.hpp"
+#include "core/logger.hpp"
+#include "core/network-interface.hpp"
+
+NFD_LOG_INIT("TcpFactory");
+
+namespace nfd {
+
+static const boost::asio::ip::address_v4 ALL_V4_ENDPOINT(
+  boost::asio::ip::address_v4::from_string("0.0.0.0"));
+
+static const boost::asio::ip::address_v6 ALL_V6_ENDPOINT(
+  boost::asio::ip::address_v6::from_string("::"));
+
+TcpFactory::TcpFactory(const std::string& defaultPort/* = "6363"*/)
+  : m_defaultPort(defaultPort)
+{
+}
+
+void
+TcpFactory::prohibitEndpoint(const tcp::Endpoint& endpoint)
+{
+  using namespace boost::asio::ip;
+
+  const address& address = endpoint.address();
+
+  if (address.is_v4() && address == ALL_V4_ENDPOINT)
+    {
+      prohibitAllIpv4Endpoints(endpoint.port());
+    }
+  else if (endpoint.address().is_v6() && address == ALL_V6_ENDPOINT)
+    {
+      prohibitAllIpv6Endpoints(endpoint.port());
+    }
+
+  NFD_LOG_TRACE("prohibiting TCP " << endpoint);
+
+  m_prohibitedEndpoints.insert(endpoint);
+}
+
+void
+TcpFactory::prohibitAllIpv4Endpoints(const uint16_t port)
+{
+  using namespace boost::asio::ip;
+
+  for (const NetworkInterfaceInfo& nic : listNetworkInterfaces()) {
+    for (const address_v4& addr : nic.ipv4Addresses) {
+      if (addr != ALL_V4_ENDPOINT) {
+        prohibitEndpoint(tcp::Endpoint(addr, port));
+      }
+    }
+  }
+}
+
+void
+TcpFactory::prohibitAllIpv6Endpoints(const uint16_t port)
+{
+  using namespace boost::asio::ip;
+
+  for (const NetworkInterfaceInfo& nic : listNetworkInterfaces()) {
+    for (const address_v6& addr : nic.ipv6Addresses) {
+      if (addr != ALL_V6_ENDPOINT) {
+        prohibitEndpoint(tcp::Endpoint(addr, port));
+      }
+    }
+  }
+}
+
+shared_ptr<TcpChannel>
+TcpFactory::createChannel(const tcp::Endpoint& endpoint)
+{
+  shared_ptr<TcpChannel> channel = findChannel(endpoint);
+  if (static_cast<bool>(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& localHost, const std::string& localPort)
+{
+  return createChannel(TcpResolver::syncResolve(localHost, localPort));
+}
+
+shared_ptr<TcpChannel>
+TcpFactory::findChannel(const tcp::Endpoint& localEndpoint)
+{
+  ChannelMap::iterator i = m_channels.find(localEndpoint);
+  if (i != m_channels.end())
+    return i->second;
+  else
+    return shared_ptr<TcpChannel>();
+}
+
+void
+TcpFactory::createFace(const FaceUri& uri,
+                       const FaceCreatedCallback& onCreated,
+                       const FaceConnectFailedCallback& onConnectFailed)
+{
+  resolver::AddressSelector addressSelector = resolver::AnyAddress();
+  if (uri.getScheme() == "tcp4")
+    addressSelector = resolver::Ipv4Address();
+  else if (uri.getScheme() == "tcp6")
+    addressSelector = resolver::Ipv6Address();
+
+  if (!uri.getPath().empty() && uri.getPath() != "/")
+    {
+      onConnectFailed("Invalid URI");
+    }
+
+  TcpResolver::asyncResolve(uri.getHost(),
+                            uri.getPort().empty() ? m_defaultPort : uri.getPort(),
+                            bind(&TcpFactory::continueCreateFaceAfterResolve, this, _1,
+                                 onCreated, onConnectFailed),
+                            onConnectFailed,
+                            addressSelector);
+}
+
+void
+TcpFactory::continueCreateFaceAfterResolve(const tcp::Endpoint& endpoint,
+                                           const FaceCreatedCallback& onCreated,
+                                           const FaceConnectFailedCallback& onConnectFailed)
+{
+  if (m_prohibitedEndpoints.find(endpoint) != m_prohibitedEndpoints.end())
+    {
+      onConnectFailed("Requested endpoint is prohibited "
+                      "(reserved by this NFD or disallowed by face management protocol)");
+      return;
+    }
+
+  // very simple logic for now
+
+  for (ChannelMap::iterator channel = m_channels.begin();
+       channel != m_channels.end();
+       ++channel)
+    {
+      if ((channel->first.address().is_v4() && endpoint.address().is_v4()) ||
+          (channel->first.address().is_v6() && endpoint.address().is_v6()))
+        {
+          channel->second->connect(endpoint, onCreated, onConnectFailed);
+          return;
+        }
+    }
+  onConnectFailed("No channels available to connect to "
+                  + boost::lexical_cast<std::string>(endpoint));
+}
+
+std::list<shared_ptr<const Channel> >
+TcpFactory::getChannels() const
+{
+  std::list<shared_ptr<const Channel> > channels;
+  for (ChannelMap::const_iterator i = m_channels.begin(); i != m_channels.end(); ++i)
+    {
+      channels.push_back(i->second);
+    }
+
+  return channels;
+}
+
+} // namespace nfd
diff --git a/NFD/daemon/face/tcp-factory.hpp b/NFD/daemon/face/tcp-factory.hpp
new file mode 100644
index 0000000..e2ebda5
--- /dev/null
+++ b/NFD/daemon/face/tcp-factory.hpp
@@ -0,0 +1,129 @@
+/* -*- 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_DAEMON_FACE_TCP_FACTORY_HPP
+#define NFD_DAEMON_FACE_TCP_FACTORY_HPP
+
+#include "protocol-factory.hpp"
+#include "tcp-channel.hpp"
+
+namespace nfd {
+
+class TcpFactory : public ProtocolFactory
+{
+public:
+  /**
+   * \brief Exception of TcpFactory
+   */
+  struct Error : public ProtocolFactory::Error
+  {
+    Error(const std::string& what) : ProtocolFactory::Error(what) {}
+  };
+
+  explicit
+  TcpFactory(const std::string& defaultPort = "6363");
+
+  /**
+   * \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.
+   *
+   * \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
+   */
+  shared_ptr<TcpChannel>
+  createChannel(const tcp::Endpoint& localEndpoint);
+
+  /**
+   * \brief Create TCP-based channel using specified host and port number
+   *
+   * This method will attempt to resolve the provided host and port numbers
+   * and will throw TcpFactory::Error when channel cannot be created.
+   *
+   * Note that this call will **BLOCK** until resolution is done or failed.
+   *
+   * \throws TcpFactory::Error or std::runtime_error
+   */
+  shared_ptr<TcpChannel>
+  createChannel(const std::string& localHost, const std::string& localPort);
+
+  // from Factory
+
+  virtual void
+  createFace(const FaceUri& uri,
+             const FaceCreatedCallback& onCreated,
+             const FaceConnectFailedCallback& onConnectFailed);
+
+  virtual std::list<shared_ptr<const Channel> >
+  getChannels() const;
+
+PUBLIC_WITH_TESTS_ELSE_PRIVATE:
+
+  void
+  prohibitEndpoint(const tcp::Endpoint& endpoint);
+
+  void
+  prohibitAllIpv4Endpoints(const uint16_t port);
+
+  void
+  prohibitAllIpv6Endpoints(const uint16_t port);
+
+  /**
+   * \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
+   *
+   * \throws never
+   */
+  shared_ptr<TcpChannel>
+  findChannel(const tcp::Endpoint& localEndpoint);
+
+  void
+  continueCreateFaceAfterResolve(const tcp::Endpoint& endpoint,
+                                 const FaceCreatedCallback& onCreated,
+                                 const FaceConnectFailedCallback& onConnectFailed);
+
+PUBLIC_WITH_TESTS_ELSE_PRIVATE:
+  typedef std::map< tcp::Endpoint, shared_ptr<TcpChannel> > ChannelMap;
+  ChannelMap m_channels;
+
+  std::string m_defaultPort;
+
+  std::set<tcp::Endpoint> m_prohibitedEndpoints;
+};
+
+} // namespace nfd
+
+#endif // NFD_DAEMON_FACE_TCP_FACTORY_HPP
diff --git a/NFD/daemon/face/udp-channel.cpp b/NFD/daemon/face/udp-channel.cpp
new file mode 100644
index 0000000..2ab4b77
--- /dev/null
+++ b/NFD/daemon/face/udp-channel.cpp
@@ -0,0 +1,263 @@
+/* -*- 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/>.
+ */
+
+#include "udp-channel.hpp"
+#include "core/global-io.hpp"
+
+namespace nfd {
+
+NFD_LOG_INIT("UdpChannel");
+
+using namespace boost::asio;
+
+UdpChannel::UdpChannel(const udp::Endpoint& localEndpoint,
+                       const time::seconds& timeout)
+  : m_localEndpoint(localEndpoint)
+  , m_isListening(false)
+  , m_idleFaceTimeout(timeout)
+{
+  /// \todo the reuse_address works as we want in Linux, but in other system could be different.
+  ///       We need to check this
+  ///       (SO_REUSEADDR doesn't behave uniformly in different OS)
+
+  m_socket = make_shared<ip::udp::socket>(ref(getGlobalIoService()));
+  m_socket->open(m_localEndpoint.protocol());
+  m_socket->set_option(boost::asio::ip::udp::socket::reuse_address(true));
+  if (m_localEndpoint.address().is_v6())
+    {
+      m_socket->set_option(ip::v6_only(true));
+    }
+
+  try {
+    m_socket->bind(m_localEndpoint);
+  }
+  catch (boost::system::system_error& e) {
+    //The bind failed, so the socket is useless now
+    m_socket->close();
+    throw Error("Failed to properly configure the socket. "
+                "UdpChannel creation aborted, check the address (" + std::string(e.what()) + ")");
+  }
+
+  this->setUri(FaceUri(localEndpoint));
+}
+
+void
+UdpChannel::listen(const FaceCreatedCallback& onFaceCreated,
+                   const ConnectFailedCallback& onListenFailed)
+{
+  if (m_isListening) {
+    throw Error("Listen already called on this channel");
+  }
+  m_isListening = true;
+
+  onFaceCreatedNewPeerCallback = onFaceCreated;
+  onConnectFailedNewPeerCallback = onListenFailed;
+
+  m_socket->async_receive_from(boost::asio::buffer(m_inputBuffer, ndn::MAX_NDN_PACKET_SIZE),
+                               m_newRemoteEndpoint,
+                               bind(&UdpChannel::newPeer, this,
+                                    boost::asio::placeholders::error,
+                                    boost::asio::placeholders::bytes_transferred));
+}
+
+
+void
+UdpChannel::connect(const udp::Endpoint& remoteEndpoint,
+                    const FaceCreatedCallback& onFaceCreated,
+                    const ConnectFailedCallback& onConnectFailed)
+{
+  ChannelFaceMap::iterator i = m_channelFaces.find(remoteEndpoint);
+  if (i != m_channelFaces.end()) {
+    i->second->setOnDemand(false);
+    onFaceCreated(i->second);
+    return;
+  }
+
+  //creating a new socket for the face that will be created soon
+  shared_ptr<ip::udp::socket> clientSocket =
+    make_shared<ip::udp::socket>(ref(getGlobalIoService()));
+
+  clientSocket->open(m_localEndpoint.protocol());
+  clientSocket->set_option(ip::udp::socket::reuse_address(true));
+
+  try {
+    clientSocket->bind(m_localEndpoint);
+    clientSocket->connect(remoteEndpoint); //@todo connect or async_connect
+    //(since there is no handshake the connect shouldn't block). If we go for
+    //async_connect, make sure that if in the meantime we receive a UDP pkt from
+    //that endpoint nothing bad happen (it's difficult, but it could happen)
+  }
+  catch (boost::system::system_error& e) {
+    clientSocket->close();
+    onConnectFailed("Failed to configure socket (" + std::string(e.what()) + ")");
+    return;
+  }
+  createFace(clientSocket, onFaceCreated, false);
+}
+
+void
+UdpChannel::connect(const std::string& remoteHost,
+                    const std::string& remotePort,
+                    const FaceCreatedCallback& onFaceCreated,
+                    const ConnectFailedCallback& onConnectFailed)
+{
+  ip::udp::resolver::query query(remoteHost, remotePort);
+  shared_ptr<ip::udp::resolver> resolver =
+    make_shared<ip::udp::resolver>(ref(getGlobalIoService()));
+
+  resolver->async_resolve(query,
+                          bind(&UdpChannel::handleEndpointResolution, this, _1, _2,
+                               onFaceCreated, onConnectFailed,
+                               resolver));
+}
+
+void
+UdpChannel::handleEndpointResolution(const boost::system::error_code& error,
+                                      ip::udp::resolver::iterator remoteEndpoint,
+                                      const FaceCreatedCallback& onFaceCreated,
+                                      const ConnectFailedCallback& onConnectFailed,
+                                      const shared_ptr<ip::udp::resolver>& resolver)
+{
+  if (error != 0 ||
+      remoteEndpoint == ip::udp::resolver::iterator())
+  {
+    if (error == boost::system::errc::operation_canceled) // when socket is closed by someone
+      return;
+
+    NFD_LOG_DEBUG("Remote endpoint hostname or port cannot be resolved: "
+                    << error.category().message(error.value()));
+
+    onConnectFailed("Remote endpoint hostname or port cannot be resolved: " +
+                      error.category().message(error.value()));
+      return;
+  }
+
+  connect(*remoteEndpoint, onFaceCreated, onConnectFailed);
+}
+
+size_t
+UdpChannel::size() const
+{
+  return m_channelFaces.size();
+}
+
+
+shared_ptr<UdpFace>
+UdpChannel::createFace(const shared_ptr<ip::udp::socket>& socket,
+                       const FaceCreatedCallback& onFaceCreated,
+                       bool isOnDemand)
+{
+  udp::Endpoint remoteEndpoint = socket->remote_endpoint();
+
+  shared_ptr<UdpFace> face;
+
+  ChannelFaceMap::iterator faceMapPos = m_channelFaces.find(remoteEndpoint);
+  if (faceMapPos == m_channelFaces.end())
+    {
+      face = make_shared<UdpFace>(socket, isOnDemand, m_idleFaceTimeout);
+      face->onFail += bind(&UdpChannel::afterFaceFailed, this, remoteEndpoint);
+
+      m_channelFaces[remoteEndpoint] = face;
+    }
+  else
+    {
+      // we've already created a a face for this endpoint, just reuse it
+      face = faceMapPos->second;
+
+      boost::system::error_code error;
+      socket->shutdown(ip::udp::socket::shutdown_both, error);
+      socket->close(error);
+    }
+
+  // Need to invoke the callback regardless of whether or not we have already created
+  // the face so that control responses and such can be sent.
+  onFaceCreated(face);
+
+  return face;
+}
+
+void
+UdpChannel::newPeer(const boost::system::error_code& error,
+                    size_t nBytesReceived)
+{
+  NFD_LOG_DEBUG("UdpChannel::newPeer from " << m_newRemoteEndpoint);
+
+  shared_ptr<UdpFace> face;
+
+  ChannelFaceMap::iterator i = m_channelFaces.find(m_newRemoteEndpoint);
+  if (i != m_channelFaces.end()) {
+    //The face already exists.
+    //Usually this shouldn't happen, because the channel creates a Udpface
+    //as soon as it receives a pkt from a new endpoint and then the
+    //traffic is dispatched by the kernel directly to the face.
+    //However, if the node receives multiple packets from the same endpoint
+    //"at the same time", while the channel is creating the face the kernel
+    //could dispatch the other pkts to the channel because the face is not yet
+    //ready. In this case, the channel has to pass the pkt to the face
+
+    NFD_LOG_DEBUG("The creation of the face for the remote endpoint "
+                  << m_newRemoteEndpoint
+                  << " is in progress");
+
+    face = i->second;
+  }
+  else {
+    shared_ptr<ip::udp::socket> clientSocket =
+      make_shared<ip::udp::socket>(ref(getGlobalIoService()));
+    clientSocket->open(m_localEndpoint.protocol());
+    clientSocket->set_option(ip::udp::socket::reuse_address(true));
+    clientSocket->bind(m_localEndpoint);
+    boost::system::error_code ec;
+    clientSocket->connect(m_newRemoteEndpoint, ec);
+    if (ec) {
+      NFD_LOG_WARN("Error while creating on-demand UDP face from " << m_newRemoteEndpoint << ": "
+                   << boost::system::system_error(ec).what());
+      return;
+    }
+
+    face = createFace(clientSocket,
+                      onFaceCreatedNewPeerCallback,
+                      true);
+  }
+
+  // dispatch the datagram to the face for processing
+  face->receiveDatagram(m_inputBuffer, nBytesReceived, error);
+
+  m_socket->async_receive_from(boost::asio::buffer(m_inputBuffer, ndn::MAX_NDN_PACKET_SIZE),
+                               m_newRemoteEndpoint,
+                               bind(&UdpChannel::newPeer, this,
+                                    boost::asio::placeholders::error,
+                                    boost::asio::placeholders::bytes_transferred));
+}
+
+
+void
+UdpChannel::afterFaceFailed(udp::Endpoint &endpoint)
+{
+  NFD_LOG_DEBUG("afterFaceFailed: " << endpoint);
+  m_channelFaces.erase(endpoint);
+}
+
+} // namespace nfd
diff --git a/NFD/daemon/face/udp-channel.hpp b/NFD/daemon/face/udp-channel.hpp
new file mode 100644
index 0000000..bcefd8b
--- /dev/null
+++ b/NFD/daemon/face/udp-channel.hpp
@@ -0,0 +1,179 @@
+/* -*- 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_DAEMON_FACE_UDP_CHANNEL_HPP
+#define NFD_DAEMON_FACE_UDP_CHANNEL_HPP
+
+#include "channel.hpp"
+#include "core/global-io.hpp"
+#include "udp-face.hpp"
+
+namespace nfd {
+
+namespace udp {
+typedef boost::asio::ip::udp::endpoint Endpoint;
+} // namespace udp
+
+/**
+ * \brief Class implementing UDP-based channel to create faces
+ *
+ *
+ */
+class UdpChannel : public Channel
+{
+public:
+  /**
+   * \brief Exception of UdpChannel
+   */
+  struct Error : public std::runtime_error
+  {
+    Error(const std::string& what) : runtime_error(what) {}
+  };
+
+  /**
+   * \brief Create UDP channel for the local endpoint
+   *
+   * 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
+   */
+  UdpChannel(const udp::Endpoint& localEndpoint,
+             const time::seconds& timeout);
+
+  /**
+   * \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 onAcceptFailed Callback to notify when channel fails
+   *
+   * 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
+   *
+   * \throws UdpChannel::Error if called multiple times
+   */
+  void
+  listen(const FaceCreatedCallback& onFaceCreated,
+         const ConnectFailedCallback& onAcceptFailed);
+
+  /**
+   * \brief Create a face by establishing connection to remote endpoint
+   *
+   * \throw UdpChannel::Error if bind or connect on the socket fail
+   */
+  void
+  connect(const udp::Endpoint& remoteEndpoint,
+          const FaceCreatedCallback& onFaceCreated,
+          const ConnectFailedCallback& onConnectFailed);
+  /**
+   * \brief Create a face by establishing connection to the specified
+   *        remote host and remote port
+   *
+   * This method will never block and will return immediately. All
+   * necessary hostname and port resolution and connection will happen
+   * in asynchronous mode.
+   *
+   * If connection cannot be established within specified timeout, it
+   * will be aborted.
+   */
+  void
+  connect(const std::string& remoteHost, const std::string& remotePort,
+          const FaceCreatedCallback& onFaceCreated,
+          const ConnectFailedCallback& onConnectFailed);
+
+  /**
+   * \brief Get number of faces in the channel
+   */
+  size_t
+  size() const;
+
+private:
+  shared_ptr<UdpFace>
+  createFace(const shared_ptr<boost::asio::ip::udp::socket>& socket,
+             const FaceCreatedCallback& onFaceCreated,
+             bool isOnDemand);
+  void
+  afterFaceFailed(udp::Endpoint& endpoint);
+
+  /**
+   * \brief The UdpChannel has received a new pkt from a remote endpoint not yet
+   *        associated with any UdpFace
+   */
+  void
+  newPeer(const boost::system::error_code& error, size_t nBytesReceived);
+
+  void
+  handleEndpointResolution(const boost::system::error_code& error,
+                           boost::asio::ip::udp::resolver::iterator remoteEndpoint,
+                           const FaceCreatedCallback& onFaceCreated,
+                           const ConnectFailedCallback& onConnectFailed,
+                           const shared_ptr<boost::asio::ip::udp::resolver>& resolver);
+
+private:
+  udp::Endpoint m_localEndpoint;
+
+  /**
+   * \brief Endpoint used to store the information about the last new remote endpoint
+   */
+  udp::Endpoint m_newRemoteEndpoint;
+
+  /**
+   * Callbacks for face creation.
+   * New communications are detected using async_receive_from.
+   * Its handler has a fixed signature. No space for the face callback
+   */
+  FaceCreatedCallback onFaceCreatedNewPeerCallback;
+
+  // @todo remove the onConnectFailedNewPeerCallback if it remains unused
+  ConnectFailedCallback onConnectFailedNewPeerCallback;
+
+  /**
+   * \brief Socket used to "accept" new communication
+   **/
+  shared_ptr<boost::asio::ip::udp::socket> m_socket;
+
+  uint8_t m_inputBuffer[ndn::MAX_NDN_PACKET_SIZE];
+
+  typedef std::map< udp::Endpoint, shared_ptr<UdpFace> > ChannelFaceMap;
+  ChannelFaceMap m_channelFaces;
+
+  /**
+   * \brief If true, it means the function listen has already been called
+   */
+  bool m_isListening;
+
+  /**
+   * \brief every time m_idleFaceTimeout expires all the idle (and on-demand)
+   *        faces will be removed
+   */
+  time::seconds m_idleFaceTimeout;
+};
+
+} // namespace nfd
+
+#endif // NFD_DAEMON_FACE_UDP_CHANNEL_HPP
diff --git a/NFD/daemon/face/udp-face.cpp b/NFD/daemon/face/udp-face.cpp
new file mode 100644
index 0000000..81e915c
--- /dev/null
+++ b/NFD/daemon/face/udp-face.cpp
@@ -0,0 +1,127 @@
+/* -*- 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/>.
+ */
+
+#include "udp-face.hpp"
+
+#ifdef __linux__
+#include <netinet/in.h> // for IP_MTU_DISCOVER and IP_PMTUDISC_DONT
+#include <sys/socket.h> // for setsockopt()
+#endif
+
+namespace nfd {
+
+NFD_LOG_INCLASS_TEMPLATE_SPECIALIZATION_DEFINE(DatagramFace, UdpFace::protocol, "UdpFace");
+
+UdpFace::UdpFace(const shared_ptr<UdpFace::protocol::socket>& socket,
+                 bool isOnDemand,
+                 const time::seconds& idleTimeout)
+  : DatagramFace<protocol>(FaceUri(socket->remote_endpoint()),
+                           FaceUri(socket->local_endpoint()),
+                           socket, isOnDemand)
+  , m_idleTimeout(idleTimeout)
+  , m_lastIdleCheck(time::steady_clock::now())
+{
+#ifdef __linux__
+  //
+  // By default, Linux does path MTU discovery on IPv4 sockets,
+  // and sets the DF (Don't Fragment) flag on datagrams smaller
+  // than the interface MTU. However this does not work for us,
+  // because we cannot properly respond to ICMP "packet too big"
+  // messages by fragmenting the packet at the application level,
+  // since we want to rely on IP for fragmentation and reassembly.
+  //
+  // Therefore, we disable PMTU discovery, which prevents the kernel
+  // from setting the DF flag on outgoing datagrams, and thus allows
+  // routers along the path to perform fragmentation as needed.
+  //
+  const int value = IP_PMTUDISC_DONT;
+  if (::setsockopt(socket->native_handle(), IPPROTO_IP,
+                   IP_MTU_DISCOVER, &value, sizeof(value)) < 0)
+    {
+      NFD_LOG_WARN("[id:" << this->getId()
+                   << ",uri:" << this->getRemoteUri()
+                   << "] Failed to disable path MTU discovery");
+    }
+#endif
+
+  if (isOnDemand && m_idleTimeout > time::seconds::zero()) {
+    m_closeIfIdleEvent = scheduler::schedule(m_idleTimeout,
+                                             bind(&UdpFace::closeIfIdle, this));
+  }
+}
+
+UdpFace::~UdpFace()
+{
+  scheduler::cancel(m_closeIfIdleEvent);
+}
+
+ndn::nfd::FaceStatus
+UdpFace::getFaceStatus() const
+{
+  ndn::nfd::FaceStatus status = Face::getFaceStatus();
+  if (isOnDemand()) {
+    time::milliseconds left = m_idleTimeout - time::duration_cast<time::milliseconds>(
+      time::steady_clock::now() - m_lastIdleCheck);
+    if (left < time::milliseconds::zero())
+      left = time::milliseconds::zero();
+
+    if (hasBeenUsedRecently()) {
+      status.setExpirationPeriod(left + m_idleTimeout);
+    }
+    else {
+      status.setExpirationPeriod(left);
+    }
+  }
+  return status;
+}
+
+void
+UdpFace::closeIfIdle()
+{
+  // Face can be switched from on-demand to non-on-demand mode
+  // (non-on-demand -> on-demand transition is not allowed)
+  if (isOnDemand()) {
+    if (!hasBeenUsedRecently()) {
+      // face has been idle since the last time closeIfIdle
+      // has been called. Going to close it
+      NFD_LOG_DEBUG("Found idle face id: " << getId());
+
+      NFD_LOG_INFO("[id:" << this->getId()
+                   << ",uri:" << this->getRemoteUri()
+                   << "] Idle for more than " << m_idleTimeout << ", closing");
+      close();
+    }
+    else {
+      resetRecentUsage();
+
+      m_lastIdleCheck = time::steady_clock::now();
+      m_closeIfIdleEvent = scheduler::schedule(m_idleTimeout,
+                                               bind(&UdpFace::closeIfIdle, this));
+    }
+  }
+  // else do nothing and do not reschedule the event
+}
+
+} // namespace nfd
diff --git a/NFD/daemon/face/udp-face.hpp b/NFD/daemon/face/udp-face.hpp
new file mode 100644
index 0000000..49c374b
--- /dev/null
+++ b/NFD/daemon/face/udp-face.hpp
@@ -0,0 +1,62 @@
+/* -*- 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
+ *
+ * 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_UDP_FACE_HPP
+#define NFD_DAEMON_FACE_UDP_FACE_HPP
+
+#include "datagram-face.hpp"
+#include "core/scheduler.hpp"
+
+namespace nfd {
+
+/**
+ * \brief Implementation of Face abstraction that uses UDP
+ *        as underlying transport mechanism
+ */
+class UdpFace : public DatagramFace<boost::asio::ip::udp>
+{
+public:
+  UdpFace(const shared_ptr<protocol::socket>& socket,
+          bool isOnDemand,
+          const time::seconds& idleTimeout);
+
+  virtual
+  ~UdpFace();
+
+  virtual ndn::nfd::FaceStatus
+  getFaceStatus() const;
+
+private:
+  void
+  closeIfIdle();
+
+private:
+  EventId m_closeIfIdleEvent;
+  time::seconds m_idleTimeout;
+  time::steady_clock::TimePoint m_lastIdleCheck;
+};
+
+} // namespace nfd
+
+#endif // NFD_DAEMON_FACE_UDP_FACE_HPP
diff --git a/NFD/daemon/face/udp-factory.cpp b/NFD/daemon/face/udp-factory.cpp
new file mode 100644
index 0000000..458d4fb
--- /dev/null
+++ b/NFD/daemon/face/udp-factory.cpp
@@ -0,0 +1,361 @@
+/* -*- 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/>.
+ */
+
+#include "udp-factory.hpp"
+#include "core/global-io.hpp"
+#include "core/resolver.hpp"
+#include "core/network-interface.hpp"
+
+#if defined(__linux__)
+#include <sys/socket.h>
+#endif
+
+namespace nfd {
+
+using namespace boost::asio;
+
+NFD_LOG_INIT("UdpFactory");
+
+static const boost::asio::ip::address_v4 ALL_V4_ENDPOINT(
+  boost::asio::ip::address_v4::from_string("0.0.0.0"));
+
+static const boost::asio::ip::address_v6 ALL_V6_ENDPOINT(
+  boost::asio::ip::address_v6::from_string("::"));
+
+UdpFactory::UdpFactory(const std::string& defaultPort/* = "6363"*/)
+  : m_defaultPort(defaultPort)
+{
+}
+
+void
+UdpFactory::prohibitEndpoint(const udp::Endpoint& endpoint)
+{
+  using namespace boost::asio::ip;
+
+  const address& address = endpoint.address();
+
+  if (address.is_v4() && address == ALL_V4_ENDPOINT)
+    {
+      prohibitAllIpv4Endpoints(endpoint.port());
+    }
+  else if (endpoint.address().is_v6() && address == ALL_V6_ENDPOINT)
+    {
+      prohibitAllIpv6Endpoints(endpoint.port());
+    }
+
+  NFD_LOG_TRACE("prohibiting UDP " << endpoint);
+
+  m_prohibitedEndpoints.insert(endpoint);
+}
+
+void
+UdpFactory::prohibitAllIpv4Endpoints(const uint16_t port)
+{
+  using namespace boost::asio::ip;
+
+  for (const NetworkInterfaceInfo& nic : listNetworkInterfaces()) {
+    for (const address_v4& addr : nic.ipv4Addresses) {
+      if (addr != ALL_V4_ENDPOINT) {
+        prohibitEndpoint(udp::Endpoint(addr, port));
+      }
+    }
+
+    if (nic.isBroadcastCapable() && nic.broadcastAddress != ALL_V4_ENDPOINT)
+    {
+      NFD_LOG_TRACE("prohibiting broadcast address: " << nic.broadcastAddress.to_string());
+      prohibitEndpoint(udp::Endpoint(nic.broadcastAddress, port));
+    }
+  }
+
+  prohibitEndpoint(udp::Endpoint(address::from_string("255.255.255.255"), port));
+}
+
+void
+UdpFactory::prohibitAllIpv6Endpoints(const uint16_t port)
+{
+  using namespace boost::asio::ip;
+
+  for (const NetworkInterfaceInfo& nic : listNetworkInterfaces()) {
+    for (const address_v6& addr : nic.ipv6Addresses) {
+      if (addr != ALL_V6_ENDPOINT) {
+        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);
+
+  shared_ptr<UdpChannel> channel = findChannel(endpoint);
+  if (static_cast<bool>(channel))
+    return channel;
+
+  //checking if the endpoint is already in use for multicast face
+  shared_ptr<MulticastUdpFace> multicast = findMulticastFace(endpoint);
+  if (static_cast<bool>(multicast))
+    throw Error("Cannot create the requested UDP unicast channel, local "
+                "endpoint is already allocated for a UDP multicast face");
+
+  if (endpoint.address().is_multicast()) {
+    throw Error("This method is only for unicast channel. The provided "
+                "endpoint is multicast. Use createMulticastFace to "
+                "create a multicast face");
+  }
+
+  channel = make_shared<UdpChannel>(endpoint, timeout);
+  m_channels[endpoint] = channel;
+  prohibitEndpoint(endpoint);
+
+  return channel;
+}
+
+shared_ptr<UdpChannel>
+UdpFactory::createChannel(const std::string& localHost,
+                          const std::string& localPort,
+                          const time::seconds& timeout)
+{
+  return createChannel(UdpResolver::syncResolve(localHost, localPort), timeout);
+}
+
+shared_ptr<MulticastUdpFace>
+UdpFactory::createMulticastFace(const udp::Endpoint& localEndpoint,
+                                const udp::Endpoint& multicastEndpoint,
+                                const std::string& networkInterfaceName /* "" */)
+{
+  //checking if the local and multicast endpoint are already in use for a multicast face
+  shared_ptr<MulticastUdpFace> multicastFace = findMulticastFace(localEndpoint);
+  if (static_cast<bool>(multicastFace)) {
+    if (multicastFace->getMulticastGroup() == multicastEndpoint)
+      return multicastFace;
+    else
+      throw 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 an unicast channel
+  shared_ptr<UdpChannel> unicast = findChannel(localEndpoint);
+  if (static_cast<bool>(unicast)) {
+    throw 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()) {
+    throw 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()) {
+    throw Error("IPv6 multicast is not supported yet. Please provide an IPv4 address");
+  }
+
+  if (localEndpoint.port() != multicastEndpoint.port()) {
+    throw Error("Cannot create the requested UDP multicast face, "
+                "both endpoints should have the same port number. ");
+  }
+
+  if (!multicastEndpoint.address().is_multicast()) {
+    throw Error("Cannot create the requested UDP multicast face, "
+                "the multicast group given as input is not a multicast address");
+  }
+
+  shared_ptr<ip::udp::socket> receiveSocket =
+    make_shared<ip::udp::socket>(ref(getGlobalIoService()));
+
+  shared_ptr<ip::udp::socket> sendSocket =
+    make_shared<ip::udp::socket>(ref(getGlobalIoService()));
+
+  receiveSocket->open(multicastEndpoint.protocol());
+  receiveSocket->set_option(ip::udp::socket::reuse_address(true));
+
+  sendSocket->open(multicastEndpoint.protocol());
+  sendSocket->set_option(ip::udp::socket::reuse_address(true));
+  sendSocket->set_option(ip::multicast::enable_loopback(false));
+
+  try {
+    sendSocket->bind(udp::Endpoint(ip::address_v4::any(), multicastEndpoint.port()));
+    receiveSocket->bind(multicastEndpoint);
+
+    if (localEndpoint.address() != ip::address::from_string("0.0.0.0")) {
+      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()));
+  }
+  catch (boost::system::system_error& e) {
+    std::stringstream msg;
+    msg << "Failed to properly configure the socket, check the address (" << e.what() << ")";
+    throw Error(msg.str());
+  }
+
+#if defined(__linux__)
+  //On linux system, if there are more than one MulticastUdpFace for the same multicast group but
+  //bound on different network interfaces, the socket has to be bound with the specific interface
+  //using SO_BINDTODEVICE, otherwise the face will receive packets also from other interfaces.
+  //Without SO_BINDTODEVICE every MulticastUdpFace that have joined the same multicast group
+  //on different interfaces will receive the same packet.
+  //This applies only on linux, for OS X the ip::multicast::join_group 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) == -1){
+      throw Error("Cannot bind multicast face to " + networkInterfaceName
+                  + " make sure you have CAP_NET_RAW capability" );
+    }
+  }
+
+#endif
+
+  multicastFace = make_shared<MulticastUdpFace>(receiveSocket, sendSocket,
+                                                localEndpoint, multicastEndpoint);
+  multicastFace->onFail += bind(&UdpFactory::afterFaceFailed, this, localEndpoint);
+
+  m_multicastFaces[localEndpoint] = multicastFace;
+
+  return multicastFace;
+}
+
+shared_ptr<MulticastUdpFace>
+UdpFactory::createMulticastFace(const std::string& localIp,
+                                const std::string& multicastIp,
+                                const std::string& multicastPort,
+                                const std::string& networkInterfaceName /* "" */)
+{
+
+  return createMulticastFace(UdpResolver::syncResolve(localIp,
+                                                      multicastPort),
+                             UdpResolver::syncResolve(multicastIp,
+                                                      multicastPort),
+                             networkInterfaceName);
+}
+
+void
+UdpFactory::createFace(const FaceUri& uri,
+                       const FaceCreatedCallback& onCreated,
+                       const FaceConnectFailedCallback& onConnectFailed)
+{
+  resolver::AddressSelector addressSelector = resolver::AnyAddress();
+  if (uri.getScheme() == "udp4")
+    addressSelector = resolver::Ipv4Address();
+  else if (uri.getScheme() == "udp6")
+    addressSelector = resolver::Ipv6Address();
+
+  if (!uri.getPath().empty() && uri.getPath() != "/")
+    {
+      onConnectFailed("Invalid URI");
+    }
+
+  UdpResolver::asyncResolve(uri.getHost(),
+                            uri.getPort().empty() ? m_defaultPort : uri.getPort(),
+                            bind(&UdpFactory::continueCreateFaceAfterResolve, this, _1,
+                                 onCreated, onConnectFailed),
+                            onConnectFailed,
+                            addressSelector);
+
+}
+
+void
+UdpFactory::continueCreateFaceAfterResolve(const udp::Endpoint& endpoint,
+                                           const FaceCreatedCallback& onCreated,
+                                           const FaceConnectFailedCallback& onConnectFailed)
+{
+  if (endpoint.address().is_multicast()) {
+    onConnectFailed("The provided address is multicast. Please use createMulticastFace method");
+    return;
+  }
+
+  if (m_prohibitedEndpoints.find(endpoint) != m_prohibitedEndpoints.end())
+    {
+      onConnectFailed("Requested endpoint is prohibited "
+                      "(reserved by this NFD or disallowed by face management protocol)");
+      return;
+    }
+
+  // very simple logic for now
+
+  for (ChannelMap::iterator channel = m_channels.begin();
+       channel != m_channels.end();
+       ++channel)
+  {
+    if ((channel->first.address().is_v4() && endpoint.address().is_v4()) ||
+        (channel->first.address().is_v6() && endpoint.address().is_v6()))
+    {
+      channel->second->connect(endpoint, onCreated, onConnectFailed);
+      return;
+    }
+  }
+  onConnectFailed("No channels available to connect to " +
+                  boost::lexical_cast<std::string>(endpoint));
+}
+
+shared_ptr<UdpChannel>
+UdpFactory::findChannel(const udp::Endpoint& localEndpoint)
+{
+  ChannelMap::iterator i = m_channels.find(localEndpoint);
+  if (i != m_channels.end())
+    return i->second;
+  else
+    return shared_ptr<UdpChannel>();
+}
+
+shared_ptr<MulticastUdpFace>
+UdpFactory::findMulticastFace(const udp::Endpoint& localEndpoint)
+{
+  MulticastFaceMap::iterator i = m_multicastFaces.find(localEndpoint);
+  if (i != m_multicastFaces.end())
+    return i->second;
+  else
+    return shared_ptr<MulticastUdpFace>();
+}
+
+void
+UdpFactory::afterFaceFailed(udp::Endpoint& endpoint)
+{
+  NFD_LOG_DEBUG("afterFaceFailed: " << endpoint);
+  m_multicastFaces.erase(endpoint);
+}
+
+std::list<shared_ptr<const Channel> >
+UdpFactory::getChannels() const
+{
+  std::list<shared_ptr<const Channel> > channels;
+  for (ChannelMap::const_iterator i = m_channels.begin(); i != m_channels.end(); ++i)
+    {
+      channels.push_back(i->second);
+    }
+
+  return channels;
+}
+
+
+
+} // namespace nfd
diff --git a/NFD/daemon/face/udp-factory.hpp b/NFD/daemon/face/udp-factory.hpp
new file mode 100644
index 0000000..1f7f7de
--- /dev/null
+++ b/NFD/daemon/face/udp-factory.hpp
@@ -0,0 +1,225 @@
+/* -*- 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_DAEMON_FACE_UDP_FACTORY_HPP
+#define NFD_DAEMON_FACE_UDP_FACTORY_HPP
+
+#include "protocol-factory.hpp"
+#include "udp-channel.hpp"
+#include "multicast-udp-face.hpp"
+
+
+namespace nfd {
+
+// @todo The multicast support for ipv6 must be implemented
+
+class UdpFactory : public ProtocolFactory
+{
+public:
+  /**
+   * \brief Exception of UdpFactory
+   */
+  class Error : public ProtocolFactory::Error
+  {
+  public:
+    explicit
+    Error(const std::string& what)
+      : ProtocolFactory::Error(what)
+    {
+    }
+  };
+
+  typedef std::map< udp::Endpoint, shared_ptr<MulticastUdpFace> > MulticastFaceMap;
+
+  explicit
+  UdpFactory(const std::string& defaultPort = "6363");
+
+  /**
+   * \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 a multicast face is already active on the same local endpoint,
+   * 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
+   */
+  shared_ptr<UdpChannel>
+  createChannel(const udp::Endpoint& localEndpoint,
+                const time::seconds& timeout = time::seconds(600));
+
+  /**
+   * \brief Create UDP-based channel using specified host and port number
+   *
+   * This method will attempt to resolve the provided host and port numbers
+   * and will throw UdpFactory::Error when channel cannot be created.
+   *
+   * Note that this call will **BLOCK** until resolution is done or failed.
+   *
+   * 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& localHost,
+                const std::string& localPort,
+                const time::seconds& timeout = time::seconds(600));
+
+  /**
+   * \brief Create MulticastUdpFace using udp::Endpoint
+   *
+   * udp::Endpoint is really an alias for boost::asio::ip::udp::endpoint.
+   *
+   * The face will join the 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 an unicast face is already active on the same local NIC and port, the
+   * creation fails and an exception is thrown
+   *
+   * \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
+   *
+   *
+   * \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
+   */
+  shared_ptr<MulticastUdpFace>
+  createMulticastFace(const udp::Endpoint& localEndpoint,
+                      const udp::Endpoint& multicastEndpoint,
+                      const std::string& networkInterfaceName = "");
+
+  shared_ptr<MulticastUdpFace>
+  createMulticastFace(const std::string& localIp,
+                      const std::string& multicastIp,
+                      const std::string& multicastPort,
+                      const std::string& networkInterfaceName = "");
+
+  // from Factory
+  virtual void
+  createFace(const FaceUri& uri,
+             const FaceCreatedCallback& onCreated,
+             const FaceConnectFailedCallback& onConnectFailed);
+
+  virtual std::list<shared_ptr<const Channel> >
+  getChannels() const;
+
+  /**
+   * \brief Get map of configured multicast faces
+   */
+  const MulticastFaceMap&
+  getMulticastFaces() const;
+
+PUBLIC_WITH_TESTS_ELSE_PRIVATE:
+
+  void
+  prohibitEndpoint(const udp::Endpoint& endpoint);
+
+  void
+  prohibitAllIpv4Endpoints(const uint16_t port);
+
+  void
+  prohibitAllIpv6Endpoints(const uint16_t port);
+
+  void
+  afterFaceFailed(udp::Endpoint& endpoint);
+
+  /**
+   * \brief Look up UdpChannel using specified local endpoint
+   *
+   * \returns shared pointer to the existing UdpChannel object
+   *          or empty shared pointer when such channel does not exist
+   *
+   * \throws never
+   */
+  shared_ptr<UdpChannel>
+  findChannel(const udp::Endpoint& localEndpoint);
+
+
+  /**
+   * \brief Look up multicast UdpFace using specified local endpoint
+   *
+   * \returns shared pointer to the existing multicast MulticastUdpFace object
+   *          or empty shared pointer when such face does not exist
+   *
+   * \throws never
+   */
+  shared_ptr<MulticastUdpFace>
+  findMulticastFace(const udp::Endpoint& localEndpoint);
+
+  void
+  continueCreateFaceAfterResolve(const udp::Endpoint& endpoint,
+                                 const FaceCreatedCallback& onCreated,
+                                 const FaceConnectFailedCallback& onConnectFailed);
+
+PUBLIC_WITH_TESTS_ELSE_PRIVATE:
+  typedef std::map< udp::Endpoint, shared_ptr<UdpChannel> > ChannelMap;
+
+  ChannelMap m_channels;
+  MulticastFaceMap m_multicastFaces;
+
+  std::string m_defaultPort;
+  std::set<udp::Endpoint> m_prohibitedEndpoints;
+};
+
+
+inline const UdpFactory::MulticastFaceMap&
+UdpFactory::getMulticastFaces() const
+{
+  return m_multicastFaces;
+}
+
+
+} // namespace nfd
+
+#endif // NFD_DAEMON_FACE_UDP_FACTORY_HPP
diff --git a/NFD/daemon/face/unix-stream-channel.cpp b/NFD/daemon/face/unix-stream-channel.cpp
new file mode 100644
index 0000000..d64f333
--- /dev/null
+++ b/NFD/daemon/face/unix-stream-channel.cpp
@@ -0,0 +1,149 @@
+/* -*- 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
+ *
+ * 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 "unix-stream-channel.hpp"
+#include "core/global-io.hpp"
+
+#include <boost/filesystem.hpp>
+#include <sys/stat.h> // for chmod()
+
+namespace nfd {
+
+NFD_LOG_INIT("UnixStreamChannel");
+
+using namespace boost::asio::local;
+
+UnixStreamChannel::UnixStreamChannel(const unix_stream::Endpoint& endpoint)
+  : m_endpoint(endpoint)
+  , m_isListening(false)
+{
+  setUri(FaceUri(endpoint));
+}
+
+UnixStreamChannel::~UnixStreamChannel()
+{
+  if (m_isListening)
+    {
+      // use the non-throwing variants during destruction
+      // and ignore any errors
+      boost::system::error_code error;
+      m_acceptor->close(error);
+      NFD_LOG_TRACE("[" << m_endpoint << "] Removing socket file");
+      boost::filesystem::remove(m_endpoint.path(), error);
+    }
+}
+
+void
+UnixStreamChannel::listen(const FaceCreatedCallback& onFaceCreated,
+                          const ConnectFailedCallback& onAcceptFailed,
+                          int backlog/* = acceptor::max_connections*/)
+{
+  if (m_isListening) {
+    NFD_LOG_WARN("[" << m_endpoint << "] Already listening");
+    return;
+  }
+
+  namespace fs = boost::filesystem;
+
+  fs::path socketPath(m_endpoint.path());
+  fs::file_type type = fs::symlink_status(socketPath).type();
+
+  if (type == fs::socket_file)
+    {
+      boost::system::error_code error;
+      stream_protocol::socket socket(getGlobalIoService());
+      socket.connect(m_endpoint, error);
+      NFD_LOG_TRACE("[" << m_endpoint << "] connect() on existing socket file returned: "
+                    + error.message());
+      if (!error)
+        {
+          // someone answered, leave the socket alone
+          throw Error("Socket file at " + m_endpoint.path()
+                      + " belongs to another NFD process");
+        }
+      else if (error == boost::system::errc::connection_refused ||
+               error == boost::system::errc::timed_out)
+        {
+          // no one is listening on the remote side,
+          // we can safely remove the socket file
+          NFD_LOG_INFO("[" << m_endpoint << "] Removing stale socket file");
+          fs::remove(socketPath);
+        }
+    }
+  else if (type != fs::file_not_found)
+    {
+      throw Error(m_endpoint.path() + " already exists and is not a socket file");
+    }
+
+  m_acceptor = make_shared<stream_protocol::acceptor>(ref(getGlobalIoService()));
+  m_acceptor->open();
+  m_acceptor->bind(m_endpoint);
+  m_acceptor->listen(backlog);
+  m_isListening = true;
+
+  if (::chmod(m_endpoint.path().c_str(), 0666) < 0)
+    {
+      throw Error("Failed to chmod() socket file at " + m_endpoint.path());
+    }
+
+  shared_ptr<stream_protocol::socket> clientSocket =
+    make_shared<stream_protocol::socket>(ref(getGlobalIoService()));
+
+  m_acceptor->async_accept(*clientSocket,
+                           bind(&UnixStreamChannel::handleSuccessfulAccept, this,
+                                boost::asio::placeholders::error, clientSocket,
+                                onFaceCreated, onAcceptFailed));
+}
+
+void
+UnixStreamChannel::handleSuccessfulAccept(const boost::system::error_code& error,
+                                          const shared_ptr<stream_protocol::socket>& socket,
+                                          const FaceCreatedCallback& onFaceCreated,
+                                          const ConnectFailedCallback& onAcceptFailed)
+{
+  if (error) {
+    if (error == boost::system::errc::operation_canceled) // when socket is closed by someone
+      return;
+
+    NFD_LOG_DEBUG("[" << m_endpoint << "] Connection failed: " << error.message());
+    onAcceptFailed("Connection failed: " + error.message());
+    return;
+  }
+
+  NFD_LOG_DEBUG("[" << m_endpoint << "] << Incoming connection");
+
+  shared_ptr<stream_protocol::socket> clientSocket =
+    make_shared<stream_protocol::socket>(ref(getGlobalIoService()));
+
+  // prepare accepting the next connection
+  m_acceptor->async_accept(*clientSocket,
+                           bind(&UnixStreamChannel::handleSuccessfulAccept, this,
+                                boost::asio::placeholders::error, clientSocket,
+                                onFaceCreated, onAcceptFailed));
+
+  shared_ptr<UnixStreamFace> face = make_shared<UnixStreamFace>(socket);
+  onFaceCreated(face);
+}
+
+} // namespace nfd
diff --git a/NFD/daemon/face/unix-stream-channel.hpp b/NFD/daemon/face/unix-stream-channel.hpp
new file mode 100644
index 0000000..c923c39
--- /dev/null
+++ b/NFD/daemon/face/unix-stream-channel.hpp
@@ -0,0 +1,95 @@
+/* -*- 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
+ *
+ * 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_UNIX_STREAM_CHANNEL_HPP
+#define NFD_DAEMON_FACE_UNIX_STREAM_CHANNEL_HPP
+
+#include "channel.hpp"
+#include "unix-stream-face.hpp"
+
+namespace nfd {
+
+namespace unix_stream {
+typedef boost::asio::local::stream_protocol::endpoint Endpoint;
+} // namespace unix_stream
+
+/**
+ * \brief Class implementing a local channel to create faces
+ *
+ * Channel can create faces as a response to incoming IPC connections
+ * (UnixStreamChannel::listen needs to be called for that to work).
+ */
+class UnixStreamChannel : public Channel
+{
+public:
+  /**
+   * \brief UnixStreamChannel-related error
+   */
+  struct Error : public std::runtime_error
+  {
+    Error(const std::string& what) : std::runtime_error(what) {}
+  };
+
+  /**
+   * \brief Create UnixStream channel for the specified endpoint
+   *
+   * To enable creation of faces upon incoming connections, one
+   * needs to explicitly call UnixStreamChannel::listen method.
+   */
+  explicit
+  UnixStreamChannel(const unix_stream::Endpoint& endpoint);
+
+  virtual
+  ~UnixStreamChannel();
+
+  /**
+   * \brief Enable listening on the local endpoint, accept connections,
+   *        and create faces when a connection is made
+   * \param onFaceCreated  Callback to notify successful creation of the face
+   * \param onAcceptFailed Callback to notify when channel fails (accept call
+   *                       returns an error)
+   * \param backlog        The maximum length of the queue of pending incoming
+   *                       connections
+   */
+  void
+  listen(const FaceCreatedCallback& onFaceCreated,
+         const ConnectFailedCallback& onAcceptFailed,
+         int backlog = boost::asio::local::stream_protocol::acceptor::max_connections);
+
+private:
+  void
+  handleSuccessfulAccept(const boost::system::error_code& error,
+                         const shared_ptr<boost::asio::local::stream_protocol::socket>& socket,
+                         const FaceCreatedCallback& onFaceCreated,
+                         const ConnectFailedCallback& onConnectFailed);
+
+private:
+  unix_stream::Endpoint m_endpoint;
+  shared_ptr<boost::asio::local::stream_protocol::acceptor> m_acceptor;
+  bool m_isListening;
+};
+
+} // namespace nfd
+
+#endif // NFD_DAEMON_FACE_UNIX_STREAM_CHANNEL_HPP
diff --git a/NFD/daemon/face/unix-stream-face.cpp b/NFD/daemon/face/unix-stream-face.cpp
new file mode 100644
index 0000000..93ff30e
--- /dev/null
+++ b/NFD/daemon/face/unix-stream-face.cpp
@@ -0,0 +1,46 @@
+/* -*- 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
+ *
+ * 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 "unix-stream-face.hpp"
+
+namespace nfd {
+
+// The whole purpose of this file is to specialize the logger,
+// otherwise, everything could be put into the header file.
+
+NFD_LOG_INCLASS_2TEMPLATE_SPECIALIZATION_DEFINE(StreamFace,
+                                                UnixStreamFace::protocol, LocalFace,
+                                                "UnixStreamFace");
+
+BOOST_STATIC_ASSERT((boost::is_same<UnixStreamFace::protocol::socket::native_handle_type,
+                     int>::value));
+
+UnixStreamFace::UnixStreamFace(const shared_ptr<UnixStreamFace::protocol::socket>& socket)
+  : StreamFace<protocol, LocalFace>(FaceUri::fromFd(socket->native_handle()),
+                                    FaceUri(socket->local_endpoint()),
+                                    socket, true)
+{
+}
+
+} // namespace nfd
diff --git a/NFD/daemon/face/unix-stream-face.hpp b/NFD/daemon/face/unix-stream-face.hpp
new file mode 100644
index 0000000..51482c4
--- /dev/null
+++ b/NFD/daemon/face/unix-stream-face.hpp
@@ -0,0 +1,49 @@
+/* -*- 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
+ *
+ * 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_UNIX_STREAM_FACE_HPP
+#define NFD_DAEMON_FACE_UNIX_STREAM_FACE_HPP
+
+#include "stream-face.hpp"
+
+#ifndef HAVE_UNIX_SOCKETS
+#error "Cannot include this file when UNIX sockets are not available"
+#endif
+
+namespace nfd {
+
+/**
+ * \brief Implementation of Face abstraction that uses stream-oriented
+ *        Unix domain sockets as underlying transport mechanism
+ */
+class UnixStreamFace : public StreamFace<boost::asio::local::stream_protocol, LocalFace>
+{
+public:
+  explicit
+  UnixStreamFace(const shared_ptr<protocol::socket>& socket);
+};
+
+} // namespace nfd
+
+#endif // NFD_DAEMON_FACE_UNIX_STREAM_FACE_HPP
diff --git a/NFD/daemon/face/unix-stream-factory.cpp b/NFD/daemon/face/unix-stream-factory.cpp
new file mode 100644
index 0000000..d7513f6
--- /dev/null
+++ b/NFD/daemon/face/unix-stream-factory.cpp
@@ -0,0 +1,78 @@
+/* -*- 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/>.
+ */
+
+#include "unix-stream-factory.hpp"
+
+#include <boost/filesystem.hpp> // for canonical()
+
+namespace nfd {
+
+shared_ptr<UnixStreamChannel>
+UnixStreamFactory::createChannel(const std::string& unixSocketPath)
+{
+  boost::filesystem::path p(unixSocketPath);
+  p = boost::filesystem::canonical(p.parent_path()) / p.filename();
+  unix_stream::Endpoint endpoint(p.string());
+
+  shared_ptr<UnixStreamChannel> channel = findChannel(endpoint);
+  if (channel)
+    return channel;
+
+  channel = make_shared<UnixStreamChannel>(endpoint);
+  m_channels[endpoint] = channel;
+  return channel;
+}
+
+shared_ptr<UnixStreamChannel>
+UnixStreamFactory::findChannel(const unix_stream::Endpoint& endpoint)
+{
+  ChannelMap::iterator i = m_channels.find(endpoint);
+  if (i != m_channels.end())
+    return i->second;
+  else
+    return shared_ptr<UnixStreamChannel>();
+}
+
+void
+UnixStreamFactory::createFace(const FaceUri& uri,
+                              const FaceCreatedCallback& onCreated,
+                              const FaceConnectFailedCallback& onConnectFailed)
+{
+  throw Error("UnixStreamFactory does not support 'createFace' operation");
+}
+
+std::list<shared_ptr<const Channel> >
+UnixStreamFactory::getChannels() const
+{
+  std::list<shared_ptr<const Channel> > channels;
+  for (ChannelMap::const_iterator i = m_channels.begin(); i != m_channels.end(); ++i)
+    {
+      channels.push_back(i->second);
+    }
+
+  return channels;
+}
+
+} // namespace nfd
diff --git a/NFD/daemon/face/unix-stream-factory.hpp b/NFD/daemon/face/unix-stream-factory.hpp
new file mode 100644
index 0000000..e2aff30
--- /dev/null
+++ b/NFD/daemon/face/unix-stream-factory.hpp
@@ -0,0 +1,89 @@
+/* -*- 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_DAEMON_FACE_UNIX_STREAM_FACTORY_HPP
+#define NFD_DAEMON_FACE_UNIX_STREAM_FACTORY_HPP
+
+#include "protocol-factory.hpp"
+#include "unix-stream-channel.hpp"
+
+namespace nfd {
+
+class UnixStreamFactory : public ProtocolFactory
+{
+public:
+  /**
+   * \brief Exception of UnixStreamFactory
+   */
+  struct Error : public ProtocolFactory::Error
+  {
+    Error(const std::string& what) : ProtocolFactory::Error(what) {}
+  };
+
+  /**
+   * \brief Create stream-oriented Unix channel using specified socket path
+   *
+   * If this method is called twice with the same path, only one channel
+   * will be created.  The second call will just retrieve the existing
+   * channel.
+   *
+   * \returns always a valid pointer to a UnixStreamChannel object,
+   *          an exception will be thrown if the channel cannot be created.
+   *
+   * \throws UnixStreamFactory::Error
+   */
+  shared_ptr<UnixStreamChannel>
+  createChannel(const std::string& unixSocketPath);
+
+  // from Factory
+
+  virtual void
+  createFace(const FaceUri& uri,
+             const FaceCreatedCallback& onCreated,
+             const FaceConnectFailedCallback& onConnectFailed);
+
+  virtual std::list<shared_ptr<const Channel> >
+  getChannels() const;
+
+private:
+  /**
+   * \brief Look up UnixStreamChannel using specified endpoint
+   *
+   * \returns shared pointer to the existing UnixStreamChannel object
+   *          or empty shared pointer when such channel does not exist
+   *
+   * \throws never
+   */
+  shared_ptr<UnixStreamChannel>
+  findChannel(const unix_stream::Endpoint& endpoint);
+
+private:
+  typedef std::map< unix_stream::Endpoint, shared_ptr<UnixStreamChannel> > ChannelMap;
+  ChannelMap m_channels;
+};
+
+} // namespace nfd
+
+#endif // NFD_DAEMON_FACE_UNIX_STREAM_FACTORY_HPP
diff --git a/NFD/daemon/face/websocket-channel.cpp b/NFD/daemon/face/websocket-channel.cpp
new file mode 100644
index 0000000..8abdbac
--- /dev/null
+++ b/NFD/daemon/face/websocket-channel.cpp
@@ -0,0 +1,196 @@
+/* -*- 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/>.
+ */
+
+#include "websocket-channel.hpp"
+
+#include <boost/date_time/posix_time/posix_time.hpp>
+
+namespace nfd {
+
+NFD_LOG_INIT("WebSocketChannel");
+
+using namespace boost::asio;
+
+WebSocketChannel::WebSocketChannel(const websocket::Endpoint& localEndpoint)
+  : m_localEndpoint(localEndpoint)
+  , m_isListening(false)
+  , m_pingInterval(10000)
+{
+  // Setup WebSocket server
+  m_server.clear_access_channels(websocketpp::log::alevel::all);
+  m_server.clear_error_channels(websocketpp::log::alevel::all);
+
+  m_server.set_message_handler(bind(&WebSocketChannel::handleMessage, this, _1, _2));
+  m_server.set_open_handler(bind(&WebSocketChannel::handleOpen, this, _1));
+  m_server.set_close_handler(bind(&WebSocketChannel::handleClose, this, _1));
+  m_server.init_asio(&getGlobalIoService());
+  // Always set SO_REUSEADDR flag
+  m_server.set_reuse_addr(true);
+
+  // Detect disconnection using PONG message
+  m_server.set_pong_handler(bind(&WebSocketChannel::handlePong, this, _1, _2));
+  m_server.set_pong_timeout_handler(bind(&WebSocketChannel::handlePongTimeout,
+                                         this, _1, _2));
+
+  this->setUri(FaceUri(localEndpoint, "ws"));
+}
+
+WebSocketChannel::~WebSocketChannel()
+{
+}
+
+void
+WebSocketChannel::setPongTimeout(time::milliseconds timeout)
+{
+  m_server.set_pong_timeout(static_cast<long>(timeout.count()));
+}
+
+void
+WebSocketChannel::handlePongTimeout(websocketpp::connection_hdl hdl, std::string msg)
+{
+  ChannelFaceMap::iterator it = m_channelFaces.find(hdl);
+  if (it != m_channelFaces.end())
+    {
+      it->second->close();
+      NFD_LOG_DEBUG("handlePongTimeout: remove " << it->second->getRemoteUri());
+      m_channelFaces.erase(it);
+    }
+}
+
+void
+WebSocketChannel::handlePong(websocketpp::connection_hdl hdl, std::string msg)
+{
+  ChannelFaceMap::iterator it = m_channelFaces.find(hdl);
+  if (it != m_channelFaces.end())
+    {
+      NFD_LOG_TRACE("handlePong: from " << it->second->getRemoteUri());
+    }
+}
+
+void
+WebSocketChannel::handleMessage(websocketpp::connection_hdl hdl,
+                                websocket::Server::message_ptr msg)
+{
+  ChannelFaceMap::iterator it = m_channelFaces.find(hdl);
+  if (it != m_channelFaces.end())
+    {
+      it->second->handleReceive(msg->get_payload());
+    }
+}
+
+void
+WebSocketChannel::handleOpen(websocketpp::connection_hdl hdl)
+{
+  std::string remote;
+  try
+    {
+      remote = "wsclient://" + m_server.get_con_from_hdl(hdl)->get_remote_endpoint();
+    }
+  catch (websocketpp::lib::error_code&)
+    {
+      NFD_LOG_DEBUG("handleOpen: cannot get remote uri");
+      websocketpp::lib::error_code ecode;
+      m_server.close(hdl, websocketpp::close::status::normal, "closed by channel", ecode);
+    }
+  shared_ptr<WebSocketFace> face = ndn::make_shared<WebSocketFace>(FaceUri(remote), this->getUri(),
+                                                                   hdl, ref(m_server));
+  m_onFaceCreatedCallback(face);
+  m_channelFaces[hdl] = face;
+
+  // Schedule PING message
+  EventId pingEvent = scheduler::schedule(m_pingInterval,
+                                          bind(&WebSocketChannel::sendPing, this, hdl));
+  face->setPingEventId(pingEvent);
+}
+
+void
+WebSocketChannel::sendPing(websocketpp::connection_hdl hdl)
+{
+  ChannelFaceMap::iterator it = m_channelFaces.find(hdl);
+  if (it != m_channelFaces.end())
+    {
+      try
+        {
+          m_server.ping(hdl, "NFD-WebSocket");
+        }
+      catch (websocketpp::lib::error_code&)
+        {
+          it->second->close();
+          NFD_LOG_DEBUG("sendPing: failed to ping " << it->second->getRemoteUri());
+          m_channelFaces.erase(it);
+        }
+
+      NFD_LOG_TRACE("sendPing: to " << it->second->getRemoteUri());
+
+      // Schedule next PING message
+      EventId pingEvent = scheduler::schedule(m_pingInterval,
+                                              bind(&WebSocketChannel::sendPing, this, hdl));
+      it->second->setPingEventId(pingEvent);
+    }
+}
+
+void
+WebSocketChannel::handleClose(websocketpp::connection_hdl hdl)
+{
+  ChannelFaceMap::iterator it = m_channelFaces.find(hdl);
+  if (it != m_channelFaces.end())
+    {
+      it->second->close();
+      NFD_LOG_DEBUG("handleClose: remove " << it->second->getRemoteUri());
+      m_channelFaces.erase(it);
+    }
+}
+
+
+void
+WebSocketChannel::listen(const FaceCreatedCallback& onFaceCreated)
+{
+  if (m_isListening)
+    {
+      throw Error("Listen already called on this channel");
+    }
+  m_isListening = true;
+
+  m_onFaceCreatedCallback = onFaceCreated;
+
+  try
+    {
+      m_server.listen(m_localEndpoint);
+    }
+  catch (websocketpp::lib::error_code ec)
+    {
+      throw Error("Failed to listen on local endpoint");
+    }
+
+  m_server.start_accept();
+}
+
+size_t
+WebSocketChannel::size() const
+{
+  return m_channelFaces.size();
+}
+
+} // namespace nfd
diff --git a/NFD/daemon/face/websocket-channel.hpp b/NFD/daemon/face/websocket-channel.hpp
new file mode 100644
index 0000000..f7c6ae2
--- /dev/null
+++ b/NFD/daemon/face/websocket-channel.hpp
@@ -0,0 +1,151 @@
+/* -*- 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_DAEMON_FACE_WEBSOCKET_CHANNEL_HPP
+#define NFD_DAEMON_FACE_WEBSOCKET_CHANNEL_HPP
+
+#include "channel.hpp"
+#include "core/global-io.hpp"
+#include "core/scheduler.hpp"
+#include "websocket-face.hpp"
+
+namespace nfd {
+
+/**
+ * \brief Class implementing WebSocket-based channel to create faces
+ *
+ *
+ */
+class WebSocketChannel : public Channel
+{
+public:
+  /**
+   * \brief Exception of WebSocketChannel
+   */
+  class Error : public std::runtime_error
+  {
+  public:
+    explicit
+    Error(const std::string& what)
+      : runtime_error(what)
+    {
+    }
+  };
+
+  /**
+   * \brief Create WebSocket channel for the local endpoint
+   *
+   * To enable creation of faces upon incoming connections,
+   * one needs to explicitly call WebSocketChannel::listen method.
+   * The created socket is bound to the localEndpoint.
+   *
+   * \throw WebSocketChannel::Error if bind on the socket fails
+   */
+  explicit
+  WebSocketChannel(const websocket::Endpoint& localEndpoint);
+
+  virtual
+  ~WebSocketChannel();
+
+  /**
+   * \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
+   *
+   * \throws WebSocketChannel::Error if called multiple times
+   */
+  void
+  listen(const FaceCreatedCallback& onFaceCreated);
+
+  /**
+   * \brief Get number of faces in the channel
+   */
+  size_t
+  size() const;
+
+  bool
+  isListening() const;
+
+  void
+  setPingInterval(time::milliseconds interval)
+  {
+    m_pingInterval = interval;
+  }
+
+  void
+  setPongTimeout(time::milliseconds timeout);
+
+private:
+  void
+  sendPing(websocketpp::connection_hdl hdl);
+
+  void
+  handlePong(websocketpp::connection_hdl hdl, std::string msg);
+
+  void
+  handlePongTimeout(websocketpp::connection_hdl hdl, std::string msg);
+
+  void
+  handleMessage(websocketpp::connection_hdl hdl, websocket::Server::message_ptr msg);
+
+  void
+  handleOpen(websocketpp::connection_hdl hdl);
+
+  void
+  handleClose(websocketpp::connection_hdl hdl);
+
+private:
+  websocket::Endpoint m_localEndpoint;
+
+  websocket::Server m_server;
+
+  /**
+   * Callbacks for face creation.
+   * New communications are detected using async_receive_from.
+   * Its handler has a fixed signature. No space for the face callback
+   */
+  FaceCreatedCallback m_onFaceCreatedCallback;
+
+  typedef std::map< websocketpp::connection_hdl, shared_ptr<WebSocketFace>,
+                    std::owner_less<websocketpp::connection_hdl> > ChannelFaceMap;
+  ChannelFaceMap m_channelFaces;
+
+  /**
+   * \brief If true, it means the function listen has already been called
+   */
+  bool m_isListening;
+
+  time::milliseconds m_pingInterval;
+};
+
+inline bool
+WebSocketChannel::isListening() const
+{
+  return m_isListening;
+}
+
+} // namespace nfd
+
+#endif // NFD_DAEMON_FACE_WEBSOCKET_CHANNEL_HPP
diff --git a/NFD/daemon/face/websocket-face.cpp b/NFD/daemon/face/websocket-face.cpp
new file mode 100644
index 0000000..abd8b3c
--- /dev/null
+++ b/NFD/daemon/face/websocket-face.cpp
@@ -0,0 +1,133 @@
+/* -*- 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/>.
+ */
+
+#include "websocket-face.hpp"
+#include "core/global-io.hpp"
+
+namespace nfd {
+
+NFD_LOG_INIT("WebSocketFace");
+
+WebSocketFace::WebSocketFace(const FaceUri& remoteUri, const FaceUri& localUri,
+                             websocketpp::connection_hdl hdl,
+                             websocket::Server& server)
+  : Face(remoteUri, localUri)
+  , m_handle(hdl)
+  , m_server(server)
+  , m_closed(false)
+{
+  this->setOnDemand(true);
+}
+
+void
+WebSocketFace::sendInterest(const Interest& interest)
+{
+  if (m_closed)
+    return;
+
+  this->onSendInterest(interest);
+  const Block& payload = interest.wireEncode();
+  this->getMutableCounters().getNOutBytes() += payload.size();
+
+  try {
+    m_server.send(m_handle, payload.wire(), payload.size(),
+                  websocketpp::frame::opcode::binary);
+  }
+  catch (const websocketpp::lib::error_code& e) {
+    NFD_LOG_DEBUG("Failed to send Interest because: " << e
+                  << "(" << e.message() << ")");
+  }
+}
+
+void
+WebSocketFace::sendData(const Data& data)
+{
+  if (m_closed)
+    return;
+
+  this->onSendData(data);
+  const Block& payload = data.wireEncode();
+  this->getMutableCounters().getNOutBytes() += payload.size();
+
+  try {
+    m_server.send(m_handle, payload.wire(), payload.size(),
+                  websocketpp::frame::opcode::binary);
+  }
+  catch (const websocketpp::lib::error_code& e) {
+    NFD_LOG_DEBUG("Failed to send Data because: " << e
+                  << "(" << e.message() << ")");
+  }
+}
+
+void
+WebSocketFace::close()
+{
+  if (m_closed == false)
+    {
+      m_closed = true;
+      scheduler::cancel(m_pingEventId);
+      websocketpp::lib::error_code ecode;
+      m_server.close(m_handle, websocketpp::close::status::normal, "closed by nfd", ecode);
+
+      fail("Face closed");
+    }
+}
+
+void
+WebSocketFace::handleReceive(const std::string& msg)
+{
+  // Copy message into Face internal buffer
+  if (msg.size() > ndn::MAX_NDN_PACKET_SIZE)
+    {
+      NFD_LOG_WARN("[id:" << this->getId()
+                   << "] Received WebSocket message size ["
+                   << msg.size() << "] is too big");
+      return;
+    }
+
+  this->getMutableCounters().getNInBytes() += msg.size();
+
+  // Try to parse message data
+  bool isOk = true;
+  Block element;
+  isOk = Block::fromBuffer(reinterpret_cast<const uint8_t*>(msg.c_str()), msg.size(), element);
+  if (!isOk)
+    {
+      NFD_LOG_WARN("[id:" << this->getId()
+                   << "] Received invalid NDN packet of length ["
+                   << msg.size() << "]");
+      return;
+    }
+
+  if (!this->decodeAndDispatchInput(element))
+    {
+      NFD_LOG_WARN("[id:" << this->getId()
+                   << "] Received unrecognized block of type ["
+                   << element.type() << "]");
+      // ignore unknown packet and proceed
+    }
+}
+
+} // namespace nfd
diff --git a/NFD/daemon/face/websocket-face.hpp b/NFD/daemon/face/websocket-face.hpp
new file mode 100644
index 0000000..81a1111
--- /dev/null
+++ b/NFD/daemon/face/websocket-face.hpp
@@ -0,0 +1,85 @@
+/* -*- 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_DAEMON_FACE_WEBSOCKET_FACE_HPP
+#define NFD_DAEMON_FACE_WEBSOCKET_FACE_HPP
+
+#include "face.hpp"
+#include "core/logger.hpp"
+#include "core/scheduler.hpp"
+
+#ifndef HAVE_WEBSOCKET
+#error "Cannot include this file when WebSocket support is not enabled"
+#endif // HAVE_WEBSOCKET
+
+#include "websocketpp.hpp"
+
+namespace nfd {
+
+namespace websocket {
+typedef boost::asio::ip::tcp::endpoint Endpoint;
+typedef websocketpp::server<websocketpp::config::asio> Server;
+} // namespace websocket
+
+
+/**
+ * \brief Implementation of Face abstraction that uses WebSocket
+ *        as underlying transport mechanism
+ */
+class WebSocketFace : public Face
+{
+public:
+  WebSocketFace(const FaceUri& remoteUri, const FaceUri& localUri,
+                websocketpp::connection_hdl hdl, websocket::Server& server);
+
+  // from Face
+  virtual void
+  sendInterest(const Interest& interest);
+
+  virtual void
+  sendData(const Data& data);
+
+  virtual void
+  close();
+
+  void
+  setPingEventId(EventId& id)
+  {
+    m_pingEventId = id;
+  }
+
+  void
+  handleReceive(const std::string& msg);
+
+private:
+  websocketpp::connection_hdl m_handle;
+  websocket::Server& m_server;
+  EventId m_pingEventId;
+  bool m_closed;
+};
+
+} // namespace nfd
+
+#endif // NFD_DAEMON_FACE_WEBSOCKET_FACE_HPP
diff --git a/NFD/daemon/face/websocket-factory.cpp b/NFD/daemon/face/websocket-factory.cpp
new file mode 100644
index 0000000..30f3afd
--- /dev/null
+++ b/NFD/daemon/face/websocket-factory.cpp
@@ -0,0 +1,91 @@
+/* -*- 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/>.
+ */
+
+#include "websocket-factory.hpp"
+#include "core/resolver.hpp"
+
+namespace nfd {
+
+using namespace boost::asio;
+
+NFD_LOG_INIT("WebSocketFactory");
+
+WebSocketFactory::WebSocketFactory(const std::string& defaultPort)
+  : m_defaultPort(defaultPort)
+{
+}
+
+shared_ptr<WebSocketChannel>
+WebSocketFactory::createChannel(const websocket::Endpoint& endpoint)
+{
+  shared_ptr<WebSocketChannel> channel = findChannel(endpoint);
+  if (static_cast<bool>(channel))
+    return channel;
+
+  channel = make_shared<WebSocketChannel>(endpoint);
+  m_channels[endpoint] = channel;
+
+  return channel;
+}
+
+shared_ptr<WebSocketChannel>
+WebSocketFactory::createChannel(const std::string& host, const std::string& port)
+{
+  ip::tcp::endpoint tcpEndpoint = TcpResolver::syncResolve(host, port);
+  websocket::Endpoint endpoint(tcpEndpoint.address(), tcpEndpoint.port());
+  return createChannel(endpoint);
+}
+
+shared_ptr<WebSocketChannel>
+WebSocketFactory::findChannel(const websocket::Endpoint& localEndpoint)
+{
+  ChannelMap::iterator i = m_channels.find(localEndpoint);
+  if (i != m_channels.end())
+    return i->second;
+  else
+    return shared_ptr<WebSocketChannel>();
+}
+
+void
+WebSocketFactory::createFace(const FaceUri& uri,
+                             const FaceCreatedCallback& onCreated,
+                             const FaceConnectFailedCallback& onConnectFailed)
+{
+  throw Error("WebSocketFactory does not support 'createFace' operation");
+}
+
+std::list<shared_ptr<const Channel> >
+WebSocketFactory::getChannels() const
+{
+  std::list<shared_ptr<const Channel> > channels;
+  for (ChannelMap::const_iterator i = m_channels.begin(); i != m_channels.end(); ++i)
+    {
+      channels.push_back(i->second);
+    }
+
+  return channels;
+}
+
+} // namespace nfd
diff --git a/NFD/daemon/face/websocket-factory.hpp b/NFD/daemon/face/websocket-factory.hpp
new file mode 100644
index 0000000..d3a6324
--- /dev/null
+++ b/NFD/daemon/face/websocket-factory.hpp
@@ -0,0 +1,110 @@
+/* -*- 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_DAEMON_FACE_WEBSOCKET_FACTORY_HPP
+#define NFD_DAEMON_FACE_WEBSOCKET_FACTORY_HPP
+
+#include "protocol-factory.hpp"
+#include "websocket-channel.hpp"
+
+
+namespace nfd {
+
+class WebSocketFactory : public ProtocolFactory
+{
+public:
+  /**
+   * \brief Exception of WebSocketFactory
+   */
+  class Error : public ProtocolFactory::Error
+  {
+  public:
+    explicit
+    Error(const std::string& what)
+      : ProtocolFactory::Error(what)
+    {
+    }
+  };
+
+  explicit
+  WebSocketFactory(const std::string& defaultPort);
+
+  /**
+   * \brief Create WebSocket-based channel using websocket::Endpoint
+   *
+   * websocket::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.
+   *
+   * \returns always a valid pointer to a WebSocketChannel object, an exception
+   *          is thrown if it cannot be created.
+   *
+   * \throws WebSocketFactory::Error, TcpResolver::Error
+   *
+   */
+  shared_ptr<WebSocketChannel>
+  createChannel(const websocket::Endpoint& localEndpoint);
+
+  /**
+   * \brief Create WebSocket-based channel using specified ip address and port number
+   *
+   * \throws WebSocketFactory::Error
+   */
+  shared_ptr<WebSocketChannel>
+  createChannel(const std::string& ipAddress, const std::string& port);
+
+  // from Factory
+  virtual void
+  createFace(const FaceUri& uri,
+             const FaceCreatedCallback& onCreated,
+             const FaceConnectFailedCallback& onConnectFailed);
+
+  virtual std::list<shared_ptr<const Channel> >
+  getChannels() const;
+
+private:
+
+  /**
+   * \brief Look up WebSocketChannel using specified local endpoint
+   *
+   * \returns shared pointer to the existing WebSocketChannel object
+   *          or empty shared pointer when such channel does not exist
+   *
+   * \throws never
+   */
+  shared_ptr<WebSocketChannel>
+  findChannel(const websocket::Endpoint& localEndpoint);
+
+  typedef std::map< websocket::Endpoint, shared_ptr<WebSocketChannel> > ChannelMap;
+  ChannelMap m_channels;
+
+  std::string m_defaultPort;
+};
+
+} // namespace nfd
+
+#endif // NFD_DAEMON_FACE_WEBSOCKET_FACTORY_HPP
diff --git a/NFD/daemon/face/websocketpp.hpp b/NFD/daemon/face/websocketpp.hpp
new file mode 100644
index 0000000..f590bd1
--- /dev/null
+++ b/NFD/daemon/face/websocketpp.hpp
@@ -0,0 +1,40 @@
+/* -*- 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_DAEMON_FACE_WEBSOCKETPP_HPP
+#define NFD_DAEMON_FACE_WEBSOCKETPP_HPP
+
+#ifndef HAVE_WEBSOCKET
+#error "This file must not be included when WebSocket Face support is disabled"
+#endif
+
+// suppress websocketpp warnings
+#pragma GCC system_header
+#pragma clang system_header
+
+#include "websocketpp/config/asio_no_tls.hpp"
+#include "websocketpp/server.hpp"
+
+#endif // NFD_DAEMON_FACE_WEBSOCKETPP_HPP
diff --git a/NFD/daemon/fw/available-strategies.cpp b/NFD/daemon/fw/available-strategies.cpp
new file mode 100644
index 0000000..45be270
--- /dev/null
+++ b/NFD/daemon/fw/available-strategies.cpp
@@ -0,0 +1,62 @@
+/* -*- 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/>.
+ */
+
+#include "best-route-strategy.hpp"
+#include "broadcast-strategy.hpp"
+#include "client-control-strategy.hpp"
+#include "ncc-strategy.hpp"
+#include "best-route-strategy2.hpp"
+
+namespace nfd {
+namespace fw {
+
+shared_ptr<Strategy>
+makeDefaultStrategy(Forwarder& forwarder)
+{
+  return make_shared<BestRouteStrategy2>(ref(forwarder));
+}
+
+template<typename S>
+inline void
+installStrategy(Forwarder& forwarder)
+{
+  StrategyChoice& strategyChoice = forwarder.getStrategyChoice();
+  if (!strategyChoice.hasStrategy(S::STRATEGY_NAME)) {
+    strategyChoice.install(make_shared<S>(ref(forwarder)));
+  }
+}
+
+void
+installStrategies(Forwarder& forwarder)
+{
+  installStrategy<BestRouteStrategy>(forwarder);
+  installStrategy<BroadcastStrategy>(forwarder);
+  installStrategy<ClientControlStrategy>(forwarder);
+  installStrategy<NccStrategy>(forwarder);
+  installStrategy<BestRouteStrategy2>(forwarder);
+}
+
+} // namespace fw
+} // namespace nfd
diff --git a/NFD/daemon/fw/available-strategies.hpp b/NFD/daemon/fw/available-strategies.hpp
new file mode 100644
index 0000000..616a1b4
--- /dev/null
+++ b/NFD/daemon/fw/available-strategies.hpp
@@ -0,0 +1,42 @@
+/* -*- 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
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+#ifndef NFD_DAEMON_FW_AVAILABLE_STRATEGIES_HPP
+#define NFD_DAEMON_FW_AVAILABLE_STRATEGIES_HPP
+
+#include "strategy.hpp"
+
+namespace nfd {
+namespace fw {
+
+shared_ptr<Strategy>
+makeDefaultStrategy(Forwarder& forwarder);
+
+void
+installStrategies(Forwarder& forwarder);
+
+} // namespace fw
+} // namespace nfd
+
+#endif // NFD_DAEMON_FW_AVAILABLE_STRATEGIES_HPP
diff --git a/NFD/daemon/fw/best-route-strategy.cpp b/NFD/daemon/fw/best-route-strategy.cpp
new file mode 100644
index 0000000..f64bb66
--- /dev/null
+++ b/NFD/daemon/fw/best-route-strategy.cpp
@@ -0,0 +1,74 @@
+/* -*- 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/>.
+ */
+
+#include "best-route-strategy.hpp"
+
+namespace nfd {
+namespace fw {
+
+const Name BestRouteStrategy::STRATEGY_NAME("ndn:/localhost/nfd/strategy/best-route/%FD%01");
+
+BestRouteStrategy::BestRouteStrategy(Forwarder& forwarder, const Name& name)
+  : Strategy(forwarder, name)
+{
+}
+
+BestRouteStrategy::~BestRouteStrategy()
+{
+}
+
+static inline bool
+predicate_PitEntry_canForwardTo_NextHop(shared_ptr<pit::Entry> pitEntry,
+                                        const fib::NextHop& nexthop)
+{
+  return pitEntry->canForwardTo(*nexthop.getFace());
+}
+
+void
+BestRouteStrategy::afterReceiveInterest(const Face& inFace,
+                   const Interest& interest,
+                   shared_ptr<fib::Entry> fibEntry,
+                   shared_ptr<pit::Entry> pitEntry)
+{
+  if (pitEntry->hasUnexpiredOutRecords()) {
+    // not a new Interest, don't forward
+    return;
+  }
+
+  const fib::NextHopList& nexthops = fibEntry->getNextHops();
+  fib::NextHopList::const_iterator it = std::find_if(nexthops.begin(), nexthops.end(),
+    bind(&predicate_PitEntry_canForwardTo_NextHop, pitEntry, _1));
+
+  if (it == nexthops.end()) {
+    this->rejectPendingInterest(pitEntry);
+    return;
+  }
+
+  shared_ptr<Face> outFace = it->getFace();
+  this->sendInterest(pitEntry, outFace);
+}
+
+} // namespace fw
+} // namespace nfd
diff --git a/NFD/daemon/fw/best-route-strategy.hpp b/NFD/daemon/fw/best-route-strategy.hpp
new file mode 100644
index 0000000..7ec64ff
--- /dev/null
+++ b/NFD/daemon/fw/best-route-strategy.hpp
@@ -0,0 +1,66 @@
+/* -*- 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_DAEMON_FW_BEST_ROUTE_STRATEGY_HPP
+#define NFD_DAEMON_FW_BEST_ROUTE_STRATEGY_HPP
+
+#include "strategy.hpp"
+
+namespace nfd {
+namespace fw {
+
+/** \brief Best Route strategy version 1
+ *
+ *  This strategy forwards a new Interest to the lowest-cost nexthop
+ *  that is not same as the downstream, and does not violate scope.
+ *  Subsequent similar Interests or consumer retransmissions are suppressed
+ *  until after InterestLifetime expiry.
+ *
+ *  \deprecated This strategy is superceded by Best Route strategy version 2,
+ *              which allows consumer retransmissions. This version is kept for
+ *              comparison purposes and is not recommended for general usage.
+ */
+class BestRouteStrategy : public Strategy
+{
+public:
+  BestRouteStrategy(Forwarder& forwarder, const Name& name = STRATEGY_NAME);
+
+  virtual
+  ~BestRouteStrategy();
+
+  virtual void
+  afterReceiveInterest(const Face& inFace,
+                       const Interest& interest,
+                       shared_ptr<fib::Entry> fibEntry,
+                       shared_ptr<pit::Entry> pitEntry) DECL_OVERRIDE;
+
+public:
+  static const Name STRATEGY_NAME;
+};
+
+} // namespace fw
+} // namespace nfd
+
+#endif // NFD_DAEMON_FW_BEST_ROUTE_STRATEGY_HPP
diff --git a/NFD/daemon/fw/best-route-strategy2.cpp b/NFD/daemon/fw/best-route-strategy2.cpp
new file mode 100644
index 0000000..044f5e9
--- /dev/null
+++ b/NFD/daemon/fw/best-route-strategy2.cpp
@@ -0,0 +1,174 @@
+/* -*- 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/>.
+ */
+
+#include "best-route-strategy2.hpp"
+#include "core/logger.hpp"
+
+namespace nfd {
+namespace fw {
+
+NFD_LOG_INIT("BestRouteStrategy2");
+
+const Name BestRouteStrategy2::STRATEGY_NAME("ndn:/localhost/nfd/strategy/best-route/%FD%02");
+/// \todo don't use fixed interval; make it adaptive or use exponential back-off #1913
+const time::milliseconds BestRouteStrategy2::MIN_RETRANSMISSION_INTERVAL(100);
+
+BestRouteStrategy2::BestRouteStrategy2(Forwarder& forwarder, const Name& name)
+  : Strategy(forwarder, name)
+{
+}
+
+/** \brief determines whether a NextHop is eligible
+ *  \param currentDownstream incoming FaceId of current Interest
+ *  \param wantUnused if true, NextHop must not have unexpired OutRecord
+ *  \param now time::steady_clock::now(), ignored if !wantUnused
+ */
+static inline bool
+predicate_NextHop_eligible(const shared_ptr<pit::Entry>& pitEntry,
+  const fib::NextHop& nexthop, FaceId currentDownstream,
+  bool wantUnused = false,
+  time::steady_clock::TimePoint now = time::steady_clock::TimePoint::min())
+{
+  shared_ptr<Face> upstream = nexthop.getFace();
+
+  // upstream is current downstream
+  if (upstream->getId() == currentDownstream)
+    return false;
+
+  // forwarding would violate scope
+  if (pitEntry->violatesScope(*upstream))
+    return false;
+
+  if (wantUnused) {
+    // NextHop must not have unexpired OutRecord
+    pit::OutRecordCollection::const_iterator outRecord = pitEntry->getOutRecord(*upstream);
+    if (outRecord != pitEntry->getOutRecords().end() &&
+        outRecord->getExpiry() > now) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+static inline bool
+compare_OutRecord_lastRenewed(const pit::OutRecord& a, const pit::OutRecord& b)
+{
+  return a.getLastRenewed() < b.getLastRenewed();
+}
+
+/** \brief pick an eligible NextHop with earliest OutRecord
+ *  \note It is assumed that every nexthop has an OutRecord
+ */
+static inline fib::NextHopList::const_iterator
+findEligibleNextHopWithEarliestOutRecord(const shared_ptr<pit::Entry>& pitEntry,
+                                         const fib::NextHopList& nexthops,
+                                         FaceId currentDownstream)
+{
+  fib::NextHopList::const_iterator found = nexthops.end();
+  time::steady_clock::TimePoint earliestRenewed = time::steady_clock::TimePoint::max();
+  for (fib::NextHopList::const_iterator it = nexthops.begin(); it != nexthops.end(); ++it) {
+    if (!predicate_NextHop_eligible(pitEntry, *it, currentDownstream))
+      continue;
+    pit::OutRecordCollection::const_iterator outRecord = pitEntry->getOutRecord(*it->getFace());
+    BOOST_ASSERT(outRecord != pitEntry->getOutRecords().end());
+    if (outRecord->getLastRenewed() < earliestRenewed) {
+      found = it;
+      earliestRenewed = outRecord->getLastRenewed();
+    }
+  }
+  return found;
+}
+
+void
+BestRouteStrategy2::afterReceiveInterest(const Face& inFace,
+                                         const Interest& interest,
+                                         shared_ptr<fib::Entry> fibEntry,
+                                         shared_ptr<pit::Entry> pitEntry)
+{
+  const fib::NextHopList& nexthops = fibEntry->getNextHops();
+  fib::NextHopList::const_iterator it = nexthops.end();
+
+  bool isNewPitEntry = !pitEntry->hasUnexpiredOutRecords();
+  if (isNewPitEntry) {
+    // forward to nexthop with lowest cost except downstream
+    it = std::find_if(nexthops.begin(), nexthops.end(),
+      bind(&predicate_NextHop_eligible, pitEntry, _1, inFace.getId(),
+           false, time::steady_clock::TimePoint::min()));
+
+    if (it == nexthops.end()) {
+      NFD_LOG_DEBUG(interest << " from=" << inFace.getId() << " noNextHop");
+      this->rejectPendingInterest(pitEntry);
+      return;
+    }
+
+    shared_ptr<Face> outFace = it->getFace();
+    this->sendInterest(pitEntry, outFace);
+    NFD_LOG_DEBUG(interest << " from=" << inFace.getId()
+                           << " newPitEntry-to=" << outFace->getId());
+    return;
+  }
+
+  // when was the last outgoing Interest?
+  const pit::OutRecordCollection& outRecords = pitEntry->getOutRecords();
+  pit::OutRecordCollection::const_iterator lastOutgoing = std::max_element(
+    outRecords.begin(), outRecords.end(), &compare_OutRecord_lastRenewed);
+  BOOST_ASSERT(lastOutgoing != outRecords.end()); // otherwise it's new PIT entry
+
+  time::steady_clock::TimePoint now = time::steady_clock::now();
+  time::steady_clock::Duration sinceLastOutgoing = now - lastOutgoing->getLastRenewed();
+  bool shouldRetransmit = sinceLastOutgoing >= MIN_RETRANSMISSION_INTERVAL;
+  if (!shouldRetransmit) {
+    NFD_LOG_DEBUG(interest << " from=" << inFace.getId()
+                           << " dontRetransmit sinceLastOutgoing=" << sinceLastOutgoing.count());
+    return;
+  }
+
+  // find an unused upstream with lowest cost except downstream
+  it = std::find_if(nexthops.begin(), nexthops.end(),
+    bind(&predicate_NextHop_eligible, pitEntry, _1, inFace.getId(), true, now));
+  if (it != nexthops.end()) {
+    shared_ptr<Face> outFace = it->getFace();
+    this->sendInterest(pitEntry, outFace);
+    NFD_LOG_DEBUG(interest << " from=" << inFace.getId()
+                           << " retransmit-unused-to=" << outFace->getId());
+    return;
+  }
+
+  // find an eligible upstream that is used earliest
+  it = findEligibleNextHopWithEarliestOutRecord(pitEntry, nexthops, inFace.getId());
+  if (it == nexthops.end()) {
+    NFD_LOG_DEBUG(interest << " from=" << inFace.getId() << " retransmitNoNextHop");
+  }
+  else {
+    shared_ptr<Face> outFace = it->getFace();
+    this->sendInterest(pitEntry, outFace);
+    NFD_LOG_DEBUG(interest << " from=" << inFace.getId()
+                           << " retransmit-retry-to=" << outFace->getId());
+  }
+}
+
+} // namespace fw
+} // namespace nfd
diff --git a/NFD/daemon/fw/best-route-strategy2.hpp b/NFD/daemon/fw/best-route-strategy2.hpp
new file mode 100644
index 0000000..bb259d0
--- /dev/null
+++ b/NFD/daemon/fw/best-route-strategy2.hpp
@@ -0,0 +1,61 @@
+/* -*- 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_DAEMON_FW_BEST_ROUTE_STRATEGY2_HPP
+#define NFD_DAEMON_FW_BEST_ROUTE_STRATEGY2_HPP
+
+#include "strategy.hpp"
+
+namespace nfd {
+namespace fw {
+
+/** \brief Best Route strategy version 2
+ *
+ *  This strategy forwards a new Interest to the lowest-cost nexthop (except downstream).
+ *  After that, it recognizes consumer retransmission:
+ *  if a similar Interest arrives from any downstream after MIN_RETRANSMISSION_INTERVAL,
+ *  the strategy forwards the Interest again to the lowest-cost nexthop (except downstream)
+ *  that is not previously used. If all nexthops have been used, the strategy starts over.
+ */
+class BestRouteStrategy2 : public Strategy
+{
+public:
+  BestRouteStrategy2(Forwarder& forwarder, const Name& name = STRATEGY_NAME);
+
+  virtual void
+  afterReceiveInterest(const Face& inFace,
+                       const Interest& interest,
+                       shared_ptr<fib::Entry> fibEntry,
+                       shared_ptr<pit::Entry> pitEntry) DECL_OVERRIDE;
+
+public:
+  static const Name STRATEGY_NAME;
+  static const time::milliseconds MIN_RETRANSMISSION_INTERVAL;
+};
+
+} // namespace fw
+} // namespace nfd
+
+#endif // NFD_DAEMON_FW_BEST_ROUTE_STRATEGY2_HPP
diff --git a/NFD/daemon/fw/broadcast-strategy.cpp b/NFD/daemon/fw/broadcast-strategy.cpp
new file mode 100644
index 0000000..48ab17b
--- /dev/null
+++ b/NFD/daemon/fw/broadcast-strategy.cpp
@@ -0,0 +1,63 @@
+/* -*- 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/>.
+ */
+
+#include "broadcast-strategy.hpp"
+
+namespace nfd {
+namespace fw {
+
+const Name BroadcastStrategy::STRATEGY_NAME("ndn:/localhost/nfd/strategy/broadcast/%FD%01");
+
+BroadcastStrategy::BroadcastStrategy(Forwarder& forwarder, const Name& name)
+  : Strategy(forwarder, name)
+{
+}
+
+BroadcastStrategy::~BroadcastStrategy()
+{
+}
+
+void
+BroadcastStrategy::afterReceiveInterest(const Face& inFace,
+                   const Interest& interest,
+                   shared_ptr<fib::Entry> fibEntry,
+                   shared_ptr<pit::Entry> pitEntry)
+{
+  const fib::NextHopList& nexthops = fibEntry->getNextHops();
+
+  for (fib::NextHopList::const_iterator it = nexthops.begin(); it != nexthops.end(); ++it) {
+    shared_ptr<Face> outFace = it->getFace();
+    if (pitEntry->canForwardTo(*outFace)) {
+      this->sendInterest(pitEntry, outFace);
+    }
+  }
+
+  if (!pitEntry->hasUnexpiredOutRecords()) {
+    this->rejectPendingInterest(pitEntry);
+  }
+}
+
+} // namespace fw
+} // namespace nfd
diff --git a/NFD/daemon/fw/broadcast-strategy.hpp b/NFD/daemon/fw/broadcast-strategy.hpp
new file mode 100644
index 0000000..0807bde
--- /dev/null
+++ b/NFD/daemon/fw/broadcast-strategy.hpp
@@ -0,0 +1,59 @@
+/* -*- 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_DAEMON_FW_BROADCAST_STRATEGY_HPP
+#define NFD_DAEMON_FW_BROADCAST_STRATEGY_HPP
+
+#include "strategy.hpp"
+
+namespace nfd {
+namespace fw {
+
+/** \class BroadcastStrategy
+ *  \brief a forwarding strategy that forwards Interest
+ *         to all nexthops
+ */
+class BroadcastStrategy : public Strategy
+{
+public:
+  BroadcastStrategy(Forwarder& forwarder, const Name& name = STRATEGY_NAME);
+
+  virtual
+  ~BroadcastStrategy();
+
+  virtual void
+  afterReceiveInterest(const Face& inFace,
+                       const Interest& interest,
+                       shared_ptr<fib::Entry> fibEntry,
+                       shared_ptr<pit::Entry> pitEntry) DECL_OVERRIDE;
+
+public:
+  static const Name STRATEGY_NAME;
+};
+
+} // namespace fw
+} // namespace nfd
+
+#endif // NFD_DAEMON_FW_BROADCAST_STRATEGY_HPP
diff --git a/NFD/daemon/fw/client-control-strategy.cpp b/NFD/daemon/fw/client-control-strategy.cpp
new file mode 100644
index 0000000..d3700e3
--- /dev/null
+++ b/NFD/daemon/fw/client-control-strategy.cpp
@@ -0,0 +1,74 @@
+/* -*- 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/>.
+ */
+
+#include "client-control-strategy.hpp"
+#include "core/logger.hpp"
+
+namespace nfd {
+namespace fw {
+
+NFD_LOG_INIT("ClientControlStrategy");
+
+const Name
+ClientControlStrategy::STRATEGY_NAME("ndn:/localhost/nfd/strategy/client-control/%FD%01");
+
+ClientControlStrategy::ClientControlStrategy(Forwarder& forwarder, const Name& name)
+  : BestRouteStrategy(forwarder, name)
+{
+}
+
+ClientControlStrategy::~ClientControlStrategy()
+{
+}
+
+void
+ClientControlStrategy::afterReceiveInterest(const Face& inFace,
+                                            const Interest& interest,
+                                            shared_ptr<fib::Entry> fibEntry,
+                                            shared_ptr<pit::Entry> pitEntry)
+{
+  // Strategy needn't check whether LocalControlHeader-NextHopFaceId is enabled.
+  // LocalFace does this check.
+  if (!interest.getLocalControlHeader().hasNextHopFaceId()) {
+    this->BestRouteStrategy::afterReceiveInterest(inFace, interest, fibEntry, pitEntry);
+    return;
+  }
+
+  FaceId outFaceId = static_cast<FaceId>(interest.getNextHopFaceId());
+  shared_ptr<Face> outFace = this->getFace(outFaceId);
+  if (!static_cast<bool>(outFace)) {
+    // If outFace doesn't exist, it's better to reject the Interest
+    // than to use BestRouteStrategy.
+    NFD_LOG_WARN("Interest " << interest.getName() <<
+                 " NextHopFaceId=" << outFaceId << " non-existent face");
+    this->rejectPendingInterest(pitEntry);
+    return;
+  }
+
+  this->sendInterest(pitEntry, outFace);
+}
+
+} // namespace fw
+} // namespace nfd
diff --git a/NFD/daemon/fw/client-control-strategy.hpp b/NFD/daemon/fw/client-control-strategy.hpp
new file mode 100644
index 0000000..a0ddbf2
--- /dev/null
+++ b/NFD/daemon/fw/client-control-strategy.hpp
@@ -0,0 +1,58 @@
+/* -*- 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_DAEMON_FW_CLIENT_CONTROL_STRATEGY_HPP
+#define NFD_DAEMON_FW_CLIENT_CONTROL_STRATEGY_HPP
+
+#include "best-route-strategy.hpp"
+
+namespace nfd {
+namespace fw {
+
+/** \brief a forwarding strategy that forwards Interests
+ *         according to NextHopFaceId field in LocalControlHeader
+ */
+class ClientControlStrategy : public BestRouteStrategy
+{
+public:
+  ClientControlStrategy(Forwarder& forwarder, const Name& name = STRATEGY_NAME);
+
+  virtual
+  ~ClientControlStrategy();
+
+  virtual void
+  afterReceiveInterest(const Face& inFace,
+                       const Interest& interest,
+                       shared_ptr<fib::Entry> fibEntry,
+                       shared_ptr<pit::Entry> pitEntry) DECL_OVERRIDE;
+
+public:
+  static const Name STRATEGY_NAME;
+};
+
+} // namespace fw
+} // namespace nfd
+
+#endif // NFD_DAEMON_FW_CLIENT_CONTROL_STRATEGY_HPP
diff --git a/NFD/daemon/fw/face-table.cpp b/NFD/daemon/fw/face-table.cpp
new file mode 100644
index 0000000..f1db53b
--- /dev/null
+++ b/NFD/daemon/fw/face-table.cpp
@@ -0,0 +1,138 @@
+/* -*- 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/>.
+ */
+
+#include "face-table.hpp"
+#include "forwarder.hpp"
+#include "core/logger.hpp"
+
+namespace nfd {
+
+NFD_LOG_INIT("FaceTable");
+
+FaceTable::FaceTable(Forwarder& forwarder)
+  : m_forwarder(forwarder)
+  , m_lastFaceId(FACEID_RESERVED_MAX)
+{
+}
+
+FaceTable::~FaceTable()
+{
+
+}
+
+shared_ptr<Face>
+FaceTable::get(FaceId id) const
+{
+  std::map<FaceId, shared_ptr<Face> >::const_iterator i = m_faces.find(id);
+  return (i == m_faces.end()) ? (shared_ptr<Face>()) : (i->second);
+}
+
+size_t
+FaceTable::size() const
+{
+  return m_faces.size();
+}
+
+void
+FaceTable::add(shared_ptr<Face> face)
+{
+  if (face->getId() != INVALID_FACEID && m_faces.count(face->getId()) > 0) {
+    NFD_LOG_WARN("Trying to add existing face id=" << face->getId() << " to the face table");
+    return;
+  }
+
+  FaceId faceId = ++m_lastFaceId;
+  BOOST_ASSERT(faceId > FACEID_RESERVED_MAX);
+  this->addImpl(face, faceId);
+}
+
+void
+FaceTable::addReserved(shared_ptr<Face> face, FaceId faceId)
+{
+  BOOST_ASSERT(face->getId() == INVALID_FACEID);
+  BOOST_ASSERT(m_faces.count(face->getId()) == 0);
+  BOOST_ASSERT(faceId <= FACEID_RESERVED_MAX);
+  this->addImpl(face, faceId);
+}
+
+void
+FaceTable::addImpl(shared_ptr<Face> face, FaceId faceId)
+{
+  face->setId(faceId);
+  m_faces[faceId] = face;
+  NFD_LOG_INFO("Added face id=" << faceId << " remote=" << face->getRemoteUri()
+                                          << " local=" << face->getLocalUri());
+
+  face->onReceiveInterest += bind(&Forwarder::onInterest,
+                                  &m_forwarder, ref(*face), _1);
+  face->onReceiveData     += bind(&Forwarder::onData,
+                                  &m_forwarder, ref(*face), _1);
+  face->onFail            += bind(&FaceTable::remove,
+                                  this, face);
+
+  this->onAdd(face);
+}
+
+void
+FaceTable::remove(shared_ptr<Face> face)
+{
+  this->onRemove(face);
+
+  FaceId faceId = face->getId();
+  m_faces.erase(faceId);
+  face->setId(INVALID_FACEID);
+  NFD_LOG_INFO("Removed face id=" << faceId << " remote=" << face->getRemoteUri() <<
+                                                 " local=" << face->getLocalUri());
+
+  // XXX This clears all subscriptions, because EventEmitter
+  //     does not support only removing Forwarder's subscription
+  face->onReceiveInterest.clear();
+  face->onReceiveData    .clear();
+  face->onSendInterest   .clear();
+  face->onSendData       .clear();
+  // don't clear onFail because other functions may need to execute
+
+  m_forwarder.getFib().removeNextHopFromAllEntries(face);
+}
+
+FaceTable::ForwardRange
+FaceTable::getForwardRange() const
+{
+  return m_faces | boost::adaptors::map_values;
+}
+
+FaceTable::const_iterator
+FaceTable::begin() const
+{
+  return this->getForwardRange().begin();
+}
+
+FaceTable::const_iterator
+FaceTable::end() const
+{
+  return this->getForwardRange().end();
+}
+
+} // namespace nfd
diff --git a/NFD/daemon/fw/face-table.hpp b/NFD/daemon/fw/face-table.hpp
new file mode 100644
index 0000000..435999e
--- /dev/null
+++ b/NFD/daemon/fw/face-table.hpp
@@ -0,0 +1,106 @@
+/* -*- 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_DAEMON_FW_FACE_TABLE_HPP
+#define NFD_DAEMON_FW_FACE_TABLE_HPP
+
+#include "face/face.hpp"
+#include <boost/range/adaptor/map.hpp>
+
+namespace nfd {
+
+class Forwarder;
+
+/** \brief container of all Faces
+ */
+class FaceTable : noncopyable
+{
+public:
+  explicit
+  FaceTable(Forwarder& forwarder);
+
+  VIRTUAL_WITH_TESTS
+  ~FaceTable();
+
+  VIRTUAL_WITH_TESTS void
+  add(shared_ptr<Face> face);
+
+  /// add a special Face with a reserved FaceId
+  VIRTUAL_WITH_TESTS void
+  addReserved(shared_ptr<Face> face, FaceId faceId);
+
+  VIRTUAL_WITH_TESTS shared_ptr<Face>
+  get(FaceId id) const;
+
+  size_t
+  size() const;
+
+public: // enumeration
+  typedef std::map<FaceId, shared_ptr<Face>> FaceMap;
+
+  typedef boost::select_second_const_range<FaceMap> ForwardRange;
+
+  /** \brief ForwardIterator for shared_ptr<Face>
+   */
+  typedef boost::range_iterator<ForwardRange>::type const_iterator;
+
+  const_iterator
+  begin() const;
+
+  const_iterator
+  end() const;
+
+public: // signals
+  /** \brief fires after a Face is added
+   */
+  signal::Signal<FaceTable, shared_ptr<Face>> onAdd;
+
+  /** \brief fires before a Face is removed
+   *
+   *  FaceId is valid when this event is fired
+   */
+  signal::Signal<FaceTable, shared_ptr<Face>> onRemove;
+
+private:
+  void
+  addImpl(shared_ptr<Face> face, FaceId faceId);
+
+  // remove is private because it's a subscriber of face.onFail event.
+  // face->close() closes a face and triggers .remove(face)
+  void
+  remove(shared_ptr<Face> face);
+
+  ForwardRange
+  getForwardRange() const;
+
+private:
+  Forwarder& m_forwarder;
+  FaceId m_lastFaceId;
+  FaceMap m_faces;
+};
+
+} // namespace nfd
+
+#endif // NFD_DAEMON_FW_FACE_TABLE_HPP
diff --git a/NFD/daemon/fw/forwarder-counters.hpp b/NFD/daemon/fw/forwarder-counters.hpp
new file mode 100644
index 0000000..ba3b57c
--- /dev/null
+++ b/NFD/daemon/fw/forwarder-counters.hpp
@@ -0,0 +1,51 @@
+/* -*- 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_DAEMON_FW_FORWARDER_COUNTERS_HPP
+#define NFD_DAEMON_FW_FORWARDER_COUNTERS_HPP
+
+#include "face/face-counters.hpp"
+
+namespace nfd {
+
+/** \brief contains counters on forwarder
+ */
+class ForwarderCounters : public NetworkLayerCounters
+{
+public:
+  /** \brief copy current obseverations to a struct
+   *  \param recipient an object with set methods for counters
+   */
+  template<typename R>
+  void
+  copyTo(R& recipient) const
+  {
+    this->NetworkLayerCounters::copyTo(recipient);
+  }
+};
+
+} // namespace nfd
+
+#endif // NFD_DAEMON_FW_FORWARDER_COUNTERS_HPP
diff --git a/NFD/daemon/fw/forwarder.cpp b/NFD/daemon/fw/forwarder.cpp
new file mode 100644
index 0000000..1cd1d16
--- /dev/null
+++ b/NFD/daemon/fw/forwarder.cpp
@@ -0,0 +1,445 @@
+/* -*- 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/>.
+ */
+
+#include "forwarder.hpp"
+#include "core/logger.hpp"
+#include "core/random.hpp"
+#include "available-strategies.hpp"
+#include <boost/random/uniform_int_distribution.hpp>
+
+namespace nfd {
+
+NFD_LOG_INIT("Forwarder");
+
+using fw::Strategy;
+
+const Name Forwarder::LOCALHOST_NAME("ndn:/localhost");
+
+Forwarder::Forwarder()
+  : m_faceTable(*this)
+  , m_fib(m_nameTree)
+  , m_pit(m_nameTree)
+  , m_measurements(m_nameTree)
+  , m_strategyChoice(m_nameTree, fw::makeDefaultStrategy(*this))
+{
+  fw::installStrategies(*this);
+}
+
+Forwarder::~Forwarder()
+{
+
+}
+
+void
+Forwarder::onIncomingInterest(Face& inFace, const Interest& interest)
+{
+  // receive Interest
+  NFD_LOG_DEBUG("onIncomingInterest face=" << inFace.getId() <<
+                " interest=" << interest.getName());
+  const_cast<Interest&>(interest).setIncomingFaceId(inFace.getId());
+  ++m_counters.getNInInterests();
+
+  // /localhost scope control
+  bool isViolatingLocalhost = !inFace.isLocal() &&
+                              LOCALHOST_NAME.isPrefixOf(interest.getName());
+  if (isViolatingLocalhost) {
+    NFD_LOG_DEBUG("onIncomingInterest face=" << inFace.getId() <<
+                  " interest=" << interest.getName() << " violates /localhost");
+    // (drop)
+    return;
+  }
+
+  // PIT insert
+  shared_ptr<pit::Entry> pitEntry = m_pit.insert(interest).first;
+
+  // detect duplicate Nonce
+  int dnw = pitEntry->findNonce(interest.getNonce(), inFace);
+  bool hasDuplicateNonce = (dnw != pit::DUPLICATE_NONCE_NONE) ||
+                           m_deadNonceList.has(interest.getName(), interest.getNonce());
+  if (hasDuplicateNonce) {
+    // goto Interest loop pipeline
+    this->onInterestLoop(inFace, interest, pitEntry);
+    return;
+  }
+
+  // cancel unsatisfy & straggler timer
+  this->cancelUnsatisfyAndStragglerTimer(pitEntry);
+
+  // is pending?
+  const pit::InRecordCollection& inRecords = pitEntry->getInRecords();
+  bool isPending = inRecords.begin() != inRecords.end();
+  if (!isPending) {
+    // CS lookup
+    const Data* csMatch = m_cs.find(interest);
+    if (csMatch != 0) {
+      const_cast<Data*>(csMatch)->setIncomingFaceId(FACEID_CONTENT_STORE);
+      // XXX should we lookup PIT for other Interests that also match csMatch?
+
+      // set PIT straggler timer
+      this->setStragglerTimer(pitEntry, true, csMatch->getFreshnessPeriod());
+
+      // goto outgoing Data pipeline
+      this->onOutgoingData(*csMatch, inFace);
+      return;
+    }
+  }
+
+  // insert InRecord
+  pitEntry->insertOrUpdateInRecord(inFace.shared_from_this(), interest);
+
+  // set PIT unsatisfy timer
+  this->setUnsatisfyTimer(pitEntry);
+
+  // FIB lookup
+  shared_ptr<fib::Entry> fibEntry = m_fib.findLongestPrefixMatch(*pitEntry);
+
+  // dispatch to strategy
+  this->dispatchToStrategy(pitEntry, bind(&Strategy::afterReceiveInterest, _1,
+                                          cref(inFace), cref(interest), fibEntry, pitEntry));
+}
+
+void
+Forwarder::onInterestLoop(Face& inFace, const Interest& interest,
+                          shared_ptr<pit::Entry> pitEntry)
+{
+  NFD_LOG_DEBUG("onInterestLoop face=" << inFace.getId() <<
+                " interest=" << interest.getName());
+
+  // (drop)
+}
+
+/** \brief compare two InRecords for picking outgoing Interest
+ *  \return true if b is preferred over a
+ *
+ *  This function should be passed to std::max_element over InRecordCollection.
+ *  The outgoing Interest picked is the last incoming Interest
+ *  that does not come from outFace.
+ *  If all InRecords come from outFace, it's fine to pick that. This happens when
+ *  there's only one InRecord that comes from outFace. The legit use is for
+ *  vehicular network; otherwise, strategy shouldn't send to the sole inFace.
+ */
+static inline bool
+compare_pickInterest(const pit::InRecord& a, const pit::InRecord& b, const Face* outFace)
+{
+  bool isOutFaceA = a.getFace().get() == outFace;
+  bool isOutFaceB = b.getFace().get() == outFace;
+
+  if (!isOutFaceA && isOutFaceB) {
+    return false;
+  }
+  if (isOutFaceA && !isOutFaceB) {
+    return true;
+  }
+
+  return a.getLastRenewed() > b.getLastRenewed();
+}
+
+void
+Forwarder::onOutgoingInterest(shared_ptr<pit::Entry> pitEntry, Face& outFace,
+                              bool wantNewNonce)
+{
+  if (outFace.getId() == INVALID_FACEID) {
+    NFD_LOG_WARN("onOutgoingInterest face=invalid interest=" << pitEntry->getName());
+    return;
+  }
+  NFD_LOG_DEBUG("onOutgoingInterest face=" << outFace.getId() <<
+                " interest=" << pitEntry->getName());
+
+  // scope control
+  if (pitEntry->violatesScope(outFace)) {
+    NFD_LOG_DEBUG("onOutgoingInterest face=" << outFace.getId() <<
+                  " interest=" << pitEntry->getName() << " violates scope");
+    return;
+  }
+
+  // pick Interest
+  const pit::InRecordCollection& inRecords = pitEntry->getInRecords();
+  pit::InRecordCollection::const_iterator pickedInRecord = std::max_element(
+    inRecords.begin(), inRecords.end(), bind(&compare_pickInterest, _1, _2, &outFace));
+  BOOST_ASSERT(pickedInRecord != inRecords.end());
+  shared_ptr<Interest> interest = const_pointer_cast<Interest>(
+    pickedInRecord->getInterest().shared_from_this());
+
+  if (wantNewNonce) {
+    interest = make_shared<Interest>(*interest);
+    static boost::random::uniform_int_distribution<uint32_t> dist;
+    interest->setNonce(dist(getGlobalRng()));
+  }
+
+  // insert OutRecord
+  pitEntry->insertOrUpdateOutRecord(outFace.shared_from_this(), *interest);
+
+  // send Interest
+  outFace.sendInterest(*interest);
+  ++m_counters.getNOutInterests();
+}
+
+void
+Forwarder::onInterestReject(shared_ptr<pit::Entry> pitEntry)
+{
+  if (pitEntry->hasUnexpiredOutRecords()) {
+    NFD_LOG_ERROR("onInterestReject interest=" << pitEntry->getName() <<
+                  " cannot reject forwarded Interest");
+    return;
+  }
+  NFD_LOG_DEBUG("onInterestReject interest=" << pitEntry->getName());
+
+  // cancel unsatisfy & straggler timer
+  this->cancelUnsatisfyAndStragglerTimer(pitEntry);
+
+  // set PIT straggler timer
+  this->setStragglerTimer(pitEntry, false);
+}
+
+void
+Forwarder::onInterestUnsatisfied(shared_ptr<pit::Entry> pitEntry)
+{
+  NFD_LOG_DEBUG("onInterestUnsatisfied interest=" << pitEntry->getName());
+
+  // invoke PIT unsatisfied callback
+  this->dispatchToStrategy(pitEntry, bind(&Strategy::beforeExpirePendingInterest, _1,
+                                          pitEntry));
+
+  // goto Interest Finalize pipeline
+  this->onInterestFinalize(pitEntry, false);
+}
+
+void
+Forwarder::onInterestFinalize(shared_ptr<pit::Entry> pitEntry, bool isSatisfied,
+                              const time::milliseconds& dataFreshnessPeriod)
+{
+  NFD_LOG_DEBUG("onInterestFinalize interest=" << pitEntry->getName() <<
+                (isSatisfied ? " satisfied" : " unsatisfied"));
+
+  // Dead Nonce List insert if necessary
+  this->insertDeadNonceList(*pitEntry, isSatisfied, dataFreshnessPeriod, 0);
+
+  // PIT delete
+  this->cancelUnsatisfyAndStragglerTimer(pitEntry);
+  m_pit.erase(pitEntry);
+}
+
+void
+Forwarder::onIncomingData(Face& inFace, const Data& data)
+{
+  // receive Data
+  NFD_LOG_DEBUG("onIncomingData face=" << inFace.getId() << " data=" << data.getName());
+  const_cast<Data&>(data).setIncomingFaceId(inFace.getId());
+  ++m_counters.getNInDatas();
+
+  // /localhost scope control
+  bool isViolatingLocalhost = !inFace.isLocal() &&
+                              LOCALHOST_NAME.isPrefixOf(data.getName());
+  if (isViolatingLocalhost) {
+    NFD_LOG_DEBUG("onIncomingData face=" << inFace.getId() <<
+                  " data=" << data.getName() << " violates /localhost");
+    // (drop)
+    return;
+  }
+
+  // PIT match
+  pit::DataMatchResult pitMatches = m_pit.findAllDataMatches(data);
+  if (pitMatches.begin() == pitMatches.end()) {
+    // goto Data unsolicited pipeline
+    this->onDataUnsolicited(inFace, data);
+    return;
+  }
+
+  // CS insert
+  m_cs.insert(data);
+
+  std::set<shared_ptr<Face> > pendingDownstreams;
+  // foreach PitEntry
+  for (const shared_ptr<pit::Entry>& pitEntry : pitMatches) {
+    NFD_LOG_DEBUG("onIncomingData matching=" << pitEntry->getName());
+
+    // cancel unsatisfy & straggler timer
+    this->cancelUnsatisfyAndStragglerTimer(pitEntry);
+
+    // remember pending downstreams
+    const pit::InRecordCollection& inRecords = pitEntry->getInRecords();
+    for (pit::InRecordCollection::const_iterator it = inRecords.begin();
+                                                 it != inRecords.end(); ++it) {
+      if (it->getExpiry() > time::steady_clock::now()) {
+        pendingDownstreams.insert(it->getFace());
+      }
+    }
+
+    // invoke PIT satisfy callback
+    this->dispatchToStrategy(pitEntry, bind(&Strategy::beforeSatisfyInterest, _1,
+                                            pitEntry, cref(inFace), cref(data)));
+
+    // Dead Nonce List insert if necessary (for OutRecord of inFace)
+    this->insertDeadNonceList(*pitEntry, true, data.getFreshnessPeriod(), &inFace);
+
+    // mark PIT satisfied
+    pitEntry->deleteInRecords();
+    pitEntry->deleteOutRecord(inFace);
+
+    // set PIT straggler timer
+    this->setStragglerTimer(pitEntry, true, data.getFreshnessPeriod());
+  }
+
+  // foreach pending downstream
+  for (std::set<shared_ptr<Face> >::iterator it = pendingDownstreams.begin();
+      it != pendingDownstreams.end(); ++it) {
+    shared_ptr<Face> pendingDownstream = *it;
+    if (pendingDownstream.get() == &inFace) {
+      continue;
+    }
+    // goto outgoing Data pipeline
+    this->onOutgoingData(data, *pendingDownstream);
+  }
+}
+
+void
+Forwarder::onDataUnsolicited(Face& inFace, const Data& data)
+{
+  // accept to cache?
+  bool acceptToCache = inFace.isLocal();
+  if (acceptToCache) {
+    // CS insert
+    m_cs.insert(data, true);
+  }
+
+  NFD_LOG_DEBUG("onDataUnsolicited face=" << inFace.getId() <<
+                " data=" << data.getName() <<
+                (acceptToCache ? " cached" : " not cached"));
+}
+
+void
+Forwarder::onOutgoingData(const Data& data, Face& outFace)
+{
+  if (outFace.getId() == INVALID_FACEID) {
+    NFD_LOG_WARN("onOutgoingData face=invalid data=" << data.getName());
+    return;
+  }
+  NFD_LOG_DEBUG("onOutgoingData face=" << outFace.getId() << " data=" << data.getName());
+
+  // /localhost scope control
+  bool isViolatingLocalhost = !outFace.isLocal() &&
+                              LOCALHOST_NAME.isPrefixOf(data.getName());
+  if (isViolatingLocalhost) {
+    NFD_LOG_DEBUG("onOutgoingData face=" << outFace.getId() <<
+                  " data=" << data.getName() << " violates /localhost");
+    // (drop)
+    return;
+  }
+
+  // TODO traffic manager
+
+  // send Data
+  outFace.sendData(data);
+  ++m_counters.getNOutDatas();
+}
+
+static inline bool
+compare_InRecord_expiry(const pit::InRecord& a, const pit::InRecord& b)
+{
+  return a.getExpiry() < b.getExpiry();
+}
+
+void
+Forwarder::setUnsatisfyTimer(shared_ptr<pit::Entry> pitEntry)
+{
+  const pit::InRecordCollection& inRecords = pitEntry->getInRecords();
+  pit::InRecordCollection::const_iterator lastExpiring =
+    std::max_element(inRecords.begin(), inRecords.end(),
+    &compare_InRecord_expiry);
+
+  time::steady_clock::TimePoint lastExpiry = lastExpiring->getExpiry();
+  time::nanoseconds lastExpiryFromNow = lastExpiry  - time::steady_clock::now();
+  if (lastExpiryFromNow <= time::seconds(0)) {
+    // TODO all InRecords are already expired; will this happen?
+  }
+
+  scheduler::cancel(pitEntry->m_unsatisfyTimer);
+  pitEntry->m_unsatisfyTimer = scheduler::schedule(lastExpiryFromNow,
+    bind(&Forwarder::onInterestUnsatisfied, this, pitEntry));
+}
+
+void
+Forwarder::setStragglerTimer(shared_ptr<pit::Entry> pitEntry, bool isSatisfied,
+                             const time::milliseconds& dataFreshnessPeriod)
+{
+  time::nanoseconds stragglerTime = time::milliseconds(100);
+
+  scheduler::cancel(pitEntry->m_stragglerTimer);
+  pitEntry->m_stragglerTimer = scheduler::schedule(stragglerTime,
+    bind(&Forwarder::onInterestFinalize, this, pitEntry, isSatisfied, dataFreshnessPeriod));
+}
+
+void
+Forwarder::cancelUnsatisfyAndStragglerTimer(shared_ptr<pit::Entry> pitEntry)
+{
+  scheduler::cancel(pitEntry->m_unsatisfyTimer);
+  scheduler::cancel(pitEntry->m_stragglerTimer);
+}
+
+static inline void
+insertNonceToDnl(DeadNonceList& dnl, const pit::Entry& pitEntry,
+                 const pit::OutRecord& outRecord)
+{
+  dnl.add(pitEntry.getName(), outRecord.getLastNonce());
+}
+
+void
+Forwarder::insertDeadNonceList(pit::Entry& pitEntry, bool isSatisfied,
+                               const time::milliseconds& dataFreshnessPeriod,
+                               Face* upstream)
+{
+  // need Dead Nonce List insert?
+  bool needDnl = false;
+  if (isSatisfied) {
+    bool hasFreshnessPeriod = dataFreshnessPeriod >= time::milliseconds::zero();
+    // Data never becomes stale if it doesn't have FreshnessPeriod field
+    needDnl = static_cast<bool>(pitEntry.getInterest().getMustBeFresh()) &&
+              (hasFreshnessPeriod && dataFreshnessPeriod < m_deadNonceList.getLifetime());
+  }
+  else {
+    needDnl = true;
+  }
+
+  if (!needDnl) {
+    return;
+  }
+
+  // Dead Nonce List insert
+  if (upstream == 0) {
+    // insert all outgoing Nonces
+    const pit::OutRecordCollection& outRecords = pitEntry.getOutRecords();
+    std::for_each(outRecords.begin(), outRecords.end(),
+                  bind(&insertNonceToDnl, ref(m_deadNonceList), cref(pitEntry), _1));
+  }
+  else {
+    // insert outgoing Nonce of a specific face
+    pit::OutRecordCollection::const_iterator outRecord = pitEntry.getOutRecord(*upstream);
+    if (outRecord != pitEntry.getOutRecords().end()) {
+      m_deadNonceList.add(pitEntry.getName(), outRecord->getLastNonce());
+    }
+  }
+}
+
+} // namespace nfd
diff --git a/NFD/daemon/fw/forwarder.hpp b/NFD/daemon/fw/forwarder.hpp
new file mode 100644
index 0000000..a753f15
--- /dev/null
+++ b/NFD/daemon/fw/forwarder.hpp
@@ -0,0 +1,301 @@
+/* -*- 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_DAEMON_FW_FORWARDER_HPP
+#define NFD_DAEMON_FW_FORWARDER_HPP
+
+#include "common.hpp"
+#include "core/scheduler.hpp"
+#include "forwarder-counters.hpp"
+#include "face-table.hpp"
+#include "table/fib.hpp"
+#include "table/pit.hpp"
+#include "table/cs.hpp"
+#include "table/measurements.hpp"
+#include "table/strategy-choice.hpp"
+#include "table/dead-nonce-list.hpp"
+
+namespace nfd {
+
+namespace fw {
+class Strategy;
+} // namespace fw
+
+/** \brief main class of NFD
+ *
+ *  Forwarder owns all faces and tables, and implements forwarding pipelines.
+ */
+class Forwarder
+{
+public:
+  Forwarder();
+
+  VIRTUAL_WITH_TESTS
+  ~Forwarder();
+
+  const ForwarderCounters&
+  getCounters() const;
+
+public: // faces
+  FaceTable&
+  getFaceTable();
+
+  /** \brief get existing Face
+   *
+   *  shortcut to .getFaceTable().get(face)
+   */
+  shared_ptr<Face>
+  getFace(FaceId id) const;
+
+  /** \brief add new Face
+   *
+   *  shortcut to .getFaceTable().add(face)
+   */
+  void
+  addFace(shared_ptr<Face> face);
+
+public: // forwarding entrypoints and tables
+  void
+  onInterest(Face& face, const Interest& interest);
+
+  void
+  onData(Face& face, const Data& data);
+
+  NameTree&
+  getNameTree();
+
+  Fib&
+  getFib();
+
+  Pit&
+  getPit();
+
+  Cs&
+  getCs();
+
+  Measurements&
+  getMeasurements();
+
+  StrategyChoice&
+  getStrategyChoice();
+
+  DeadNonceList&
+  getDeadNonceList();
+
+PUBLIC_WITH_TESTS_ELSE_PRIVATE: // pipelines
+  /** \brief incoming Interest pipeline
+   */
+  VIRTUAL_WITH_TESTS void
+  onIncomingInterest(Face& inFace, const Interest& interest);
+
+  /** \brief Interest loop pipeline
+   */
+  VIRTUAL_WITH_TESTS void
+  onInterestLoop(Face& inFace, const Interest& interest,
+                 shared_ptr<pit::Entry> pitEntry);
+
+  /** \brief outgoing Interest pipeline
+   */
+  VIRTUAL_WITH_TESTS void
+  onOutgoingInterest(shared_ptr<pit::Entry> pitEntry, Face& outFace,
+                     bool wantNewNonce = false);
+
+  /** \brief Interest reject pipeline
+   */
+  VIRTUAL_WITH_TESTS void
+  onInterestReject(shared_ptr<pit::Entry> pitEntry);
+
+  /** \brief Interest unsatisfied pipeline
+   */
+  VIRTUAL_WITH_TESTS void
+  onInterestUnsatisfied(shared_ptr<pit::Entry> pitEntry);
+
+  /** \brief Interest finalize pipeline
+   *  \param isSatisfied whether the Interest has been satisfied
+   *  \param dataFreshnessPeriod FreshnessPeriod of satisfying Data
+   */
+  VIRTUAL_WITH_TESTS void
+  onInterestFinalize(shared_ptr<pit::Entry> pitEntry, bool isSatisfied,
+                     const time::milliseconds& dataFreshnessPeriod = time::milliseconds(-1));
+
+  /** \brief incoming Data pipeline
+   */
+  VIRTUAL_WITH_TESTS void
+  onIncomingData(Face& inFace, const Data& data);
+
+  /** \brief Data unsolicited pipeline
+   */
+  VIRTUAL_WITH_TESTS void
+  onDataUnsolicited(Face& inFace, const Data& data);
+
+  /** \brief outgoing Data pipeline
+   */
+  VIRTUAL_WITH_TESTS void
+  onOutgoingData(const Data& data, Face& outFace);
+
+PROTECTED_WITH_TESTS_ELSE_PRIVATE:
+  VIRTUAL_WITH_TESTS void
+  setUnsatisfyTimer(shared_ptr<pit::Entry> pitEntry);
+
+  VIRTUAL_WITH_TESTS void
+  setStragglerTimer(shared_ptr<pit::Entry> pitEntry, bool isSatisfied,
+                    const time::milliseconds& dataFreshnessPeriod = time::milliseconds(-1));
+
+  VIRTUAL_WITH_TESTS void
+  cancelUnsatisfyAndStragglerTimer(shared_ptr<pit::Entry> pitEntry);
+
+  /** \brief insert Nonce to Dead Nonce List if necessary
+   *  \param upstream if null, insert Nonces from all OutRecords;
+   *                  if not null, insert Nonce only on the OutRecord of this face
+   */
+  VIRTUAL_WITH_TESTS void
+  insertDeadNonceList(pit::Entry& pitEntry, bool isSatisfied,
+                      const time::milliseconds& dataFreshnessPeriod,
+                      Face* upstream);
+
+  /// call trigger (method) on the effective strategy of pitEntry
+#ifdef WITH_TESTS
+  virtual void
+  dispatchToStrategy(shared_ptr<pit::Entry> pitEntry, function<void(fw::Strategy*)> trigger);
+#else
+  template<class Function>
+  void
+  dispatchToStrategy(shared_ptr<pit::Entry> pitEntry, Function trigger);
+#endif
+
+private:
+  ForwarderCounters m_counters;
+
+  FaceTable m_faceTable;
+
+  // tables
+  NameTree       m_nameTree;
+  Fib            m_fib;
+  Pit            m_pit;
+  Cs             m_cs;
+  Measurements   m_measurements;
+  StrategyChoice m_strategyChoice;
+  DeadNonceList  m_deadNonceList;
+
+  static const Name LOCALHOST_NAME;
+
+  // allow Strategy (base class) to enter pipelines
+  friend class fw::Strategy;
+};
+
+inline const ForwarderCounters&
+Forwarder::getCounters() const
+{
+  return m_counters;
+}
+
+inline FaceTable&
+Forwarder::getFaceTable()
+{
+  return m_faceTable;
+}
+
+inline shared_ptr<Face>
+Forwarder::getFace(FaceId id) const
+{
+  return m_faceTable.get(id);
+}
+
+inline void
+Forwarder::addFace(shared_ptr<Face> face)
+{
+  m_faceTable.add(face);
+}
+
+inline void
+Forwarder::onInterest(Face& face, const Interest& interest)
+{
+  this->onIncomingInterest(face, interest);
+}
+
+inline void
+Forwarder::onData(Face& face, const Data& data)
+{
+  this->onIncomingData(face, data);
+}
+
+inline NameTree&
+Forwarder::getNameTree()
+{
+  return m_nameTree;
+}
+
+inline Fib&
+Forwarder::getFib()
+{
+  return m_fib;
+}
+
+inline Pit&
+Forwarder::getPit()
+{
+  return m_pit;
+}
+
+inline Cs&
+Forwarder::getCs()
+{
+  return m_cs;
+}
+
+inline Measurements&
+Forwarder::getMeasurements()
+{
+  return m_measurements;
+}
+
+inline StrategyChoice&
+Forwarder::getStrategyChoice()
+{
+  return m_strategyChoice;
+}
+
+inline DeadNonceList&
+Forwarder::getDeadNonceList()
+{
+  return m_deadNonceList;
+}
+
+#ifdef WITH_TESTS
+inline void
+Forwarder::dispatchToStrategy(shared_ptr<pit::Entry> pitEntry, function<void(fw::Strategy*)> trigger)
+#else
+template<class Function>
+inline void
+Forwarder::dispatchToStrategy(shared_ptr<pit::Entry> pitEntry, Function trigger)
+#endif
+{
+  fw::Strategy& strategy = m_strategyChoice.findEffectiveStrategy(*pitEntry);
+  trigger(&strategy);
+}
+
+} // namespace nfd
+
+#endif // NFD_DAEMON_FW_FORWARDER_HPP
diff --git a/NFD/daemon/fw/ncc-strategy.cpp b/NFD/daemon/fw/ncc-strategy.cpp
new file mode 100644
index 0000000..e5aaab9
--- /dev/null
+++ b/NFD/daemon/fw/ncc-strategy.cpp
@@ -0,0 +1,314 @@
+/* -*- 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/>.
+ */
+
+#include "ncc-strategy.hpp"
+#include "core/random.hpp"
+#include <boost/random/uniform_int_distribution.hpp>
+
+namespace nfd {
+namespace fw {
+
+const Name NccStrategy::STRATEGY_NAME("ndn:/localhost/nfd/strategy/ncc/%FD%01");
+
+NccStrategy::NccStrategy(Forwarder& forwarder, const Name& name)
+  : Strategy(forwarder, name)
+{
+}
+
+NccStrategy::~NccStrategy()
+{
+}
+
+const time::microseconds NccStrategy::DEFER_FIRST_WITHOUT_BEST_FACE = time::microseconds(4000);
+const time::microseconds NccStrategy::DEFER_RANGE_WITHOUT_BEST_FACE = time::microseconds(75000);
+const time::nanoseconds NccStrategy::MEASUREMENTS_LIFETIME = time::seconds(16);
+
+void
+NccStrategy::afterReceiveInterest(const Face& inFace,
+                                  const Interest& interest,
+                                  shared_ptr<fib::Entry> fibEntry,
+                                  shared_ptr<pit::Entry> pitEntry)
+{
+  const fib::NextHopList& nexthops = fibEntry->getNextHops();
+  if (nexthops.size() == 0) {
+    this->rejectPendingInterest(pitEntry);
+    return;
+  }
+
+  shared_ptr<PitEntryInfo> pitEntryInfo =
+    pitEntry->getOrCreateStrategyInfo<PitEntryInfo>();
+  bool isNewPitEntry = !pitEntry->hasUnexpiredOutRecords();
+  if (!isNewPitEntry) {
+    return;
+  }
+
+  shared_ptr<MeasurementsEntryInfo> measurementsEntryInfo =
+    this->getMeasurementsEntryInfo(pitEntry);
+
+  time::microseconds deferFirst = DEFER_FIRST_WITHOUT_BEST_FACE;
+  time::microseconds deferRange = DEFER_RANGE_WITHOUT_BEST_FACE;
+  size_t nUpstreams = nexthops.size();
+
+  shared_ptr<Face> bestFace = measurementsEntryInfo->getBestFace();
+  if (static_cast<bool>(bestFace) && fibEntry->hasNextHop(bestFace) &&
+      pitEntry->canForwardTo(*bestFace)) {
+    // TODO Should we use `randlow = 100 + nrand48(h->seed) % 4096U;` ?
+    deferFirst = measurementsEntryInfo->prediction;
+    deferRange = time::microseconds((deferFirst.count() + 1) / 2);
+    --nUpstreams;
+    this->sendInterest(pitEntry, bestFace);
+    pitEntryInfo->bestFaceTimeout = scheduler::schedule(
+      measurementsEntryInfo->prediction,
+      bind(&NccStrategy::timeoutOnBestFace, this, weak_ptr<pit::Entry>(pitEntry)));
+  }
+  else {
+    // use first nexthop
+    this->sendInterest(pitEntry, nexthops.begin()->getFace());
+    // TODO avoid sending to inFace
+  }
+
+  shared_ptr<Face> previousFace = measurementsEntryInfo->previousFace.lock();
+  if (static_cast<bool>(previousFace) && fibEntry->hasNextHop(previousFace) &&
+      pitEntry->canForwardTo(*previousFace)) {
+    --nUpstreams;
+  }
+
+  if (nUpstreams > 0) {
+    pitEntryInfo->maxInterval = std::max(time::microseconds(1),
+      time::microseconds((2 * deferRange.count() + nUpstreams - 1) / nUpstreams));
+  }
+  else {
+    // Normally, maxInterval is unused if there aren't any face beyond best and previousBest.
+    // However, in case FIB entry gains a new nexthop before doPropagate executes (bug 1853),
+    // this maxInterval would be used to determine when the next doPropagate would happen.
+    pitEntryInfo->maxInterval = deferFirst;
+  }
+  pitEntryInfo->propagateTimer = scheduler::schedule(deferFirst,
+    bind(&NccStrategy::doPropagate, this,
+         weak_ptr<pit::Entry>(pitEntry), weak_ptr<fib::Entry>(fibEntry)));
+}
+
+void
+NccStrategy::doPropagate(weak_ptr<pit::Entry> pitEntryWeak, weak_ptr<fib::Entry> fibEntryWeak)
+{
+  shared_ptr<pit::Entry> pitEntry = pitEntryWeak.lock();
+  if (!static_cast<bool>(pitEntry)) {
+    return;
+  }
+  shared_ptr<fib::Entry> fibEntry = fibEntryWeak.lock();
+  if (!static_cast<bool>(fibEntry)) {
+    return;
+  }
+
+  shared_ptr<PitEntryInfo> pitEntryInfo = pitEntry->getStrategyInfo<PitEntryInfo>();
+  // pitEntryInfo is guaranteed to exist here, because doPropagate is triggered
+  // from a timer set by NccStrategy.
+  BOOST_ASSERT(static_cast<bool>(pitEntryInfo));
+
+  shared_ptr<MeasurementsEntryInfo> measurementsEntryInfo =
+    this->getMeasurementsEntryInfo(pitEntry);
+
+  shared_ptr<Face> previousFace = measurementsEntryInfo->previousFace.lock();
+  if (static_cast<bool>(previousFace) && fibEntry->hasNextHop(previousFace) &&
+      pitEntry->canForwardTo(*previousFace)) {
+    this->sendInterest(pitEntry, previousFace);
+  }
+
+  const fib::NextHopList& nexthops = fibEntry->getNextHops();
+  bool isForwarded = false;
+  for (fib::NextHopList::const_iterator it = nexthops.begin(); it != nexthops.end(); ++it) {
+    shared_ptr<Face> face = it->getFace();
+    if (pitEntry->canForwardTo(*face)) {
+      isForwarded = true;
+      this->sendInterest(pitEntry, face);
+      break;
+    }
+  }
+
+  if (isForwarded) {
+    boost::random::uniform_int_distribution<time::nanoseconds::rep> dist(0,
+      pitEntryInfo->maxInterval.count() - 1);
+    time::nanoseconds deferNext = time::nanoseconds(dist(getGlobalRng()));
+    pitEntryInfo->propagateTimer = scheduler::schedule(deferNext,
+    bind(&NccStrategy::doPropagate, this,
+         weak_ptr<pit::Entry>(pitEntry), weak_ptr<fib::Entry>(fibEntry)));
+  }
+}
+
+void
+NccStrategy::timeoutOnBestFace(weak_ptr<pit::Entry> pitEntryWeak)
+{
+  shared_ptr<pit::Entry> pitEntry = pitEntryWeak.lock();
+  if (!static_cast<bool>(pitEntry)) {
+    return;
+  }
+  shared_ptr<measurements::Entry> measurementsEntry = this->getMeasurements().get(*pitEntry);
+
+  for (int i = 0; i < UPDATE_MEASUREMENTS_N_LEVELS; ++i) {
+    if (!static_cast<bool>(measurementsEntry)) {
+      // going out of this strategy's namespace
+      return;
+    }
+    this->getMeasurements().extendLifetime(*measurementsEntry, MEASUREMENTS_LIFETIME);
+
+    shared_ptr<MeasurementsEntryInfo> measurementsEntryInfo =
+      this->getMeasurementsEntryInfo(measurementsEntry);
+    measurementsEntryInfo->adjustPredictUp();
+
+    measurementsEntry = this->getMeasurements().getParent(*measurementsEntry);
+  }
+}
+
+void
+NccStrategy::beforeSatisfyInterest(shared_ptr<pit::Entry> pitEntry,
+                                   const Face& inFace, const Data& data)
+{
+  if (pitEntry->getInRecords().empty()) {
+    // PIT entry has already been satisfied (and is now waiting for straggler timer to expire)
+    // NCC does not collect measurements for non-best face
+    return;
+  }
+
+  shared_ptr<measurements::Entry> measurementsEntry = this->getMeasurements().get(*pitEntry);
+
+  for (int i = 0; i < UPDATE_MEASUREMENTS_N_LEVELS; ++i) {
+    if (!static_cast<bool>(measurementsEntry)) {
+      // going out of this strategy's namespace
+      return;
+    }
+    this->getMeasurements().extendLifetime(*measurementsEntry, MEASUREMENTS_LIFETIME);
+
+    shared_ptr<MeasurementsEntryInfo> measurementsEntryInfo =
+      this->getMeasurementsEntryInfo(measurementsEntry);
+    measurementsEntryInfo->updateBestFace(inFace);
+
+    measurementsEntry = this->getMeasurements().getParent(*measurementsEntry);
+  }
+
+  shared_ptr<PitEntryInfo> pitEntryInfo = pitEntry->getStrategyInfo<PitEntryInfo>();
+  if (static_cast<bool>(pitEntryInfo)) {
+    scheduler::cancel(pitEntryInfo->propagateTimer);
+  }
+}
+
+shared_ptr<NccStrategy::MeasurementsEntryInfo>
+NccStrategy::getMeasurementsEntryInfo(shared_ptr<pit::Entry> entry)
+{
+  shared_ptr<measurements::Entry> measurementsEntry = this->getMeasurements().get(*entry);
+  return this->getMeasurementsEntryInfo(measurementsEntry);
+}
+
+shared_ptr<NccStrategy::MeasurementsEntryInfo>
+NccStrategy::getMeasurementsEntryInfo(shared_ptr<measurements::Entry> entry)
+{
+  shared_ptr<MeasurementsEntryInfo> info = entry->getStrategyInfo<MeasurementsEntryInfo>();
+  if (static_cast<bool>(info)) {
+    return info;
+  }
+
+  info = make_shared<MeasurementsEntryInfo>();
+  entry->setStrategyInfo(info);
+
+  shared_ptr<measurements::Entry> parentEntry = this->getMeasurements().getParent(*entry);
+  if (static_cast<bool>(parentEntry)) {
+    shared_ptr<MeasurementsEntryInfo> parentInfo = this->getMeasurementsEntryInfo(parentEntry);
+    BOOST_ASSERT(static_cast<bool>(parentInfo));
+    info->inheritFrom(*parentInfo);
+  }
+
+  return info;
+}
+
+
+const time::microseconds NccStrategy::MeasurementsEntryInfo::INITIAL_PREDICTION =
+                                                             time::microseconds(8192);
+const time::microseconds NccStrategy::MeasurementsEntryInfo::MIN_PREDICTION =
+                                                             time::microseconds(127);
+const time::microseconds NccStrategy::MeasurementsEntryInfo::MAX_PREDICTION =
+                                                             time::microseconds(160000);
+
+NccStrategy::MeasurementsEntryInfo::MeasurementsEntryInfo()
+  : prediction(INITIAL_PREDICTION)
+{
+}
+
+void
+NccStrategy::MeasurementsEntryInfo::inheritFrom(const MeasurementsEntryInfo& other)
+{
+  this->operator=(other);
+}
+
+shared_ptr<Face>
+NccStrategy::MeasurementsEntryInfo::getBestFace(void) {
+  shared_ptr<Face> best = this->bestFace.lock();
+  if (static_cast<bool>(best)) {
+    return best;
+  }
+  this->bestFace = best = this->previousFace.lock();
+  return best;
+}
+
+void
+NccStrategy::MeasurementsEntryInfo::updateBestFace(const Face& face) {
+  if (this->bestFace.expired()) {
+    this->bestFace = const_cast<Face&>(face).shared_from_this();
+    return;
+  }
+  shared_ptr<Face> bestFace = this->bestFace.lock();
+  if (bestFace.get() == &face) {
+    this->adjustPredictDown();
+  }
+  else {
+    this->previousFace = this->bestFace;
+    this->bestFace = const_cast<Face&>(face).shared_from_this();
+  }
+}
+
+void
+NccStrategy::MeasurementsEntryInfo::adjustPredictDown() {
+  prediction = std::max(MIN_PREDICTION,
+    time::microseconds(prediction.count() - (prediction.count() >> ADJUST_PREDICT_DOWN_SHIFT)));
+}
+
+void
+NccStrategy::MeasurementsEntryInfo::adjustPredictUp() {
+  prediction = std::min(MAX_PREDICTION,
+    time::microseconds(prediction.count() + (prediction.count() >> ADJUST_PREDICT_UP_SHIFT)));
+}
+
+void
+NccStrategy::MeasurementsEntryInfo::ageBestFace() {
+  this->previousFace = this->bestFace;
+  this->bestFace.reset();
+}
+
+NccStrategy::PitEntryInfo::~PitEntryInfo()
+{
+  scheduler::cancel(this->bestFaceTimeout);
+  scheduler::cancel(this->propagateTimer);
+}
+
+} // namespace fw
+} // namespace nfd
diff --git a/NFD/daemon/fw/ncc-strategy.hpp b/NFD/daemon/fw/ncc-strategy.hpp
new file mode 100644
index 0000000..14a4696
--- /dev/null
+++ b/NFD/daemon/fw/ncc-strategy.hpp
@@ -0,0 +1,148 @@
+/* -*- 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_DAEMON_FW_NCC_STRATEGY_HPP
+#define NFD_DAEMON_FW_NCC_STRATEGY_HPP
+
+#include "strategy.hpp"
+
+namespace nfd {
+namespace fw {
+
+/** \brief a forwarding strategy similar to CCNx 0.7.2
+ */
+class NccStrategy : public Strategy
+{
+public:
+  NccStrategy(Forwarder& forwarder, const Name& name = STRATEGY_NAME);
+
+  virtual
+  ~NccStrategy();
+
+  virtual void
+  afterReceiveInterest(const Face& inFace,
+                       const Interest& interest,
+                       shared_ptr<fib::Entry> fibEntry,
+                       shared_ptr<pit::Entry> pitEntry) DECL_OVERRIDE;
+
+  virtual void
+  beforeSatisfyInterest(shared_ptr<pit::Entry> pitEntry,
+                        const Face& inFace, const Data& data) DECL_OVERRIDE;
+
+protected:
+  /// StrategyInfo on measurements::Entry
+  class MeasurementsEntryInfo : public StrategyInfo
+  {
+  public:
+    static constexpr int
+    getTypeId()
+    {
+      return 1000;
+    }
+
+    MeasurementsEntryInfo();
+
+    void
+    inheritFrom(const MeasurementsEntryInfo& other);
+
+    shared_ptr<Face>
+    getBestFace();
+
+    void
+    updateBestFace(const Face& face);
+
+    void
+    adjustPredictUp();
+
+  private:
+    void
+    adjustPredictDown();
+
+    void
+    ageBestFace();
+
+  public:
+    weak_ptr<Face> bestFace;
+    weak_ptr<Face> previousFace;
+    time::microseconds prediction;
+
+    static const time::microseconds INITIAL_PREDICTION;
+    static const time::microseconds MIN_PREDICTION;
+    static const int ADJUST_PREDICT_DOWN_SHIFT = 7;
+    static const time::microseconds MAX_PREDICTION;
+    static const int ADJUST_PREDICT_UP_SHIFT = 3;
+  };
+
+  /// StrategyInfo on pit::Entry
+  class PitEntryInfo : public StrategyInfo
+  {
+  public:
+    static constexpr int
+    getTypeId()
+    {
+      return 1001;
+    }
+
+    virtual
+    ~PitEntryInfo();
+
+  public:
+    /// timer that expires when best face does not respond within predicted time
+    EventId bestFaceTimeout;
+    /// timer for propagating to another face
+    EventId propagateTimer;
+    /// maximum interval between forwarding to two nexthops except best and previous
+    time::microseconds maxInterval;
+  };
+
+protected:
+  shared_ptr<MeasurementsEntryInfo>
+  getMeasurementsEntryInfo(shared_ptr<measurements::Entry> entry);
+
+  shared_ptr<MeasurementsEntryInfo>
+  getMeasurementsEntryInfo(shared_ptr<pit::Entry> entry);
+
+  /// propagate to another upstream
+  void
+  doPropagate(weak_ptr<pit::Entry> pitEntryWeak, weak_ptr<fib::Entry> fibEntryWeak);
+
+  /// best face did not reply within prediction
+  void
+  timeoutOnBestFace(weak_ptr<pit::Entry> pitEntryWeak);
+
+public:
+  static const Name STRATEGY_NAME;
+
+protected:
+  static const time::microseconds DEFER_FIRST_WITHOUT_BEST_FACE;
+  static const time::microseconds DEFER_RANGE_WITHOUT_BEST_FACE;
+  static const int UPDATE_MEASUREMENTS_N_LEVELS = 2;
+  static const time::nanoseconds MEASUREMENTS_LIFETIME;
+};
+
+} // namespace fw
+} // namespace nfd
+
+#endif // NFD_DAEMON_FW_NCC_STRATEGY_HPP
diff --git a/NFD/daemon/fw/rtt-estimator.cpp b/NFD/daemon/fw/rtt-estimator.cpp
new file mode 100644
index 0000000..97b2ab0
--- /dev/null
+++ b/NFD/daemon/fw/rtt-estimator.cpp
@@ -0,0 +1,79 @@
+/* -*- 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/>.
+ **/
+
+#include "rtt-estimator.hpp"
+
+namespace nfd {
+
+RttEstimator::RttEstimator(uint16_t maxMultiplier, Duration minRto, double gain)
+  : m_maxMultiplier(maxMultiplier)
+  , m_minRto(minRto.count())
+  , m_rtt(RttEstimator::getInitialRtt().count())
+  , m_gain(gain)
+  , m_variance(0)
+  , m_multiplier(1)
+  , m_nSamples(0)
+{
+}
+
+void
+RttEstimator::addMeasurement(Duration measure)
+{
+  double m = static_cast<double>(measure.count());
+  if (m_nSamples > 0) {
+    double err = m - m_rtt;
+    double gErr = err * m_gain;
+    m_rtt += gErr;
+    double difference = std::abs(err) - m_variance;
+    m_variance += difference * m_gain;
+  } else {
+    m_rtt = m;
+    m_variance = m;
+  }
+  ++m_nSamples;
+  m_multiplier = 1;
+}
+
+void
+RttEstimator::incrementMultiplier()
+{
+  m_multiplier = std::min(static_cast<uint16_t>(m_multiplier + 1), m_maxMultiplier);
+}
+
+void
+RttEstimator::doubleMultiplier()
+{
+  m_multiplier = std::min(static_cast<uint16_t>(m_multiplier * 2), m_maxMultiplier);
+}
+
+RttEstimator::Duration
+RttEstimator::computeRto() const
+{
+  double rto = std::max(m_minRto, m_rtt + 4 * m_variance);
+  rto *= m_multiplier;
+  return Duration(static_cast<Duration::rep>(rto));
+}
+
+} // namespace nfd
diff --git a/NFD/daemon/fw/rtt-estimator.hpp b/NFD/daemon/fw/rtt-estimator.hpp
new file mode 100644
index 0000000..a830c5a
--- /dev/null
+++ b/NFD/daemon/fw/rtt-estimator.hpp
@@ -0,0 +1,83 @@
+/* -*- 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_DAEMON_FW_RTT_ESTIMATOR_HPP
+#define NFD_DAEMON_FW_RTT_ESTIMATOR_HPP
+
+#include "common.hpp"
+
+namespace nfd {
+
+/**
+ * \brief implements the Mean-Deviation RTT estimator
+ *
+ * reference: ns3::RttMeanDeviation
+ *
+ * This RttEstimator algorithm is designed for TCP, which is a continuous stream.
+ * NDN Interest-Data traffic is not always a continuous stream,
+ * so NDN may need a different RttEstimator.
+ * The design of a more suitable RttEstimator is a research question.
+ */
+class RttEstimator
+{
+public:
+  typedef time::microseconds Duration;
+
+  static Duration
+  getInitialRtt(void)
+  {
+    return time::seconds(1);
+  }
+
+  RttEstimator(uint16_t maxMultiplier = 16,
+               Duration minRto = time::milliseconds(1),
+               double gain = 0.1);
+
+  void
+  addMeasurement(Duration measure);
+
+  void
+  incrementMultiplier();
+
+  void
+  doubleMultiplier();
+
+  Duration
+  computeRto() const;
+
+private:
+  uint16_t m_maxMultiplier;
+  double m_minRto;
+
+  double m_rtt;
+  double m_gain;
+  double m_variance;
+  uint16_t m_multiplier;
+  uint32_t m_nSamples;
+};
+
+} // namespace nfd
+
+#endif // NFD_DAEMON_FW_RTT_ESTIMATOR_HPP
diff --git a/NFD/daemon/fw/strategy-info.hpp b/NFD/daemon/fw/strategy-info.hpp
new file mode 100644
index 0000000..b701fc5
--- /dev/null
+++ b/NFD/daemon/fw/strategy-info.hpp
@@ -0,0 +1,62 @@
+/* -*- 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_DAEMON_FW_STRATEGY_INFO_HPP
+#define NFD_DAEMON_FW_STRATEGY_INFO_HPP
+
+#include "common.hpp"
+
+namespace nfd {
+namespace fw {
+
+/** \brief contains arbitrary information forwarding strategy places on table entries
+ */
+class StrategyInfo
+{
+public:
+  /** \fn static constexpr int getTypeId()
+   *  \return an integer that uniquely identifies this StrategyInfo type
+   *  \sa http://redmine.named-data.net/projects/nfd/wiki/StrategyInfoType
+   */
+  // static constexpr int
+  // getTypeId()
+  // {
+  //   return <type-identifier>;
+  // }
+
+  virtual
+  ~StrategyInfo();
+};
+
+
+inline
+StrategyInfo::~StrategyInfo()
+{
+}
+
+} // namespace fw
+} // namespace nfd
+
+#endif // NFD_DAEMON_FW_STRATEGY_INFO_HPP
diff --git a/NFD/daemon/fw/strategy.cpp b/NFD/daemon/fw/strategy.cpp
new file mode 100644
index 0000000..f1a16f5
--- /dev/null
+++ b/NFD/daemon/fw/strategy.cpp
@@ -0,0 +1,82 @@
+/* -*- 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/>.
+ */
+
+#include "strategy.hpp"
+#include "forwarder.hpp"
+#include "core/logger.hpp"
+
+namespace nfd {
+namespace fw {
+
+NFD_LOG_INIT("Strategy");
+
+Strategy::Strategy(Forwarder& forwarder, const Name& name)
+  : afterAddFace(forwarder.getFaceTable().onAdd)
+  , beforeRemoveFace(forwarder.getFaceTable().onRemove)
+  , m_name(name)
+  , m_forwarder(forwarder)
+  , m_measurements(m_forwarder.getMeasurements(),
+                   m_forwarder.getStrategyChoice(), this)
+{
+}
+
+Strategy::~Strategy()
+{
+}
+
+void
+Strategy::beforeSatisfyInterest(shared_ptr<pit::Entry> pitEntry,
+                                const Face& inFace, const Data& data)
+{
+  NFD_LOG_DEBUG("beforeSatisfyInterest pitEntry=" << pitEntry->getName() <<
+    " inFace=" << inFace.getId() << " data=" << data.getName());
+}
+
+void
+Strategy::beforeExpirePendingInterest(shared_ptr<pit::Entry> pitEntry)
+{
+  NFD_LOG_DEBUG("beforeExpirePendingInterest pitEntry=" << pitEntry->getName());
+}
+
+//void
+//Strategy::afterAddFibEntry(shared_ptr<fib::Entry> fibEntry)
+//{
+//  NFD_LOG_DEBUG("afterAddFibEntry fibEntry=" << fibEntry->getPrefix());
+//}
+//
+//void
+//Strategy::afterUpdateFibEntry(shared_ptr<fib::Entry> fibEntry)
+//{
+//  NFD_LOG_DEBUG("afterUpdateFibEntry fibEntry=" << fibEntry->getPrefix());
+//}
+//
+//void
+//Strategy::beforeRemoveFibEntry(shared_ptr<fib::Entry> fibEntry)
+//{
+//  NFD_LOG_DEBUG("beforeRemoveFibEntry fibEntry=" << fibEntry->getPrefix());
+//}
+
+} // namespace fw
+} // namespace nfd
diff --git a/NFD/daemon/fw/strategy.hpp b/NFD/daemon/fw/strategy.hpp
new file mode 100644
index 0000000..813cce0
--- /dev/null
+++ b/NFD/daemon/fw/strategy.hpp
@@ -0,0 +1,194 @@
+/* -*- 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_DAEMON_FW_STRATEGY_HPP
+#define NFD_DAEMON_FW_STRATEGY_HPP
+
+#include "forwarder.hpp"
+#include "table/measurements-accessor.hpp"
+
+namespace nfd {
+namespace fw {
+
+/** \brief represents a forwarding strategy
+ */
+class Strategy : public enable_shared_from_this<Strategy>, noncopyable
+{
+public:
+  /** \brief construct a strategy instance
+   *  \param forwarder a reference to the Forwarder, used to enable actions and accessors.
+   *         Strategy subclasses should pass this reference,
+   *         and should not keep a reference themselves.
+   *  \param name the strategy Name.
+   *         It's recommended to include a version number as the last component.
+   */
+  Strategy(Forwarder& forwarder, const Name& name);
+
+  virtual
+  ~Strategy();
+
+  /// a Name that represent the Strategy program
+  const Name&
+  getName() const;
+
+public: // triggers
+  /** \brief trigger after Interest is received
+   *
+   *  The Interest:
+   *  - does not violate Scope
+   *  - is not looped
+   *  - cannot be satisfied by ContentStore
+   *  - is under a namespace managed by this strategy
+   *
+   *  The strategy should decide whether and where to forward this Interest.
+   *  - If the strategy decides to forward this Interest,
+   *    invoke this->sendInterest one or more times, either now or shortly after
+   *  - If strategy concludes that this Interest cannot be forwarded,
+   *    invoke this->rejectPendingInterest so that PIT entry will be deleted shortly
+   *
+   *  \note The strategy is permitted to store a weak reference to fibEntry.
+   *        Do not store a shared reference, because PIT entry may be deleted at any moment.
+   *        fibEntry is passed by value to allow obtaining a weak reference from it.
+   *  \note The strategy is permitted to store a shared reference to pitEntry.
+   *        pitEntry is passed by value to reflect this fact.
+   */
+  virtual void
+  afterReceiveInterest(const Face& inFace,
+                       const Interest& interest,
+                       shared_ptr<fib::Entry> fibEntry,
+                       shared_ptr<pit::Entry> pitEntry) = 0;
+
+  /** \brief trigger before PIT entry is satisfied
+   *
+   *  This trigger is invoked when an incoming Data satisfies the PIT entry.
+   *  It can be invoked even if the PIT entry has already been satisfied.
+   *
+   *  In this base class this method does nothing.
+   *
+   *  \note The strategy is permitted to store a shared reference to pitEntry.
+   *        pitEntry is passed by value to reflect this fact.
+   */
+  virtual void
+  beforeSatisfyInterest(shared_ptr<pit::Entry> pitEntry,
+                        const Face& inFace, const Data& data);
+
+  /** \brief trigger before PIT entry expires
+   *
+   *  PIT entry expires when InterestLifetime has elapsed for all InRecords,
+   *  and it is not satisfied by an incoming Data.
+   *
+   *  This trigger is not invoked for PIT entry already satisfied.
+   *
+   *  In this base class this method does nothing.
+   *
+   *  \note The strategy is permitted to store a shared reference to pitEntry.
+   *        pitEntry is passed by value to reflect this fact.
+   */
+  virtual void
+  beforeExpirePendingInterest(shared_ptr<pit::Entry> pitEntry);
+
+protected: // actions
+  /// send Interest to outFace
+  VIRTUAL_WITH_TESTS void
+  sendInterest(shared_ptr<pit::Entry> pitEntry,
+               shared_ptr<Face> outFace,
+               bool wantNewNonce = false);
+
+  /** \brief decide that a pending Interest cannot be forwarded
+   *
+   *  This shall not be called if the pending Interest has been
+   *  forwarded earlier, and does not need to be resent now.
+   */
+  VIRTUAL_WITH_TESTS void
+  rejectPendingInterest(shared_ptr<pit::Entry> pitEntry);
+
+protected: // accessors
+  MeasurementsAccessor&
+  getMeasurements();
+
+  shared_ptr<Face>
+  getFace(FaceId id);
+
+  const FaceTable&
+  getFaceTable();
+
+protected: // accessors
+  signal::Signal<FaceTable, shared_ptr<Face>>& afterAddFace;
+  signal::Signal<FaceTable, shared_ptr<Face>>& beforeRemoveFace;
+
+private:
+  Name m_name;
+
+  /** \brief reference to the forwarder
+   *
+   *  Triggers can access forwarder indirectly via actions.
+   */
+  Forwarder& m_forwarder;
+
+  MeasurementsAccessor m_measurements;
+};
+
+inline const Name&
+Strategy::getName() const
+{
+  return m_name;
+}
+
+inline void
+Strategy::sendInterest(shared_ptr<pit::Entry> pitEntry,
+                       shared_ptr<Face> outFace,
+                       bool wantNewNonce)
+{
+  m_forwarder.onOutgoingInterest(pitEntry, *outFace, wantNewNonce);
+}
+
+inline void
+Strategy::rejectPendingInterest(shared_ptr<pit::Entry> pitEntry)
+{
+  m_forwarder.onInterestReject(pitEntry);
+}
+
+inline MeasurementsAccessor&
+Strategy::getMeasurements()
+{
+  return m_measurements;
+}
+
+inline shared_ptr<Face>
+Strategy::getFace(FaceId id)
+{
+  return m_forwarder.getFace(id);
+}
+
+inline const FaceTable&
+Strategy::getFaceTable()
+{
+  return m_forwarder.getFaceTable();
+}
+
+} // namespace fw
+} // namespace nfd
+
+#endif // NFD_DAEMON_FW_STRATEGY_HPP
diff --git a/NFD/daemon/main.cpp b/NFD/daemon/main.cpp
new file mode 100644
index 0000000..e275668
--- /dev/null
+++ b/NFD/daemon/main.cpp
@@ -0,0 +1,408 @@
+/* -*- 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/>.
+ */
+
+#include <getopt.h>
+#include <boost/filesystem.hpp>
+
+#include "version.hpp"
+#include "core/logger.hpp"
+#include "core/global-io.hpp"
+#include "core/privilege-helper.hpp"
+#include "fw/forwarder.hpp"
+#include "face/null-face.hpp"
+#include "mgmt/internal-face.hpp"
+#include "mgmt/fib-manager.hpp"
+#include "mgmt/face-manager.hpp"
+#include "mgmt/strategy-choice-manager.hpp"
+#include "mgmt/status-server.hpp"
+#include "core/config-file.hpp"
+#include "mgmt/general-config-section.hpp"
+#include "mgmt/tables-config-section.hpp"
+
+namespace nfd {
+
+NFD_LOG_INIT("NFD");
+
+struct ProgramOptions
+{
+  bool showUsage;
+  bool showVersion;
+  bool showModules;
+  std::string config;
+};
+
+class Nfd : noncopyable
+{
+public:
+  explicit
+  Nfd(const std::string& configFile)
+    : m_configFile(configFile)
+    , m_originalStreamBuf(0)
+  {
+  }
+
+  ~Nfd()
+  {
+    if (static_cast<bool>(m_originalStreamBuf)) {
+      std::clog.rdbuf(m_originalStreamBuf);
+    }
+  }
+
+  void
+  initialize()
+  {
+    initializeLogging();
+
+    m_forwarder = make_shared<Forwarder>();
+
+    initializeManagement();
+
+    m_forwarder->getFaceTable().addReserved(make_shared<NullFace>(), FACEID_NULL);
+    m_forwarder->getFaceTable().addReserved(
+      make_shared<NullFace>(FaceUri("contentstore://")), FACEID_CONTENT_STORE);
+
+    PrivilegeHelper::drop();
+  }
+
+
+  void
+  initializeLogging()
+  {
+    ConfigFile config(&ConfigFile::ignoreUnknownSection);
+    LoggerFactory::getInstance().setConfigFile(config);
+
+    config.parse(m_configFile, true);
+    config.parse(m_configFile, false);
+  }
+
+  class IgnoreRibAndLogSections
+  {
+  public:
+    void
+    operator()(const std::string& filename,
+               const std::string& sectionName,
+               const ConfigSection& section,
+               bool isDryRun)
+
+    {
+      // Ignore "log" and sections beginning with "rib_" (intended for rib manager),
+      // but raise an error if we're missing a handler for an NFD section.
+
+      if (sectionName.find("rib") == 0 || sectionName == "log")
+        {
+          // do nothing
+        }
+      else
+        {
+          // missing NFD section
+          ConfigFile::throwErrorOnUnknownSection(filename, sectionName, section, isDryRun);
+        }
+    }
+  };
+
+  void
+  initializeManagement()
+  {
+    m_internalFace = make_shared<InternalFace>();
+
+    m_fibManager = make_shared<FibManager>(ref(m_forwarder->getFib()),
+                                           bind(&Forwarder::getFace, m_forwarder.get(), _1),
+                                           m_internalFace,
+                                           ndn::ref(m_keyChain));
+
+    m_faceManager = make_shared<FaceManager>(ref(m_forwarder->getFaceTable()),
+                                             m_internalFace,
+                                             ndn::ref(m_keyChain));
+
+    m_strategyChoiceManager =
+      make_shared<StrategyChoiceManager>(ref(m_forwarder->getStrategyChoice()),
+                                         m_internalFace,
+                                         ndn::ref(m_keyChain));
+
+    m_statusServer = make_shared<StatusServer>(m_internalFace,
+                                               ref(*m_forwarder),
+                                               ndn::ref(m_keyChain));
+
+    ConfigFile config((IgnoreRibAndLogSections()));
+    general::setConfigFile(config);
+
+    TablesConfigSection tablesConfig(m_forwarder->getCs(),
+                                     m_forwarder->getPit(),
+                                     m_forwarder->getFib(),
+                                     m_forwarder->getStrategyChoice(),
+                                     m_forwarder->getMeasurements());
+    tablesConfig.setConfigFile(config);
+
+    m_internalFace->getValidator().setConfigFile(config);
+
+    m_forwarder->getFaceTable().addReserved(m_internalFace, FACEID_INTERNAL_FACE);
+
+    m_faceManager->setConfigFile(config);
+
+    // parse config file
+    config.parse(m_configFile, true);
+    config.parse(m_configFile, false);
+
+    tablesConfig.ensureTablesAreConfigured();
+
+    // add FIB entry for NFD Management Protocol
+    shared_ptr<fib::Entry> entry = m_forwarder->getFib().insert("/localhost/nfd").first;
+    entry->addNextHop(m_internalFace, 0);
+  }
+
+  static void
+  printUsage(std::ostream& os, const std::string& programName)
+  {
+    os << "Usage: \n"
+       << "  " << programName << " [options]\n"
+       << "\n"
+       << "Run NFD forwarding daemon\n"
+       << "\n"
+       << "Options:\n"
+       << "  [--help]    - print this help message\n"
+       << "  [--version] - print version and exit\n"
+       << "  [--modules] - list available logging modules\n"
+       << "  [--config /path/to/nfd.conf] - path to configuration file "
+       << "(default: " << DEFAULT_CONFIG_FILE << ")\n"
+      ;
+  }
+
+  static void
+  printModules(std::ostream& os)
+  {
+    using namespace std;
+
+    os << "Available logging modules: \n";
+
+    list<string> modules(LoggerFactory::getInstance().getModules());
+    for (list<string>::const_iterator i = modules.begin(); i != modules.end(); ++i)
+      {
+        os << *i << "\n";
+      }
+  }
+
+  static bool
+  parseCommandLine(int argc, char** argv, ProgramOptions& options)
+  {
+    options.showUsage = false;
+    options.showVersion = false;
+    options.showModules = false;
+    options.config = DEFAULT_CONFIG_FILE;
+
+    while (true) {
+      int optionIndex = 0;
+      static ::option longOptions[] = {
+        { "help"   , no_argument      , 0, 0 },
+        { "modules", no_argument      , 0, 0 },
+        { "config" , required_argument, 0, 0 },
+        { "version", no_argument      , 0, 0 },
+        { 0        , 0                , 0, 0 }
+      };
+      int c = getopt_long_only(argc, argv, "", longOptions, &optionIndex);
+      if (c == -1)
+        break;
+
+      switch (c) {
+      case 0:
+        switch (optionIndex) {
+        case 0: // help
+          options.showUsage = true;
+          break;
+        case 1: // modules
+          options.showModules = true;
+          break;
+        case 2: // config
+          options.config = ::optarg;
+          break;
+        case 3: // version
+          options.showVersion = true;
+          break;
+        default:
+          return false;
+        }
+        break;
+      }
+    }
+    return true;
+  }
+
+  void
+  terminate(const boost::system::error_code& error,
+            int signalNo,
+            boost::asio::signal_set& signalSet)
+  {
+    if (error)
+      return;
+
+    if (signalNo == SIGINT ||
+        signalNo == SIGTERM)
+      {
+        getGlobalIoService().stop();
+        NFD_LOG_INFO("Caught signal '" << strsignal(signalNo) << "', exiting...");
+      }
+    else
+      {
+        signalSet.async_wait(bind(&Nfd::terminate, this, _1, _2, ref(signalSet)));
+      }
+  }
+
+  void
+  reload(const boost::system::error_code& error,
+         int signalNo,
+         boost::asio::signal_set& signalSet)
+  {
+    if (error)
+      return;
+
+    NFD_LOG_INFO("Caught signal '" << strsignal(signalNo));
+
+    ////////////////////////
+    // Reload config file //
+    ////////////////////////
+
+    // Logging
+    initializeLogging();
+    /// \todo Reopen log file
+
+    // Other stuff
+    ConfigFile config((IgnoreRibAndLogSections()));
+
+    general::setConfigFile(config);
+
+    TablesConfigSection tablesConfig(m_forwarder->getCs(),
+                                     m_forwarder->getPit(),
+                                     m_forwarder->getFib(),
+                                     m_forwarder->getStrategyChoice(),
+                                     m_forwarder->getMeasurements());
+
+    tablesConfig.setConfigFile(config);
+
+    m_internalFace->getValidator().setConfigFile(config);
+    m_faceManager->setConfigFile(config);
+
+    config.parse(m_configFile, false);
+
+    ////////////////////////
+
+    signalSet.async_wait(bind(&Nfd::reload, this, _1, _2, ref(signalSet)));
+  }
+
+private:
+  std::string m_configFile;
+
+  shared_ptr<Forwarder> m_forwarder;
+
+  shared_ptr<InternalFace>          m_internalFace;
+  shared_ptr<FibManager>            m_fibManager;
+  shared_ptr<FaceManager>           m_faceManager;
+  shared_ptr<StrategyChoiceManager> m_strategyChoiceManager;
+  shared_ptr<StatusServer>          m_statusServer;
+
+  shared_ptr<std::ofstream>         m_logFile;
+  std::basic_streambuf<char>*       m_originalStreamBuf;
+  ndn::KeyChain                     m_keyChain;
+};
+
+} // namespace nfd
+
+int
+main(int argc, char** argv)
+{
+  using namespace nfd;
+
+  ProgramOptions options;
+  bool isCommandLineValid = Nfd::parseCommandLine(argc, argv, options);
+  if (!isCommandLineValid) {
+    Nfd::printUsage(std::cerr, argv[0]);
+    return 1;
+  }
+
+  if (options.showUsage) {
+    Nfd::printUsage(std::cout, argv[0]);
+    return 0;
+  }
+
+  if (options.showVersion) {
+    std::cout << NFD_VERSION_BUILD_STRING << std::endl;
+    return 0;
+  }
+
+  if (options.showModules) {
+    Nfd::printModules(std::cout);
+    return 0;
+  }
+
+  Nfd nfdInstance(options.config);
+
+  try {
+    nfdInstance.initialize();
+  }
+  catch (boost::filesystem::filesystem_error& e) {
+    if (e.code() == boost::system::errc::permission_denied) {
+      NFD_LOG_FATAL("Permissions denied for " << e.path1() << ". " <<
+                    argv[0] << " should be run as superuser");
+    }
+    else {
+      NFD_LOG_FATAL(e.what());
+    }
+    return 1;
+  }
+  catch (const std::exception& e) {
+    NFD_LOG_FATAL(e.what());
+    return 2;
+  }
+  catch (const PrivilegeHelper::Error& e) {
+    // PrivilegeHelper::Errors do not inherit from std::exception
+    // and represent seteuid/gid failures
+
+    NFD_LOG_FATAL(e.what());
+    return 3;
+  }
+
+  boost::asio::signal_set terminationSignalSet(getGlobalIoService());
+  terminationSignalSet.add(SIGINT);
+  terminationSignalSet.add(SIGTERM);
+  terminationSignalSet.async_wait(bind(&Nfd::terminate, &nfdInstance, _1, _2,
+                                       ref(terminationSignalSet)));
+
+  boost::asio::signal_set reloadSignalSet(getGlobalIoService());
+  reloadSignalSet.add(SIGHUP);
+  reloadSignalSet.async_wait(bind(&Nfd::reload, &nfdInstance, _1, _2,
+                                  ref(reloadSignalSet)));
+
+  try {
+    getGlobalIoService().run();
+  }
+  catch (const std::exception& e) {
+    NFD_LOG_FATAL(e.what());
+    return 4;
+  }
+  catch (const PrivilegeHelper::Error& e) {
+    NFD_LOG_FATAL(e.what());
+    return 5;
+  }
+
+  return 0;
+}
diff --git a/NFD/daemon/mgmt/app-face.hpp b/NFD/daemon/mgmt/app-face.hpp
new file mode 100644
index 0000000..13442a8
--- /dev/null
+++ b/NFD/daemon/mgmt/app-face.hpp
@@ -0,0 +1,53 @@
+/* -*- 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_DAEMON_MGMT_APP_FACE_HPP
+#define NFD_DAEMON_MGMT_APP_FACE_HPP
+
+#include "common.hpp"
+
+#include <ndn-cxx/security/key-chain.hpp>
+
+namespace nfd {
+
+typedef function<void(const Name&, const Interest&)> OnInterest;
+
+class AppFace
+{
+public:
+  virtual void
+  setInterestFilter(const Name& filter,
+                    OnInterest onInterest) = 0;
+
+  virtual void
+  put(const Data& data) = 0;
+
+  virtual
+  ~AppFace() { }
+};
+
+} // namespace nfd
+
+#endif // NFD_DAEMON_MGMT_APP_FACE_HPP
diff --git a/NFD/daemon/mgmt/channel-status-publisher.cpp b/NFD/daemon/mgmt/channel-status-publisher.cpp
new file mode 100644
index 0000000..8abe2fb
--- /dev/null
+++ b/NFD/daemon/mgmt/channel-status-publisher.cpp
@@ -0,0 +1,87 @@
+/* -*- 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/>.
+ */
+
+#include "channel-status-publisher.hpp"
+
+#include "core/logger.hpp"
+#include "face/protocol-factory.hpp"
+#include "face/channel.hpp"
+
+#include <ndn-cxx/management/nfd-channel-status.hpp>
+
+namespace nfd {
+
+NFD_LOG_INIT("ChannelStatusPublisher");
+
+
+ChannelStatusPublisher::ChannelStatusPublisher(const FactoryMap& factories,
+                                               AppFace& face,
+                                               const Name& prefix,
+                                               ndn::KeyChain& keyChain)
+  : SegmentPublisher(face, prefix, keyChain)
+  , m_factories(factories)
+{
+
+}
+
+
+ChannelStatusPublisher::~ChannelStatusPublisher()
+{
+
+}
+
+size_t
+ChannelStatusPublisher::generate(ndn::EncodingBuffer& outBuffer)
+{
+  size_t totalLength = 0;
+  std::set<shared_ptr<ProtocolFactory> > seenFactories;
+
+  for (FactoryMap::const_iterator i = m_factories.begin();
+       i != m_factories.end(); ++i)
+    {
+      const shared_ptr<ProtocolFactory>& factory = i->second;
+
+      if (seenFactories.find(factory) != seenFactories.end())
+        {
+          continue;
+        }
+      seenFactories.insert(factory);
+
+      std::list<shared_ptr<const Channel> > channels = factory->getChannels();
+
+      for (std::list<shared_ptr<const Channel> >::const_iterator j = channels.begin();
+           j != channels.end(); ++j)
+        {
+          ndn::nfd::ChannelStatus entry;
+          entry.setLocalUri((*j)->getUri().toString());
+
+          totalLength += entry.wireEncode(outBuffer);
+        }
+    }
+
+  return totalLength;
+}
+
+} // namespace nfd
diff --git a/NFD/daemon/mgmt/channel-status-publisher.hpp b/NFD/daemon/mgmt/channel-status-publisher.hpp
new file mode 100644
index 0000000..ef660a3
--- /dev/null
+++ b/NFD/daemon/mgmt/channel-status-publisher.hpp
@@ -0,0 +1,60 @@
+/* -*- 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_DAEMON_MGMT_CHANNEL_STATUS_PUBLISHER_HPP
+#define NFD_DAEMON_MGMT_CHANNEL_STATUS_PUBLISHER_HPP
+
+#include "core/segment-publisher.hpp"
+#include "mgmt/app-face.hpp"
+
+namespace nfd {
+
+class ProtocolFactory;
+
+class ChannelStatusPublisher : public SegmentPublisher<AppFace>
+{
+public:
+  typedef std::map< std::string/*protocol*/, shared_ptr<ProtocolFactory> > FactoryMap;
+
+  ChannelStatusPublisher(const FactoryMap& factories,
+                         AppFace& face,
+                         const Name& prefix,
+                         ndn::KeyChain& keyChain);
+
+  virtual
+  ~ChannelStatusPublisher();
+
+protected:
+
+  virtual size_t
+  generate(ndn::EncodingBuffer& outBuffer);
+
+private:
+  const FactoryMap& m_factories;
+};
+
+} // namespace nfd
+
+#endif // NFD_DAEMON_MGMT_CHANNEL_STATUS_PUBLISHER_HPP
diff --git a/NFD/daemon/mgmt/command-validator.cpp b/NFD/daemon/mgmt/command-validator.cpp
new file mode 100644
index 0000000..396cfe4
--- /dev/null
+++ b/NFD/daemon/mgmt/command-validator.cpp
@@ -0,0 +1,222 @@
+/* -*- 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
+ *
+ * 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 "command-validator.hpp"
+#include "core/logger.hpp"
+
+#include <ndn-cxx/util/io.hpp>
+#include <ndn-cxx/security/identity-certificate.hpp>
+
+#include <boost/filesystem.hpp>
+#include <fstream>
+
+namespace nfd {
+
+NFD_LOG_INIT("CommandValidator");
+
+CommandValidator::CommandValidator()
+{
+
+}
+
+CommandValidator::~CommandValidator()
+{
+
+}
+
+void
+CommandValidator::setConfigFile(ConfigFile& configFile)
+{
+  configFile.addSectionHandler("authorizations",
+                               bind(&CommandValidator::onConfig, this, _1, _2, _3));
+}
+
+static inline void
+aggregateErrors(std::stringstream& ss, const std::string& msg)
+{
+  if (!ss.str().empty())
+    {
+      ss << "\n";
+    }
+  ss << msg;
+}
+
+void
+CommandValidator::onConfig(const ConfigSection& section,
+                           bool isDryRun,
+                           const std::string& filename)
+{
+  using namespace boost::filesystem;
+
+  const ConfigSection EMPTY_SECTION;
+
+  m_validator.reset();
+
+  if (section.begin() == section.end())
+    {
+      throw ConfigFile::Error("No authorize sections found");
+    }
+
+  std::stringstream dryRunErrors;
+  ConfigSection::const_iterator authIt;
+  for (authIt = section.begin(); authIt != section.end(); authIt++)
+    {
+      std::string certfile;
+      try
+        {
+          certfile = authIt->second.get<std::string>("certfile");
+        }
+      catch (const std::runtime_error& e)
+        {
+          std::string msg = "No certfile specified";
+          if (!isDryRun)
+            {
+              throw ConfigFile::Error(msg);
+            }
+          aggregateErrors(dryRunErrors, msg);
+          continue;
+        }
+
+      shared_ptr<ndn::IdentityCertificate> id;
+
+      if (certfile != "any")
+        {
+          path certfilePath = absolute(certfile, path(filename).parent_path());
+          NFD_LOG_DEBUG("generated certfile path: " << certfilePath.native());
+
+          std::ifstream in;
+          in.open(certfilePath.c_str());
+          if (!in.is_open())
+            {
+              std::string msg = "Unable to open certificate file " + certfilePath.native();
+              if (!isDryRun)
+                {
+                  throw ConfigFile::Error(msg);
+                }
+              aggregateErrors(dryRunErrors, msg);
+              continue;
+            }
+
+          try
+            {
+              id = ndn::io::load<ndn::IdentityCertificate>(in);
+            }
+          catch (const std::runtime_error& error)
+            {
+              // do nothing
+            }
+
+          if (!static_cast<bool>(id)) {
+            std::string msg = "Malformed certificate file " + certfilePath.native();
+            if (!isDryRun)
+              {
+                throw ConfigFile::Error(msg);
+              }
+            aggregateErrors(dryRunErrors, msg);
+            continue;
+          }
+
+          in.close();
+        }
+
+      std::string keyNameForLogging;
+      if (static_cast<bool>(id))
+        keyNameForLogging = id->getPublicKeyName().toUri();
+      else
+        {
+          keyNameForLogging = "wildcard";
+          NFD_LOG_WARN("Wildcard identity is intended for demo purpose only and " <<
+                       "SHOULD NOT be used in production environment");
+        }
+      const ConfigSection* privileges = 0;
+      try
+        {
+          privileges = &authIt->second.get_child("privileges");
+        }
+      catch (const std::runtime_error& error)
+        {
+          std::string msg = "No privileges section found for certificate file " +
+            certfile + " (" + keyNameForLogging + ")";
+          if (!isDryRun)
+            {
+              throw ConfigFile::Error(msg);
+            }
+          aggregateErrors(dryRunErrors, msg);
+          continue;
+        }
+
+      if (privileges->begin() == privileges->end())
+        {
+          NFD_LOG_WARN("No privileges specified for certificate file " << certfile
+                       << " (" << keyNameForLogging << ")");
+        }
+
+      ConfigSection::const_iterator privIt;
+      for (privIt = privileges->begin(); privIt != privileges->end(); privIt++)
+        {
+          const std::string& privilegeName = privIt->first;
+          if (m_supportedPrivileges.find(privilegeName) != m_supportedPrivileges.end())
+            {
+              NFD_LOG_INFO("Giving privilege \"" << privilegeName
+                           << "\" to identity " << keyNameForLogging);
+              if (!isDryRun)
+                {
+                  const std::string regex = "^<localhost><nfd><" + privilegeName + ">";
+                  if (static_cast<bool>(id))
+                    m_validator.addInterestRule(regex, *id);
+                  else
+                    m_validator.addInterestBypassRule(regex);
+                }
+            }
+          else
+            {
+              // Invalid configuration
+              std::string msg = "Invalid privilege \"" + privilegeName +
+                "\" for certificate file " + certfile + " (" + keyNameForLogging + ")";
+              if (!isDryRun)
+                {
+                  throw ConfigFile::Error(msg);
+                }
+              aggregateErrors(dryRunErrors, msg);
+            }
+        }
+    }
+
+  if (!dryRunErrors.str().empty())
+    {
+      throw ConfigFile::Error(dryRunErrors.str());
+    }
+}
+
+void
+CommandValidator::addSupportedPrivilege(const std::string& privilege)
+{
+  if (m_supportedPrivileges.find(privilege) != m_supportedPrivileges.end())
+    {
+      throw CommandValidator::Error("Duplicated privilege: " + privilege);
+    }
+  m_supportedPrivileges.insert(privilege);
+}
+
+} // namespace nfd
diff --git a/NFD/daemon/mgmt/command-validator.hpp b/NFD/daemon/mgmt/command-validator.hpp
new file mode 100644
index 0000000..b2cb184
--- /dev/null
+++ b/NFD/daemon/mgmt/command-validator.hpp
@@ -0,0 +1,116 @@
+/* -*- 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
+ *
+ * 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_MGMT_COMMAND_VALIDATOR_HPP
+#define NFD_DAEMON_MGMT_COMMAND_VALIDATOR_HPP
+
+#include "common.hpp"
+#include "config-file.hpp"
+#include <ndn-cxx/util/command-interest-validator.hpp>
+
+namespace nfd {
+
+class CommandValidator
+{
+public:
+
+  class Error : public std::runtime_error
+  {
+  public:
+    explicit
+    Error(const std::string& what)
+      : std::runtime_error(what)
+    {
+
+    }
+  };
+
+  CommandValidator();
+
+  ~CommandValidator();
+
+  void
+  setConfigFile(ConfigFile& configFile);
+
+  /**
+   * \param section "authorizations" section to parse
+   * \param isDryRun true if performing a dry run of configuration, false otherwise
+   * \param filename filename of configuration file
+   * \throws ConfigFile::Error on parse error
+   */
+  void
+  onConfig(const ConfigSection& section, bool isDryRun, const std::string& filename);
+
+  /**
+   * \param privilege name of privilege to add
+   * \throws CommandValidator::Error on duplicated privilege
+   */
+  void
+  addSupportedPrivilege(const std::string& privilege);
+
+  void
+  addInterestRule(const std::string& regex,
+                  const ndn::IdentityCertificate& certificate);
+
+  void
+  addInterestRule(const std::string& regex,
+                  const Name& keyName,
+                  const ndn::PublicKey& publicKey);
+
+  void
+  validate(const Interest& interest,
+           const ndn::OnInterestValidated& onValidated,
+           const ndn::OnInterestValidationFailed& onValidationFailed);
+
+private:
+  ndn::CommandInterestValidator m_validator;
+  std::set<std::string> m_supportedPrivileges;
+};
+
+inline void
+CommandValidator::addInterestRule(const std::string& regex,
+                                  const ndn::IdentityCertificate& certificate)
+{
+  m_validator.addInterestRule(regex, certificate);
+}
+
+inline void
+CommandValidator::addInterestRule(const std::string& regex,
+                                  const Name& keyName,
+                                  const ndn::PublicKey& publicKey)
+{
+  m_validator.addInterestRule(regex, keyName, publicKey);
+}
+
+inline void
+CommandValidator::validate(const Interest& interest,
+                           const ndn::OnInterestValidated& onValidated,
+                           const ndn::OnInterestValidationFailed& onValidationFailed)
+{
+  m_validator.validate(interest, onValidated, onValidationFailed);
+}
+
+} // namespace nfd
+
+#endif // NFD_DAEMON_MGMT_COMMAND_VALIDATOR_HPP
diff --git a/NFD/daemon/mgmt/face-manager.cpp b/NFD/daemon/mgmt/face-manager.cpp
new file mode 100644
index 0000000..768f1b9
--- /dev/null
+++ b/NFD/daemon/mgmt/face-manager.cpp
@@ -0,0 +1,1191 @@
+/* -*- 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/>.
+ */
+
+#include "face-manager.hpp"
+
+#include "core/logger.hpp"
+#include "core/network-interface.hpp"
+#include "fw/face-table.hpp"
+#include "face/tcp-factory.hpp"
+#include "face/udp-factory.hpp"
+#include "core/config-file.hpp"
+
+#ifdef HAVE_UNIX_SOCKETS
+#include "face/unix-stream-factory.hpp"
+#endif // HAVE_UNIX_SOCKETS
+
+#ifdef HAVE_LIBPCAP
+#include "face/ethernet-factory.hpp"
+#include "face/ethernet-face.hpp"
+#endif // HAVE_LIBPCAP
+
+#ifdef HAVE_WEBSOCKET
+#include "face/websocket-factory.hpp"
+#endif // HAVE_WEBSOCKET
+
+#include <ndn-cxx/management/nfd-face-event-notification.hpp>
+#include <ndn-cxx/management/nfd-face-query-filter.hpp>
+
+namespace nfd {
+
+NFD_LOG_INIT("FaceManager");
+
+const Name FaceManager::COMMAND_PREFIX("/localhost/nfd/faces");
+
+const size_t FaceManager::COMMAND_UNSIGNED_NCOMPS =
+  FaceManager::COMMAND_PREFIX.size() +
+  1 + // verb
+  1;  // verb parameters
+
+const size_t FaceManager::COMMAND_SIGNED_NCOMPS =
+  FaceManager::COMMAND_UNSIGNED_NCOMPS +
+  4; // (timestamp, nonce, signed info tlv, signature tlv)
+
+const FaceManager::SignedVerbAndProcessor FaceManager::SIGNED_COMMAND_VERBS[] =
+  {
+    SignedVerbAndProcessor(
+                           Name::Component("create"),
+                           &FaceManager::createFace
+                           ),
+
+    SignedVerbAndProcessor(
+                           Name::Component("destroy"),
+                           &FaceManager::destroyFace
+                           ),
+
+    SignedVerbAndProcessor(
+                           Name::Component("enable-local-control"),
+                           &FaceManager::enableLocalControl
+                           ),
+
+    SignedVerbAndProcessor(
+                           Name::Component("disable-local-control"),
+                           &FaceManager::disableLocalControl
+                           ),
+  };
+
+const FaceManager::UnsignedVerbAndProcessor FaceManager::UNSIGNED_COMMAND_VERBS[] =
+  {
+    UnsignedVerbAndProcessor(
+                             Name::Component("list"),
+                             &FaceManager::listFaces
+                             ),
+
+    UnsignedVerbAndProcessor(
+                             Name::Component("events"),
+                             &FaceManager::ignoreUnsignedVerb
+                             ),
+
+    UnsignedVerbAndProcessor(
+                             Name::Component("channels"),
+                             &FaceManager::listChannels
+                             ),
+
+    UnsignedVerbAndProcessor(
+                             Name::Component("query"),
+                             &FaceManager::listQueriedFaces
+                             ),
+  };
+
+const Name FaceManager::FACES_LIST_DATASET_PREFIX("/localhost/nfd/faces/list");
+const size_t FaceManager::FACES_LIST_DATASET_NCOMPS = FACES_LIST_DATASET_PREFIX.size();
+
+const Name FaceManager::FACE_EVENTS_PREFIX("/localhost/nfd/faces/events");
+
+const Name FaceManager::CHANNELS_LIST_DATASET_PREFIX("/localhost/nfd/faces/channels");
+const size_t FaceManager::CHANNELS_LIST_DATASET_NCOMPS = CHANNELS_LIST_DATASET_PREFIX.size();
+
+const Name FaceManager::FACES_QUERY_DATASET_PREFIX("/localhost/nfd/faces/query");
+const size_t FaceManager::FACES_QUERY_DATASET_NCOMPS = FACES_QUERY_DATASET_PREFIX.size() + 1;
+
+FaceManager::FaceManager(FaceTable& faceTable,
+                         shared_ptr<InternalFace> face,
+                         ndn::KeyChain& keyChain)
+  : ManagerBase(face, FACE_MANAGER_PRIVILEGE, keyChain)
+  , m_faceTable(faceTable)
+  , m_faceAddConn(m_faceTable.onAdd.connect(bind(&FaceManager::onAddFace, this, _1)))
+  , m_faceRemoveConn(m_faceTable.onRemove.connect(bind(&FaceManager::onRemoveFace, this, _1)))
+  , m_faceStatusPublisher(m_faceTable, *m_face, FACES_LIST_DATASET_PREFIX, keyChain)
+  , m_channelStatusPublisher(m_factories, *m_face, CHANNELS_LIST_DATASET_PREFIX, keyChain)
+  , m_notificationStream(*m_face, FACE_EVENTS_PREFIX, keyChain)
+  , m_signedVerbDispatch(SIGNED_COMMAND_VERBS,
+                         SIGNED_COMMAND_VERBS +
+                         (sizeof(SIGNED_COMMAND_VERBS) / sizeof(SignedVerbAndProcessor)))
+  , m_unsignedVerbDispatch(UNSIGNED_COMMAND_VERBS,
+                           UNSIGNED_COMMAND_VERBS +
+                           (sizeof(UNSIGNED_COMMAND_VERBS) / sizeof(UnsignedVerbAndProcessor)))
+
+{
+  face->setInterestFilter("/localhost/nfd/faces",
+                          bind(&FaceManager::onFaceRequest, this, _2));
+}
+
+FaceManager::~FaceManager()
+{
+
+}
+
+void
+FaceManager::setConfigFile(ConfigFile& configFile)
+{
+  configFile.addSectionHandler("face_system",
+                               bind(&FaceManager::onConfig, this, _1, _2, _3));
+}
+
+
+void
+FaceManager::onConfig(const ConfigSection& configSection,
+                      bool isDryRun,
+                      const std::string& filename)
+{
+  bool hasSeenUnix = false;
+  bool hasSeenTcp = false;
+  bool hasSeenUdp = false;
+  bool hasSeenEther = false;
+  bool hasSeenWebSocket = false;
+
+  const std::vector<NetworkInterfaceInfo> nicList(listNetworkInterfaces());
+
+  for (const auto& item : configSection)
+    {
+      if (item.first == "unix")
+        {
+          if (hasSeenUnix)
+            throw Error("Duplicate \"unix\" section");
+          hasSeenUnix = true;
+
+          processSectionUnix(item.second, isDryRun);
+        }
+      else if (item.first == "tcp")
+        {
+          if (hasSeenTcp)
+            throw Error("Duplicate \"tcp\" section");
+          hasSeenTcp = true;
+
+          processSectionTcp(item.second, isDryRun);
+        }
+      else if (item.first == "udp")
+        {
+          if (hasSeenUdp)
+            throw Error("Duplicate \"udp\" section");
+          hasSeenUdp = true;
+
+          processSectionUdp(item.second, isDryRun, nicList);
+        }
+      else if (item.first == "ether")
+        {
+          if (hasSeenEther)
+            throw Error("Duplicate \"ether\" section");
+          hasSeenEther = true;
+
+          processSectionEther(item.second, isDryRun, nicList);
+        }
+      else if (item.first == "websocket")
+        {
+          if (hasSeenWebSocket)
+            throw Error("Duplicate \"websocket\" section");
+          hasSeenWebSocket = true;
+
+          processSectionWebSocket(item.second, isDryRun);
+        }
+      else
+        {
+          throw Error("Unrecognized option \"" + item.first + "\"");
+        }
+    }
+}
+
+void
+FaceManager::processSectionUnix(const ConfigSection& configSection, bool isDryRun)
+{
+  // ; the unix section contains settings of Unix stream faces and channels
+  // unix
+  // {
+  //   path /var/run/nfd.sock ; Unix stream listener path
+  // }
+
+#if defined(HAVE_UNIX_SOCKETS)
+
+  std::string path = "/var/run/nfd.sock";
+
+  for (ConfigSection::const_iterator i = configSection.begin();
+       i != configSection.end();
+       ++i)
+    {
+      if (i->first == "path")
+        {
+          path = i->second.get_value<std::string>();
+        }
+      else
+        {
+          throw ConfigFile::Error("Unrecognized option \"" + i->first + "\" in \"unix\" section");
+        }
+    }
+
+  if (!isDryRun)
+    {
+      if (m_factories.count("unix") > 0)
+        {
+          return;
+          // shared_ptr<UnixStreamFactory> factory
+          //   = static_pointer_cast<UnixStreamFactory>(m_factories["unix"]);
+          // shared_ptr<UnixStreamChannel> unixChannel = factory->findChannel(path);
+
+          // if (static_cast<bool>(unixChannel))
+          //   {
+          //     return;
+          //   }
+        }
+
+      shared_ptr<UnixStreamFactory> factory = make_shared<UnixStreamFactory>();
+      shared_ptr<UnixStreamChannel> unixChannel = factory->createChannel(path);
+
+      // Should acceptFailed callback be used somehow?
+      unixChannel->listen(bind(&FaceTable::add, &m_faceTable, _1),
+                          UnixStreamChannel::ConnectFailedCallback());
+
+      m_factories.insert(std::make_pair("unix", factory));
+    }
+#else
+  throw ConfigFile::Error("NFD was compiled without Unix sockets support, "
+                          "cannot process \"unix\" section");
+#endif // HAVE_UNIX_SOCKETS
+
+}
+
+void
+FaceManager::processSectionTcp(const ConfigSection& configSection, bool isDryRun)
+{
+  // ; the tcp section contains settings of TCP faces and channels
+  // tcp
+  // {
+  //   listen yes ; set to 'no' to disable TCP listener, default 'yes'
+  //   port 6363 ; TCP listener port number
+  // }
+
+  std::string port = "6363";
+  bool needToListen = true;
+  bool enableV4 = true;
+  bool enableV6 = true;
+
+  for (ConfigSection::const_iterator i = configSection.begin();
+       i != configSection.end();
+       ++i)
+    {
+      if (i->first == "port")
+        {
+          port = i->second.get_value<std::string>();
+          try
+            {
+              uint16_t portNo = boost::lexical_cast<uint16_t>(port);
+              NFD_LOG_TRACE("TCP port set to " << portNo);
+            }
+          catch (const std::bad_cast& error)
+            {
+              throw ConfigFile::Error("Invalid value for option " +
+                                      i->first + "\" in \"tcp\" section");
+            }
+        }
+      else if (i->first == "listen")
+        {
+          needToListen = parseYesNo(i, i->first, "tcp");
+        }
+      else if (i->first == "enable_v4")
+        {
+          enableV4 = parseYesNo(i, i->first, "tcp");
+        }
+      else if (i->first == "enable_v6")
+        {
+          enableV6 = parseYesNo(i, i->first, "tcp");
+        }
+      else
+        {
+          throw ConfigFile::Error("Unrecognized option \"" + i->first + "\" in \"tcp\" section");
+        }
+    }
+
+  if (!enableV4 && !enableV6)
+    {
+      throw ConfigFile::Error("IPv4 and IPv6 channels have been disabled."
+                              " Remove \"tcp\" section to disable TCP channels or"
+                              " re-enable at least one channel type.");
+    }
+
+  if (!isDryRun)
+    {
+      if (m_factories.count("tcp") > 0)
+        {
+          return;
+        }
+
+      shared_ptr<TcpFactory> factory = make_shared<TcpFactory>(port);
+      m_factories.insert(std::make_pair("tcp", factory));
+
+      if (enableV4)
+        {
+          shared_ptr<TcpChannel> ipv4Channel = factory->createChannel("0.0.0.0", port);
+          if (needToListen)
+            {
+              // Should acceptFailed callback be used somehow?
+              ipv4Channel->listen(bind(&FaceTable::add, &m_faceTable, _1),
+                                  TcpChannel::ConnectFailedCallback());
+            }
+
+          m_factories.insert(std::make_pair("tcp4", factory));
+        }
+
+      if (enableV6)
+        {
+          shared_ptr<TcpChannel> ipv6Channel = factory->createChannel("::", port);
+          if (needToListen)
+            {
+              // Should acceptFailed callback be used somehow?
+              ipv6Channel->listen(bind(&FaceTable::add, &m_faceTable, _1),
+                                  TcpChannel::ConnectFailedCallback());
+            }
+
+          m_factories.insert(std::make_pair("tcp6", factory));
+        }
+    }
+}
+
+void
+FaceManager::processSectionUdp(const ConfigSection& configSection,
+                               bool isDryRun,
+                               const std::vector<NetworkInterfaceInfo>& nicList)
+{
+  // ; the udp section contains settings of UDP faces and channels
+  // udp
+  // {
+  //   port 6363 ; UDP unicast port number
+  //   idle_timeout 30 ; idle time (seconds) before closing a UDP unicast face
+  //   keep_alive_interval 25; interval (seconds) between keep-alive refreshes
+
+  //   ; NFD creates one UDP multicast face per NIC
+  //   mcast yes ; set to 'no' to disable UDP multicast, default 'yes'
+  //   mcast_port 56363 ; UDP multicast port number
+  //   mcast_group 224.0.23.170 ; UDP multicast group (IPv4 only)
+  // }
+
+  std::string port = "6363";
+  bool enableV4 = true;
+  bool enableV6 = true;
+  size_t timeout = 30;
+  size_t keepAliveInterval = 25;
+  bool useMcast = true;
+  std::string mcastGroup = "224.0.23.170";
+  std::string mcastPort = "56363";
+
+
+  for (ConfigSection::const_iterator i = configSection.begin();
+       i != configSection.end();
+       ++i)
+    {
+      if (i->first == "port")
+        {
+          port = i->second.get_value<std::string>();
+          try
+            {
+              uint16_t portNo = boost::lexical_cast<uint16_t>(port);
+              NFD_LOG_TRACE("UDP port set to " << portNo);
+            }
+          catch (const std::bad_cast& error)
+            {
+              throw ConfigFile::Error("Invalid value for option " +
+                                      i->first + "\" in \"udp\" section");
+            }
+        }
+      else if (i->first == "enable_v4")
+        {
+          enableV4 = parseYesNo(i, i->first, "udp");
+        }
+      else if (i->first == "enable_v6")
+        {
+          enableV6 = parseYesNo(i, i->first, "udp");
+        }
+      else if (i->first == "idle_timeout")
+        {
+          try
+            {
+              timeout = i->second.get_value<size_t>();
+            }
+          catch (const std::exception& e)
+            {
+              throw ConfigFile::Error("Invalid value for option \"" +
+                                      i->first + "\" in \"udp\" section");
+            }
+        }
+      else if (i->first == "keep_alive_interval")
+        {
+          try
+            {
+              keepAliveInterval = i->second.get_value<size_t>();
+
+              /// \todo Make use of keepAliveInterval
+              (void)(keepAliveInterval);
+            }
+          catch (const std::exception& e)
+            {
+              throw ConfigFile::Error("Invalid value for option \"" +
+                                      i->first + "\" in \"udp\" section");
+            }
+        }
+      else if (i->first == "mcast")
+        {
+          useMcast = parseYesNo(i, i->first, "udp");
+        }
+      else if (i->first == "mcast_port")
+        {
+          mcastPort = i->second.get_value<std::string>();
+          try
+            {
+              uint16_t portNo = boost::lexical_cast<uint16_t>(mcastPort);
+              NFD_LOG_TRACE("UDP multicast port set to " << portNo);
+            }
+          catch (const std::bad_cast& error)
+            {
+              throw ConfigFile::Error("Invalid value for option " +
+                                      i->first + "\" in \"udp\" section");
+            }
+        }
+      else if (i->first == "mcast_group")
+        {
+          using namespace boost::asio::ip;
+          mcastGroup = i->second.get_value<std::string>();
+          try
+            {
+              address mcastGroupTest = address::from_string(mcastGroup);
+              if (!mcastGroupTest.is_v4())
+                {
+                  throw ConfigFile::Error("Invalid value for option \"" +
+                                          i->first + "\" in \"udp\" section");
+                }
+            }
+          catch(const std::runtime_error& e)
+            {
+              throw ConfigFile::Error("Invalid value for option \"" +
+                                      i->first + "\" in \"udp\" section");
+            }
+        }
+      else
+        {
+          throw ConfigFile::Error("Unrecognized option \"" + i->first + "\" in \"udp\" section");
+        }
+    }
+
+  if (!enableV4 && !enableV6)
+    {
+      throw ConfigFile::Error("IPv4 and IPv6 channels have been disabled."
+                              " Remove \"udp\" section to disable UDP channels or"
+                              " re-enable at least one channel type.");
+    }
+  else if (useMcast && !enableV4)
+    {
+      throw ConfigFile::Error("IPv4 multicast requested, but IPv4 channels"
+                              " have been disabled (conflicting configuration options set)");
+    }
+
+  /// \todo what is keep alive interval used for?
+
+  if (!isDryRun)
+    {
+      shared_ptr<UdpFactory> factory;
+      bool isReload = false;
+      if (m_factories.count("udp") > 0) {
+        isReload = true;
+        factory = static_pointer_cast<UdpFactory>(m_factories["udp"]);
+      }
+      else {
+        factory = make_shared<UdpFactory>(port);
+        m_factories.insert(std::make_pair("udp", factory));
+      }
+
+      if (!isReload && enableV4)
+        {
+          shared_ptr<UdpChannel> v4Channel =
+            factory->createChannel("0.0.0.0", port, time::seconds(timeout));
+
+          v4Channel->listen(bind(&FaceTable::add, &m_faceTable, _1),
+                            UdpChannel::ConnectFailedCallback());
+
+          m_factories.insert(std::make_pair("udp4", factory));
+        }
+
+      if (!isReload && enableV6)
+        {
+          shared_ptr<UdpChannel> v6Channel =
+            factory->createChannel("::", port, time::seconds(timeout));
+
+          v6Channel->listen(bind(&FaceTable::add, &m_faceTable, _1),
+                            UdpChannel::ConnectFailedCallback());
+          m_factories.insert(std::make_pair("udp6", factory));
+        }
+
+      if (useMcast && enableV4)
+        {
+          std::vector<NetworkInterfaceInfo> ipv4MulticastInterfaces;
+          for (const auto& nic : nicList)
+            {
+              if (nic.isUp() && nic.isMulticastCapable() && !nic.ipv4Addresses.empty())
+                {
+                  ipv4MulticastInterfaces.push_back(nic);
+                }
+            }
+
+          bool isNicNameNecessary = false;
+#if defined(__linux__)
+          if (ipv4MulticastInterfaces.size() > 1)
+            {
+              // On Linux if we have more than one MulticastUdpFace
+              // we need to specify the name of the interface
+              isNicNameNecessary = true;
+            }
+#endif
+
+          std::list<shared_ptr<MulticastUdpFace> > multicastFacesToRemove;
+          for (UdpFactory::MulticastFaceMap::const_iterator i =
+                 factory->getMulticastFaces().begin();
+               i != factory->getMulticastFaces().end();
+               ++i)
+            {
+              multicastFacesToRemove.push_back(i->second);
+            }
+
+          for (const auto& nic : ipv4MulticastInterfaces)
+            {
+              shared_ptr<MulticastUdpFace> newFace;
+              newFace = factory->createMulticastFace(nic.ipv4Addresses[0].to_string(),
+                                                     mcastGroup,
+                                                     mcastPort,
+                                                     isNicNameNecessary ? nic.name : "");
+              addCreatedFaceToForwarder(newFace);
+              multicastFacesToRemove.remove(newFace);
+            }
+
+          for (std::list<shared_ptr<MulticastUdpFace> >::iterator i =
+                 multicastFacesToRemove.begin();
+               i != multicastFacesToRemove.end();
+               ++i)
+            {
+              (*i)->close();
+            }
+        }
+      else
+        {
+          std::list<shared_ptr<MulticastUdpFace> > multicastFacesToRemove;
+          for (UdpFactory::MulticastFaceMap::const_iterator i =
+                 factory->getMulticastFaces().begin();
+               i != factory->getMulticastFaces().end();
+               ++i)
+            {
+              multicastFacesToRemove.push_back(i->second);
+            }
+
+          for (std::list<shared_ptr<MulticastUdpFace> >::iterator i =
+                 multicastFacesToRemove.begin();
+               i != multicastFacesToRemove.end();
+               ++i)
+            {
+              (*i)->close();
+            }
+        }
+    }
+}
+
+void
+FaceManager::processSectionEther(const ConfigSection& configSection,
+                                 bool isDryRun,
+                                 const std::vector<NetworkInterfaceInfo>& nicList)
+{
+  // ; the ether section contains settings of Ethernet faces and channels
+  // ether
+  // {
+  //   ; NFD creates one Ethernet multicast face per NIC
+  //   mcast yes ; set to 'no' to disable Ethernet multicast, default 'yes'
+  //   mcast_group 01:00:5E:00:17:AA ; Ethernet multicast group
+  // }
+
+#if defined(HAVE_LIBPCAP)
+  bool useMcast = true;
+  ethernet::Address mcastGroup(ethernet::getDefaultMulticastAddress());
+
+  for (ConfigSection::const_iterator i = configSection.begin();
+       i != configSection.end();
+       ++i)
+    {
+      if (i->first == "mcast")
+        {
+          useMcast = parseYesNo(i, i->first, "ether");
+        }
+
+      else if (i->first == "mcast_group")
+        {
+          mcastGroup = ethernet::Address::fromString(i->second.get_value<std::string>());
+          if (mcastGroup.isNull())
+            {
+              throw ConfigFile::Error("Invalid value for option \"" +
+                                      i->first + "\" in \"ether\" section");
+            }
+        }
+      else
+        {
+          throw ConfigFile::Error("Unrecognized option \"" + i->first + "\" in \"ether\" section");
+        }
+    }
+
+  if (!isDryRun)
+    {
+      shared_ptr<EthernetFactory> factory;
+      if (m_factories.count("ether") > 0) {
+        factory = static_pointer_cast<EthernetFactory>(m_factories["ether"]);
+      }
+      else {
+        factory = make_shared<EthernetFactory>();
+        m_factories.insert(std::make_pair("ether", factory));
+      }
+
+      if (useMcast)
+        {
+          std::list<shared_ptr<EthernetFace> > multicastFacesToRemove;
+          for (EthernetFactory::MulticastFaceMap::const_iterator i =
+                 factory->getMulticastFaces().begin();
+               i != factory->getMulticastFaces().end();
+               ++i)
+            {
+              multicastFacesToRemove.push_back(i->second);
+            }
+
+          for (const auto& nic : nicList)
+            {
+              if (nic.isUp() && nic.isMulticastCapable())
+                {
+                  try
+                    {
+                      shared_ptr<EthernetFace> newFace =
+                        factory->createMulticastFace(nic, mcastGroup);
+
+                      addCreatedFaceToForwarder(newFace);
+                      multicastFacesToRemove.remove(newFace);
+                    }
+                  catch (const EthernetFactory::Error& factoryError)
+                    {
+                      NFD_LOG_ERROR(factoryError.what() << ", continuing");
+                    }
+                  catch (const EthernetFace::Error& faceError)
+                    {
+                      NFD_LOG_ERROR(faceError.what() << ", continuing");
+                    }
+                }
+            }
+
+          for (std::list<shared_ptr<EthernetFace> >::iterator i =
+                 multicastFacesToRemove.begin();
+               i != multicastFacesToRemove.end();
+               ++i)
+            {
+              (*i)->close();
+            }
+        }
+      else
+        {
+          std::list<shared_ptr<EthernetFace> > multicastFacesToRemove;
+          for (EthernetFactory::MulticastFaceMap::const_iterator i =
+                 factory->getMulticastFaces().begin();
+               i != factory->getMulticastFaces().end();
+               ++i)
+            {
+              multicastFacesToRemove.push_back(i->second);
+            }
+
+          for (std::list<shared_ptr<EthernetFace> >::iterator i =
+                 multicastFacesToRemove.begin();
+               i != multicastFacesToRemove.end();
+               ++i)
+            {
+              (*i)->close();
+            }
+        }
+    }
+#else
+  throw ConfigFile::Error("NFD was compiled without libpcap, cannot process \"ether\" section");
+#endif // HAVE_LIBPCAP
+}
+
+void
+FaceManager::processSectionWebSocket(const ConfigSection& configSection, bool isDryRun)
+{
+  // ; the websocket section contains settings of WebSocket faces and channels
+  // websocket
+  // {
+  //   listen yes ; set to 'no' to disable WebSocket listener, default 'yes'
+  //   port 9696 ; WebSocket listener port number
+  //   enable_v4 yes ; set to 'no' to disable listening on IPv4 socket, default 'yes'
+  //   enable_v6 yes ; set to 'no' to disable listening on IPv6 socket, default 'yes'
+  // }
+
+#if defined(HAVE_WEBSOCKET)
+
+  std::string port = "9696";
+  bool needToListen = true;
+  bool enableV4 = true;
+  bool enableV6 = true;
+
+  for (ConfigSection::const_iterator i = configSection.begin();
+       i != configSection.end();
+       ++i)
+    {
+      if (i->first == "port")
+        {
+          port = i->second.get_value<std::string>();
+          try
+            {
+              uint16_t portNo = boost::lexical_cast<uint16_t>(port);
+              NFD_LOG_TRACE("WebSocket port set to " << portNo);
+            }
+          catch (const std::bad_cast& error)
+            {
+              throw ConfigFile::Error("Invalid value for option " +
+                                      i->first + "\" in \"websocket\" section");
+            }
+        }
+      else if (i->first == "listen")
+        {
+          needToListen = parseYesNo(i, i->first, "websocket");
+        }
+      else if (i->first == "enable_v4")
+        {
+          enableV4 = parseYesNo(i, i->first, "websocket");
+        }
+      else if (i->first == "enable_v6")
+        {
+          enableV6 = parseYesNo(i, i->first, "websocket");
+        }
+      else
+        {
+          throw ConfigFile::Error("Unrecognized option \"" +
+                                  i->first + "\" in \"websocket\" section");
+        }
+    }
+
+  if (!enableV4 && !enableV6)
+    {
+      throw ConfigFile::Error("IPv4 and IPv6 channels have been disabled."
+                              " Remove \"websocket\" section to disable WebSocket channels or"
+                              " re-enable at least one channel type.");
+    }
+
+  if (!enableV4 && enableV6)
+    {
+      throw ConfigFile::Error("NFD does not allow pure IPv6 WebSocket channel.");
+    }
+
+  if (!isDryRun)
+    {
+      if (m_factories.count("websocket") > 0)
+        {
+          return;
+        }
+
+      shared_ptr<WebSocketFactory> factory = make_shared<WebSocketFactory>(port);
+      m_factories.insert(std::make_pair("websocket", factory));
+
+      if (enableV6 && enableV4)
+        {
+          shared_ptr<WebSocketChannel> ip46Channel = factory->createChannel("::", port);
+          if (needToListen)
+            {
+              ip46Channel->listen(bind(&FaceTable::add, &m_faceTable, _1));
+            }
+
+          m_factories.insert(std::make_pair("websocket46", factory));
+        }
+      else if (enableV4)
+        {
+          shared_ptr<WebSocketChannel> ipv4Channel = factory->createChannel("0.0.0.0", port);
+          if (needToListen)
+            {
+              ipv4Channel->listen(bind(&FaceTable::add, &m_faceTable, _1));
+            }
+
+          m_factories.insert(std::make_pair("websocket4", factory));
+        }
+    }
+#else
+  throw ConfigFile::Error("NFD was compiled without WebSocket, "
+                          "cannot process \"websocket\" section");
+#endif // HAVE_WEBSOCKET
+}
+
+
+void
+FaceManager::onFaceRequest(const Interest& request)
+{
+  const Name& command = request.getName();
+  const size_t commandNComps = command.size();
+
+  if (commandNComps <= COMMAND_PREFIX.size())
+    {
+      // command is too short to have a verb
+      NFD_LOG_DEBUG("command result: malformed");
+      sendResponse(command, 400, "Malformed command");
+      return;
+    }
+
+  const Name::Component& verb = command.at(COMMAND_PREFIX.size());
+
+  const auto unsignedVerbProcessor = m_unsignedVerbDispatch.find(verb);
+  if (unsignedVerbProcessor != m_unsignedVerbDispatch.end())
+    {
+      NFD_LOG_DEBUG("command result: processing verb: " << verb);
+      (unsignedVerbProcessor->second)(this, request);
+    }
+  else if (COMMAND_UNSIGNED_NCOMPS <= commandNComps &&
+           commandNComps < COMMAND_SIGNED_NCOMPS)
+    {
+      NFD_LOG_DEBUG("command result: unsigned verb: " << command);
+      sendResponse(command, 401, "Signature required");
+    }
+  else if (commandNComps < COMMAND_SIGNED_NCOMPS ||
+           !COMMAND_PREFIX.isPrefixOf(command))
+    {
+      NFD_LOG_DEBUG("command result: malformed");
+      sendResponse(command, 400, "Malformed command");
+    }
+  else
+    {
+      validate(request,
+               bind(&FaceManager::onValidatedFaceRequest, this, _1),
+               bind(&ManagerBase::onCommandValidationFailed, this, _1, _2));
+    }
+}
+
+void
+FaceManager::onValidatedFaceRequest(const shared_ptr<const Interest>& request)
+{
+  const Name& command = request->getName();
+  const Name::Component& verb = command[COMMAND_PREFIX.size()];
+  const Name::Component& parameterComponent = command[COMMAND_PREFIX.size() + 1];
+
+  SignedVerbDispatchTable::const_iterator signedVerbProcessor = m_signedVerbDispatch.find(verb);
+  if (signedVerbProcessor != m_signedVerbDispatch.end())
+    {
+      ControlParameters parameters;
+      if (!extractParameters(parameterComponent, parameters))
+        {
+          sendResponse(command, 400, "Malformed command");
+          return;
+        }
+
+      NFD_LOG_DEBUG("command result: processing verb: " << verb);
+      (signedVerbProcessor->second)(this, *request, parameters);
+    }
+  else
+    {
+      NFD_LOG_DEBUG("command result: unsupported verb: " << verb);
+      sendResponse(command, 501, "Unsupported command");
+    }
+
+}
+
+void
+FaceManager::addCreatedFaceToForwarder(const shared_ptr<Face>& newFace)
+{
+  m_faceTable.add(newFace);
+
+  //NFD_LOG_DEBUG("Created face " << newFace->getRemoteUri() << " ID " << newFace->getId());
+}
+
+void
+FaceManager::onCreated(const Name& requestName,
+                       ControlParameters& parameters,
+                       const shared_ptr<Face>& newFace)
+{
+  addCreatedFaceToForwarder(newFace);
+  parameters.setFaceId(newFace->getId());
+  parameters.setUri(newFace->getRemoteUri().toString());
+
+  sendResponse(requestName, 200, "Success", parameters.wireEncode());
+}
+
+void
+FaceManager::onConnectFailed(const Name& requestName, const std::string& reason)
+{
+  NFD_LOG_DEBUG("Failed to create face: " << reason);
+  sendResponse(requestName, 408, reason);
+}
+
+void
+FaceManager::createFace(const Interest& request,
+                        ControlParameters& parameters)
+{
+  const Name& requestName = request.getName();
+  ndn::nfd::FaceCreateCommand command;
+
+  if (!validateParameters(command, parameters))
+    {
+      sendResponse(requestName, 400, "Malformed command");
+      NFD_LOG_TRACE("invalid control parameters URI");
+      return;
+    }
+
+  FaceUri uri;
+  if (!uri.parse(parameters.getUri()))
+    {
+      sendResponse(requestName, 400, "Malformed command");
+      NFD_LOG_TRACE("failed to parse URI");
+      return;
+    }
+
+  if (!uri.isCanonical())
+    {
+      sendResponse(requestName, 400, "Non-canonical URI");
+      NFD_LOG_TRACE("received non-canonical URI");
+      return;
+    }
+
+  FactoryMap::iterator factory = m_factories.find(uri.getScheme());
+  if (factory == m_factories.end())
+    {
+      sendResponse(requestName, 501, "Unsupported protocol");
+      return;
+    }
+
+  try
+    {
+      factory->second->createFace(uri,
+                                  bind(&FaceManager::onCreated,
+                                       this, requestName, parameters, _1),
+                                  bind(&FaceManager::onConnectFailed,
+                                       this, requestName, _1));
+    }
+  catch (const std::runtime_error& error)
+    {
+      std::string errorMessage = "NFD error: ";
+      errorMessage += error.what();
+
+      NFD_LOG_ERROR(errorMessage);
+      sendResponse(requestName, 500, errorMessage);
+    }
+  catch (const std::logic_error& error)
+    {
+      std::string errorMessage = "NFD error: ";
+      errorMessage += error.what();
+
+      NFD_LOG_ERROR(errorMessage);
+      sendResponse(requestName, 500, errorMessage);
+    }
+}
+
+
+void
+FaceManager::destroyFace(const Interest& request,
+                         ControlParameters& parameters)
+{
+  const Name& requestName = request.getName();
+  ndn::nfd::FaceDestroyCommand command;
+
+  if (!validateParameters(command, parameters))
+    {
+      sendResponse(requestName, 400, "Malformed command");
+      return;
+    }
+
+  shared_ptr<Face> target = m_faceTable.get(parameters.getFaceId());
+  if (static_cast<bool>(target))
+    {
+      target->close();
+    }
+
+  sendResponse(requestName, 200, "Success", parameters.wireEncode());
+
+}
+
+void
+FaceManager::onAddFace(shared_ptr<Face> face)
+{
+  ndn::nfd::FaceEventNotification notification;
+  notification.setKind(ndn::nfd::FACE_EVENT_CREATED);
+  face->copyStatusTo(notification);
+
+  m_notificationStream.postNotification(notification);
+}
+
+void
+FaceManager::onRemoveFace(shared_ptr<Face> face)
+{
+  ndn::nfd::FaceEventNotification notification;
+  notification.setKind(ndn::nfd::FACE_EVENT_DESTROYED);
+  face->copyStatusTo(notification);
+
+  m_notificationStream.postNotification(notification);
+}
+
+bool
+FaceManager::extractLocalControlParameters(const Interest& request,
+                                           ControlParameters& parameters,
+                                           ControlCommand& command,
+                                           shared_ptr<LocalFace>& outFace,
+                                           LocalControlFeature& outFeature)
+{
+  if (!validateParameters(command, parameters))
+    {
+      sendResponse(request.getName(), 400, "Malformed command");
+      return false;
+    }
+
+  shared_ptr<Face> face = m_faceTable.get(request.getIncomingFaceId());
+
+  if (!static_cast<bool>(face))
+    {
+      NFD_LOG_DEBUG("command result: faceid " << request.getIncomingFaceId() << " not found");
+      sendResponse(request.getName(), 410, "Face not found");
+      return false;
+    }
+  else if (!face->isLocal())
+    {
+      NFD_LOG_DEBUG("command result: cannot enable local control on non-local faceid " <<
+                    face->getId());
+      sendResponse(request.getName(), 412, "Face is non-local");
+      return false;
+    }
+
+  outFace = dynamic_pointer_cast<LocalFace>(face);
+  outFeature = static_cast<LocalControlFeature>(parameters.getLocalControlFeature());
+
+  return true;
+}
+
+void
+FaceManager::enableLocalControl(const Interest& request,
+                                ControlParameters& parameters)
+{
+  ndn::nfd::FaceEnableLocalControlCommand command;
+
+
+  shared_ptr<LocalFace> face;
+  LocalControlFeature feature;
+
+  if (extractLocalControlParameters(request, parameters, command, face, feature))
+    {
+      face->setLocalControlHeaderFeature(feature, true);
+      sendResponse(request.getName(), 200, "Success", parameters.wireEncode());
+    }
+}
+
+void
+FaceManager::disableLocalControl(const Interest& request,
+                                 ControlParameters& parameters)
+{
+  ndn::nfd::FaceDisableLocalControlCommand command;
+  shared_ptr<LocalFace> face;
+  LocalControlFeature feature;
+
+  if (extractLocalControlParameters(request, parameters, command, face, feature))
+    {
+      face->setLocalControlHeaderFeature(feature, false);
+      sendResponse(request.getName(), 200, "Success", parameters.wireEncode());
+    }
+}
+
+void
+FaceManager::listFaces(const Interest& request)
+{
+  const Name& command = request.getName();
+  const size_t commandNComps = command.size();
+
+  if (commandNComps < FACES_LIST_DATASET_NCOMPS ||
+      !FACES_LIST_DATASET_PREFIX.isPrefixOf(command))
+    {
+      NFD_LOG_DEBUG("command result: malformed");
+      sendResponse(command, 400, "Malformed command");
+      return;
+    }
+
+  m_faceStatusPublisher.publish();
+}
+
+void
+FaceManager::listChannels(const Interest& request)
+{
+  NFD_LOG_DEBUG("in listChannels");
+  const Name& command = request.getName();
+  const size_t commandNComps = command.size();
+
+  if (commandNComps < CHANNELS_LIST_DATASET_NCOMPS ||
+      !CHANNELS_LIST_DATASET_PREFIX.isPrefixOf(command))
+    {
+      NFD_LOG_DEBUG("command result: malformed");
+      sendResponse(command, 400, "Malformed command");
+      return;
+    }
+
+  NFD_LOG_DEBUG("publishing");
+  m_channelStatusPublisher.publish();
+}
+
+void
+FaceManager::listQueriedFaces(const Interest& request)
+{
+  NFD_LOG_DEBUG("in listQueriedFaces");
+  const Name& query = request.getName();
+  const size_t queryNComps = query.size();
+
+  if (queryNComps < FACES_QUERY_DATASET_NCOMPS ||
+      !FACES_QUERY_DATASET_PREFIX.isPrefixOf(query))
+    {
+      NFD_LOG_DEBUG("query result: malformed");
+      sendNack(query);
+      return;
+    }
+
+  ndn::nfd::FaceQueryFilter faceFilter;
+  try
+    {
+      faceFilter.wireDecode(query[-1].blockFromValue());
+    }
+  catch (tlv::Error&)
+    {
+      NFD_LOG_DEBUG("query result: malformed filter");
+      sendNack(query);
+      return;
+    }
+
+  FaceQueryStatusPublisher
+    faceQueryStatusPublisher(m_faceTable, *m_face, query, faceFilter, m_keyChain);
+
+  faceQueryStatusPublisher.publish();
+}
+
+shared_ptr<ProtocolFactory>
+FaceManager::findFactory(const std::string& protocol)
+{
+  FactoryMap::iterator factory = m_factories.find(protocol);
+  if (factory != m_factories.end())
+    return factory->second;
+  else
+    return shared_ptr<ProtocolFactory>();
+}
+
+} // namespace nfd
diff --git a/NFD/daemon/mgmt/face-manager.hpp b/NFD/daemon/mgmt/face-manager.hpp
new file mode 100644
index 0000000..75477bf
--- /dev/null
+++ b/NFD/daemon/mgmt/face-manager.hpp
@@ -0,0 +1,250 @@
+/* -*- 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_DAEMON_MGMT_FACE_MANAGER_HPP
+#define NFD_DAEMON_MGMT_FACE_MANAGER_HPP
+
+#include "common.hpp"
+#include "core/notification-stream.hpp"
+#include "face/local-face.hpp"
+#include "mgmt/manager-base.hpp"
+#include "mgmt/face-status-publisher.hpp"
+#include "mgmt/channel-status-publisher.hpp"
+#include "mgmt/face-query-status-publisher.hpp"
+
+#include <ndn-cxx/management/nfd-control-parameters.hpp>
+#include <ndn-cxx/management/nfd-control-response.hpp>
+
+namespace nfd {
+
+const std::string FACE_MANAGER_PRIVILEGE = "faces";
+
+class ConfigFile;
+class Face;
+class FaceTable;
+class LocalFace;
+class NetworkInterfaceInfo;
+class ProtocolFactory;
+
+class FaceManager : public ManagerBase
+{
+public:
+  class Error : public ManagerBase::Error
+  {
+  public:
+    Error(const std::string& what) : ManagerBase::Error(what) {}
+  };
+
+  /**
+   * \throws FaceManager::Error if localPort is an invalid port number
+   */
+  FaceManager(FaceTable& faceTable,
+              shared_ptr<InternalFace> face,
+              ndn::KeyChain& keyChain);
+
+  virtual
+  ~FaceManager();
+
+  /** \brief Subscribe to a face management section(s) for the config file
+   */
+  void
+  setConfigFile(ConfigFile& configFile);
+
+  void
+  onFaceRequest(const Interest& request);
+
+PUBLIC_WITH_TESTS_ELSE_PRIVATE:
+  void
+  listFaces(const Interest& request);
+
+  void
+  listChannels(const Interest& request);
+
+  void
+  listQueriedFaces(const Interest& request);
+
+  shared_ptr<ProtocolFactory>
+  findFactory(const std::string& protocol);
+
+PROTECTED_WITH_TESTS_ELSE_PRIVATE:
+  void
+  onValidatedFaceRequest(const shared_ptr<const Interest>& request);
+
+  VIRTUAL_WITH_TESTS void
+  createFace(const Interest& request,
+             ControlParameters& parameters);
+
+  VIRTUAL_WITH_TESTS void
+  destroyFace(const Interest& request,
+              ControlParameters& parameters);
+
+  VIRTUAL_WITH_TESTS bool
+  extractLocalControlParameters(const Interest& request,
+                                ControlParameters& parameters,
+                                ControlCommand& command,
+                                shared_ptr<LocalFace>& outFace,
+                                LocalControlFeature& outFeature);
+
+  VIRTUAL_WITH_TESTS void
+  enableLocalControl(const Interest& request,
+                     ControlParameters& parambeters);
+
+  VIRTUAL_WITH_TESTS void
+  disableLocalControl(const Interest& request,
+                      ControlParameters& parameters);
+
+  void
+  ignoreUnsignedVerb(const Interest& request);
+
+  void
+  addCreatedFaceToForwarder(const shared_ptr<Face>& newFace);
+
+  void
+  onCreated(const Name& requestName,
+            ControlParameters& parameters,
+            const shared_ptr<Face>& newFace);
+
+  void
+  onConnectFailed(const Name& requestName, const std::string& reason);
+
+  void
+  onAddFace(shared_ptr<Face> face);
+
+  void
+  onRemoveFace(shared_ptr<Face> face);
+
+private:
+  void
+  onConfig(const ConfigSection& configSection, bool isDryRun, const std::string& filename);
+
+  void
+  processSectionUnix(const ConfigSection& configSection, bool isDryRun);
+
+  void
+  processSectionTcp(const ConfigSection& configSection, bool isDryRun);
+
+  void
+  processSectionUdp(const ConfigSection& configSection,
+                    bool isDryRun,
+                    const std::vector<NetworkInterfaceInfo>& nicList);
+
+  void
+  processSectionEther(const ConfigSection& configSection,
+                      bool isDryRun,
+                      const std::vector<NetworkInterfaceInfo>& nicList);
+
+  void
+  processSectionWebSocket(const ConfigSection& configSection, bool isDryRun);
+
+  /** \brief parse a config option that can be either "yes" or "no"
+   *  \throw ConfigFile::Error value is neither "yes" nor "no"
+   *  \return true if "yes", false if "no"
+   */
+  bool
+  parseYesNo(const ConfigSection::const_iterator& i,
+             const std::string& optionName,
+             const std::string& sectionName);
+
+PUBLIC_WITH_TESTS_ELSE_PRIVATE:
+  typedef std::map<std::string/*protocol*/, shared_ptr<ProtocolFactory>> FactoryMap;
+  FactoryMap m_factories;
+
+private:
+  FaceTable& m_faceTable;
+  signal::ScopedConnection m_faceAddConn;
+  signal::ScopedConnection m_faceRemoveConn;
+  FaceStatusPublisher m_faceStatusPublisher;
+  ChannelStatusPublisher m_channelStatusPublisher;
+  NotificationStream<AppFace> m_notificationStream;
+
+  typedef function<void(FaceManager*,
+                        const Interest&,
+                        ControlParameters&)> SignedVerbProcessor;
+
+  typedef std::map<Name::Component, SignedVerbProcessor> SignedVerbDispatchTable;
+  typedef std::pair<Name::Component, SignedVerbProcessor> SignedVerbAndProcessor;
+
+  typedef function<void(FaceManager*, const Interest&)> UnsignedVerbProcessor;
+
+  typedef std::map<Name::Component, UnsignedVerbProcessor> UnsignedVerbDispatchTable;
+  typedef std::pair<Name::Component, UnsignedVerbProcessor> UnsignedVerbAndProcessor;
+
+  const SignedVerbDispatchTable m_signedVerbDispatch;
+  const UnsignedVerbDispatchTable m_unsignedVerbDispatch;
+
+  static const Name COMMAND_PREFIX; // /localhost/nfd/faces
+
+  // number of components in an invalid signed command (i.e. should be signed, but isn't)
+  // (/localhost/nfd/faces + verb + parameters) = 5
+  static const size_t COMMAND_UNSIGNED_NCOMPS;
+
+  // number of components in a valid signed command.
+  // (see UNSIGNED_NCOMPS), 9 with signed Interest support.
+  static const size_t COMMAND_SIGNED_NCOMPS;
+
+  static const SignedVerbAndProcessor SIGNED_COMMAND_VERBS[];
+  static const UnsignedVerbAndProcessor UNSIGNED_COMMAND_VERBS[];
+
+  static const Name FACES_LIST_DATASET_PREFIX;
+  static const size_t FACES_LIST_DATASET_NCOMPS;
+
+  static const Name CHANNELS_LIST_DATASET_PREFIX;
+  static const size_t CHANNELS_LIST_DATASET_NCOMPS;
+
+  static const Name FACES_QUERY_DATASET_PREFIX;
+  static const size_t FACES_QUERY_DATASET_NCOMPS;
+
+  static const Name FACE_EVENTS_PREFIX;
+};
+
+inline bool
+FaceManager::parseYesNo(const ConfigSection::const_iterator& i,
+                        const std::string& optionName,
+                        const std::string& sectionName)
+{
+  const std::string value = i->second.get_value<std::string>();
+  if (value == "yes")
+    {
+      return true;
+    }
+  else if (value == "no")
+    {
+      return false;
+    }
+
+  throw ConfigFile::Error("Invalid value for option \"" +
+                          optionName + "\" in \"" +
+                          sectionName + "\" section");
+}
+
+inline void
+FaceManager::ignoreUnsignedVerb(const Interest& request)
+{
+  // do nothing
+}
+
+} // namespace nfd
+
+#endif // NFD_DAEMON_MGMT_FACE_MANAGER_HPP
diff --git a/NFD/daemon/mgmt/face-query-status-publisher.cpp b/NFD/daemon/mgmt/face-query-status-publisher.cpp
new file mode 100644
index 0000000..d208087
--- /dev/null
+++ b/NFD/daemon/mgmt/face-query-status-publisher.cpp
@@ -0,0 +1,108 @@
+/* -*- 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/>.
+ **/
+
+#include "face-query-status-publisher.hpp"
+#include "core/logger.hpp"
+#include <boost/range/adaptor/reversed.hpp>
+
+#include <ndn-cxx/management/nfd-face-status.hpp>
+
+namespace nfd {
+
+NFD_LOG_INIT("FaceQueryStatusPublisher");
+
+
+FaceQueryStatusPublisher::FaceQueryStatusPublisher(const FaceTable& faceTable,
+                                                   AppFace& face,
+                                                   const Name& prefix,
+                                                   const ndn::nfd::FaceQueryFilter& filter,
+                                                   ndn::KeyChain& keyChain)
+  : SegmentPublisher(face, prefix, keyChain)
+  , m_faceTable(faceTable)
+  , m_faceFilter(filter)
+{
+}
+
+FaceQueryStatusPublisher::~FaceQueryStatusPublisher()
+{
+}
+
+bool
+FaceQueryStatusPublisher::doesMatchFilter(const shared_ptr<Face>& face)
+{
+  if (m_faceFilter.hasFaceId() &&
+      m_faceFilter.getFaceId() != static_cast<uint64_t>(face->getId())) {
+    return false;
+  }
+
+  if (m_faceFilter.hasUriScheme() &&
+      (m_faceFilter.getUriScheme() != face->getRemoteUri().getScheme() ||
+       m_faceFilter.getUriScheme() != face->getLocalUri().getScheme())) {
+    return false;
+  }
+
+  if (m_faceFilter.hasRemoteUri() &&
+      m_faceFilter.getRemoteUri() != face->getRemoteUri().toString()) {
+    return false;
+  }
+
+  if (m_faceFilter.hasLocalUri() && m_faceFilter.getLocalUri() != face->getLocalUri().toString()) {
+    return false;
+  }
+
+  if (m_faceFilter.hasFaceScope() &&
+      (m_faceFilter.getFaceScope() == ndn::nfd::FACE_SCOPE_LOCAL) != face->isLocal()) {
+    return false;
+  }
+
+  if (m_faceFilter.hasFacePersistency() &&
+      (m_faceFilter.getFacePersistency() == ndn::nfd::FACE_PERSISTENCY_ON_DEMAND) !=
+      face->isOnDemand()) {
+    return false;
+  }
+
+  if (m_faceFilter.hasLinkType() &&
+      (m_faceFilter.getLinkType() == ndn::nfd::LINK_TYPE_MULTI_ACCESS) != face->isMultiAccess()) {
+    return false;
+  }
+
+  return true;
+}
+
+size_t
+FaceQueryStatusPublisher::generate(ndn::EncodingBuffer& outBuffer)
+{
+  size_t totalLength = 0;
+
+  for (const shared_ptr<Face>& face : m_faceTable | boost::adaptors::reversed) {
+    if (doesMatchFilter(face)) {
+      ndn::nfd::FaceStatus status = face->getFaceStatus();
+      totalLength += status.wireEncode(outBuffer);
+    }
+  }
+  return totalLength;
+}
+
+} // namespace nfd
diff --git a/NFD/daemon/mgmt/face-query-status-publisher.hpp b/NFD/daemon/mgmt/face-query-status-publisher.hpp
new file mode 100644
index 0000000..237cecb
--- /dev/null
+++ b/NFD/daemon/mgmt/face-query-status-publisher.hpp
@@ -0,0 +1,64 @@
+/* -*- 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_DAEMON_MGMT_QUERIED_FACE_STATUS_PUBLISHER_HPP
+#define NFD_DAEMON_MGMT_QUERIED_FACE_STATUS_PUBLISHER_HPP
+
+#include "core/segment-publisher.hpp"
+#include "mgmt/app-face.hpp"
+#include "fw/face-table.hpp"
+
+#include <ndn-cxx/management/nfd-face-query-filter.hpp>
+
+namespace nfd {
+
+class FaceQueryStatusPublisher : public SegmentPublisher<AppFace>
+{
+public:
+  FaceQueryStatusPublisher(const FaceTable& faceTable,
+                           AppFace& face,
+                           const Name& prefix,
+                           const ndn::nfd::FaceQueryFilter& filter,
+                           ndn::KeyChain& keyChain);
+
+  virtual
+  ~FaceQueryStatusPublisher();
+
+  bool
+  doesMatchFilter(const shared_ptr<Face>& face);
+
+protected:
+
+  virtual size_t
+  generate(ndn::EncodingBuffer& outBuffer);
+
+private:
+  const FaceTable& m_faceTable;
+  const ndn::nfd::FaceQueryFilter& m_faceFilter;
+};
+
+} // namespace nfd
+
+#endif // NFD_DAEMON_MGMT_QUERIED_FACE_STATUS_PUBLISHER_HPP
diff --git a/NFD/daemon/mgmt/face-status-publisher.cpp b/NFD/daemon/mgmt/face-status-publisher.cpp
new file mode 100644
index 0000000..759f9ce
--- /dev/null
+++ b/NFD/daemon/mgmt/face-status-publisher.cpp
@@ -0,0 +1,65 @@
+/* -*- 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
+ *
+ * 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-status-publisher.hpp"
+#include "core/logger.hpp"
+#include "fw/face-table.hpp"
+#include <boost/range/adaptor/reversed.hpp>
+
+#include <ndn-cxx/management/nfd-face-status.hpp>
+
+namespace nfd {
+
+NFD_LOG_INIT("FaceStatusPublisher");
+
+
+FaceStatusPublisher::FaceStatusPublisher(const FaceTable& faceTable,
+                                         AppFace& face,
+                                         const Name& prefix,
+                                         ndn::KeyChain& keyChain)
+  : SegmentPublisher(face, prefix, keyChain)
+  , m_faceTable(faceTable)
+{
+
+}
+
+
+FaceStatusPublisher::~FaceStatusPublisher()
+{
+
+}
+
+size_t
+FaceStatusPublisher::generate(ndn::EncodingBuffer& outBuffer)
+{
+  size_t totalLength = 0;
+
+  for (const shared_ptr<Face>& face : m_faceTable | boost::adaptors::reversed) {
+    ndn::nfd::FaceStatus status = face->getFaceStatus();
+    totalLength += status.wireEncode(outBuffer);
+  }
+  return totalLength;
+}
+
+} // namespace nfd
diff --git a/NFD/daemon/mgmt/face-status-publisher.hpp b/NFD/daemon/mgmt/face-status-publisher.hpp
new file mode 100644
index 0000000..5091165
--- /dev/null
+++ b/NFD/daemon/mgmt/face-status-publisher.hpp
@@ -0,0 +1,58 @@
+/* -*- 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_DAEMON_MGMT_FACE_STATUS_PUBLISHER_HPP
+#define NFD_DAEMON_MGMT_FACE_STATUS_PUBLISHER_HPP
+
+#include "core/segment-publisher.hpp"
+#include "mgmt/app-face.hpp"
+
+namespace nfd {
+
+class FaceTable;
+
+class FaceStatusPublisher : public SegmentPublisher<AppFace>
+{
+public:
+  FaceStatusPublisher(const FaceTable& faceTable,
+                      AppFace& face,
+                      const Name& prefix,
+                      ndn::KeyChain& keyChain);
+
+  virtual
+  ~FaceStatusPublisher();
+
+protected:
+
+  virtual size_t
+  generate(ndn::EncodingBuffer& outBuffer);
+
+private:
+  const FaceTable& m_faceTable;
+};
+
+} // namespace nfd
+
+#endif // NFD_DAEMON_MGMT_FACE_STATUS_PUBLISHER_HPP
diff --git a/NFD/daemon/mgmt/fib-enumeration-publisher.cpp b/NFD/daemon/mgmt/fib-enumeration-publisher.cpp
new file mode 100644
index 0000000..b7bad51
--- /dev/null
+++ b/NFD/daemon/mgmt/fib-enumeration-publisher.cpp
@@ -0,0 +1,87 @@
+/* -*- 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
+ *
+ * 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 "fib-enumeration-publisher.hpp"
+#include "core/logger.hpp"
+#include "table/fib.hpp"
+
+#include <ndn-cxx/management/nfd-fib-entry.hpp>
+
+namespace nfd {
+
+NFD_LOG_INIT("FibEnumerationPublisher");
+
+FibEnumerationPublisher::FibEnumerationPublisher(const Fib& fib,
+                                                 AppFace& face,
+                                                 const Name& prefix,
+                                                 ndn::KeyChain& keyChain)
+  : SegmentPublisher(face, prefix, keyChain)
+  , m_fib(fib)
+{
+}
+
+FibEnumerationPublisher::~FibEnumerationPublisher()
+{
+}
+
+size_t
+FibEnumerationPublisher::generate(ndn::EncodingBuffer& outBuffer)
+{
+  size_t totalLength = 0;
+
+  /// \todo Enable use of Fib::const_reverse_iterator (when it is available)
+  for (Fib::const_iterator i = m_fib.begin(); i != m_fib.end(); ++i)
+    {
+      const fib::Entry& entry = *i;
+      const Name& prefix = entry.getPrefix();
+      size_t fibEntryLength = 0;
+
+      ndn::nfd::FibEntry tlvEntry;
+      const fib::NextHopList& nextHops = entry.getNextHops();
+
+      for (fib::NextHopList::const_iterator j = nextHops.begin();
+           j != nextHops.end();
+           ++j)
+        {
+          const fib::NextHop& next = *j;
+          ndn::nfd::NextHopRecord nextHopRecord;
+          nextHopRecord.setFaceId(next.getFace()->getId());
+          nextHopRecord.setCost(next.getCost());
+
+          tlvEntry.addNextHopRecord(nextHopRecord);
+        }
+
+      tlvEntry.setPrefix(prefix);
+      fibEntryLength += tlvEntry.wireEncode(outBuffer);
+
+      NFD_LOG_DEBUG("generate: fib entry length = " << fibEntryLength);
+
+      totalLength += fibEntryLength;
+    }
+  NFD_LOG_DEBUG("generate: Total length = " << totalLength);
+  return totalLength;
+}
+
+
+} // namespace nfd
diff --git a/NFD/daemon/mgmt/fib-enumeration-publisher.hpp b/NFD/daemon/mgmt/fib-enumeration-publisher.hpp
new file mode 100644
index 0000000..2ff5c31
--- /dev/null
+++ b/NFD/daemon/mgmt/fib-enumeration-publisher.hpp
@@ -0,0 +1,58 @@
+/* -*- 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_DAEMON_MGMT_FIB_ENUMERATION_PUBLISHER_HPP
+#define NFD_DAEMON_MGMT_FIB_ENUMERATION_PUBLISHER_HPP
+
+#include "core/segment-publisher.hpp"
+#include "mgmt/app-face.hpp"
+
+namespace nfd {
+
+class Fib;
+
+class FibEnumerationPublisher : public SegmentPublisher<AppFace>
+{
+public:
+  FibEnumerationPublisher(const Fib& fib,
+                          AppFace& face,
+                          const Name& prefix,
+                          ndn::KeyChain& keyChain);
+
+  virtual
+  ~FibEnumerationPublisher();
+
+protected:
+
+  virtual size_t
+  generate(ndn::EncodingBuffer& outBuffer);
+
+private:
+  const Fib& m_fib;
+};
+
+} // namespace nfd
+
+#endif // NFD_DAEMON_MGMT_FIB_ENUMERATION_PUBLISHER_HPP
diff --git a/NFD/daemon/mgmt/fib-manager.cpp b/NFD/daemon/mgmt/fib-manager.cpp
new file mode 100644
index 0000000..3db26e7
--- /dev/null
+++ b/NFD/daemon/mgmt/fib-manager.cpp
@@ -0,0 +1,284 @@
+/* -*- 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/>.
+ */
+
+#include "fib-manager.hpp"
+
+#include "core/logger.hpp"
+#include "table/fib.hpp"
+#include "fw/forwarder.hpp"
+#include "mgmt/internal-face.hpp"
+#include "mgmt/app-face.hpp"
+
+#include <ndn-cxx/encoding/tlv.hpp>
+
+namespace nfd {
+
+NFD_LOG_INIT("FibManager");
+
+const Name FibManager::COMMAND_PREFIX = "/localhost/nfd/fib";
+
+const size_t FibManager::COMMAND_UNSIGNED_NCOMPS =
+  FibManager::COMMAND_PREFIX.size() +
+  1 + // verb
+  1;  // verb parameters
+
+const size_t FibManager::COMMAND_SIGNED_NCOMPS =
+  FibManager::COMMAND_UNSIGNED_NCOMPS +
+  4; // (timestamp, nonce, signed info tlv, signature tlv)
+
+const FibManager::SignedVerbAndProcessor FibManager::SIGNED_COMMAND_VERBS[] =
+  {
+
+    SignedVerbAndProcessor(
+                           Name::Component("add-nexthop"),
+                           &FibManager::addNextHop
+                           ),
+
+    SignedVerbAndProcessor(
+                           Name::Component("remove-nexthop"),
+                           &FibManager::removeNextHop
+                           ),
+
+  };
+
+const FibManager::UnsignedVerbAndProcessor FibManager::UNSIGNED_COMMAND_VERBS[] =
+  {
+    UnsignedVerbAndProcessor(
+                             Name::Component("list"),
+                             &FibManager::listEntries
+                             ),
+  };
+
+const Name FibManager::LIST_COMMAND_PREFIX("/localhost/nfd/fib/list");
+const size_t FibManager::LIST_COMMAND_NCOMPS = LIST_COMMAND_PREFIX.size();
+
+
+FibManager::FibManager(Fib& fib,
+                       function<shared_ptr<Face>(FaceId)> getFace,
+                       shared_ptr<InternalFace> face,
+                       ndn::KeyChain& keyChain)
+  : ManagerBase(face, FIB_PRIVILEGE, keyChain)
+  , m_managedFib(fib)
+  , m_getFace(getFace)
+  , m_fibEnumerationPublisher(fib, *face, LIST_COMMAND_PREFIX, keyChain)
+  , m_signedVerbDispatch(SIGNED_COMMAND_VERBS,
+                         SIGNED_COMMAND_VERBS +
+                         (sizeof(SIGNED_COMMAND_VERBS) / sizeof(SignedVerbAndProcessor)))
+  , m_unsignedVerbDispatch(UNSIGNED_COMMAND_VERBS,
+                           UNSIGNED_COMMAND_VERBS +
+                           (sizeof(UNSIGNED_COMMAND_VERBS) / sizeof(UnsignedVerbAndProcessor)))
+{
+  face->setInterestFilter("/localhost/nfd/fib",
+                          bind(&FibManager::onFibRequest, this, _2));
+}
+
+FibManager::~FibManager()
+{
+
+}
+
+void
+FibManager::onFibRequest(const Interest& request)
+{
+  const Name& command = request.getName();
+  const size_t commandNComps = command.size();
+
+  if (commandNComps <= COMMAND_PREFIX.size())
+    {
+      // command is too short to have a verb
+      NFD_LOG_DEBUG("command result: malformed");
+      sendResponse(command, 400, "Malformed command");
+      return;
+    }
+
+  const Name::Component& verb = command.at(COMMAND_PREFIX.size());
+
+  const auto unsignedVerbProcessor = m_unsignedVerbDispatch.find(verb);
+  if (unsignedVerbProcessor != m_unsignedVerbDispatch.end())
+    {
+      NFD_LOG_DEBUG("command result: processing verb: " << verb);
+      (unsignedVerbProcessor->second)(this, request);
+    }
+  else if (COMMAND_UNSIGNED_NCOMPS <= commandNComps &&
+           commandNComps < COMMAND_SIGNED_NCOMPS)
+    {
+      NFD_LOG_DEBUG("command result: unsigned verb: " << command);
+      sendResponse(command, 401, "Signature required");
+    }
+  else if (commandNComps < COMMAND_SIGNED_NCOMPS ||
+           !COMMAND_PREFIX.isPrefixOf(command))
+    {
+      NFD_LOG_DEBUG("command result: malformed");
+      sendResponse(command, 400, "Malformed command");
+    }
+  else
+    {
+      validate(request,
+               bind(&FibManager::onValidatedFibRequest, this, _1),
+               bind(&ManagerBase::onCommandValidationFailed, this, _1, _2));
+    }
+}
+
+void
+FibManager::onValidatedFibRequest(const shared_ptr<const Interest>& request)
+{
+  const Name& command = request->getName();
+  const Name::Component& verb = command[COMMAND_PREFIX.size()];
+  const Name::Component& parameterComponent = command[COMMAND_PREFIX.size() + 1];
+
+  SignedVerbDispatchTable::const_iterator verbProcessor = m_signedVerbDispatch.find(verb);
+  if (verbProcessor != m_signedVerbDispatch.end())
+    {
+      ControlParameters parameters;
+      if (!extractParameters(parameterComponent, parameters))
+        {
+          NFD_LOG_DEBUG("command result: malformed verb: " << verb);
+          sendResponse(command, 400, "Malformed command");
+          return;
+        }
+
+      bool isSelfRegistration = (!parameters.hasFaceId() || parameters.getFaceId() == 0);
+      if (isSelfRegistration)
+        {
+          parameters.setFaceId(request->getIncomingFaceId());
+        }
+
+      NFD_LOG_DEBUG("command result: processing verb: " << verb);
+      ControlResponse response;
+      (verbProcessor->second)(this, parameters, response);
+      sendResponse(command, response);
+    }
+  else
+    {
+      NFD_LOG_DEBUG("command result: unsupported verb: " << verb);
+      sendResponse(command, 501, "Unsupported command");
+    }
+}
+
+void
+FibManager::addNextHop(ControlParameters& parameters,
+                       ControlResponse& response)
+{
+  ndn::nfd::FibAddNextHopCommand command;
+
+  if (!validateParameters(command, parameters))
+    {
+      NFD_LOG_DEBUG("add-nexthop result: FAIL reason: malformed");
+      setResponse(response, 400, "Malformed command");
+      return;
+    }
+
+  const Name& prefix = parameters.getName();
+  FaceId faceId = parameters.getFaceId();
+  uint64_t cost = parameters.getCost();
+
+  NFD_LOG_TRACE("add-nexthop prefix: " << prefix
+                << " faceid: " << faceId
+                << " cost: " << cost);
+
+  shared_ptr<Face> nextHopFace = m_getFace(faceId);
+  if (static_cast<bool>(nextHopFace))
+    {
+      shared_ptr<fib::Entry> entry = m_managedFib.insert(prefix).first;
+
+      entry->addNextHop(nextHopFace, cost);
+
+      NFD_LOG_DEBUG("add-nexthop result: OK"
+                    << " prefix:" << prefix
+                    << " faceid: " << faceId
+                    << " cost: " << cost);
+
+      setResponse(response, 200, "Success", parameters.wireEncode());
+    }
+  else
+    {
+      NFD_LOG_DEBUG("add-nexthop result: FAIL reason: unknown-faceid: " << faceId);
+      setResponse(response, 410, "Face not found");
+    }
+}
+
+void
+FibManager::removeNextHop(ControlParameters& parameters,
+                          ControlResponse& response)
+{
+  ndn::nfd::FibRemoveNextHopCommand command;
+  if (!validateParameters(command, parameters))
+    {
+      NFD_LOG_DEBUG("remove-nexthop result: FAIL reason: malformed");
+      setResponse(response, 400, "Malformed command");
+      return;
+    }
+
+  NFD_LOG_TRACE("remove-nexthop prefix: " << parameters.getName()
+                << " faceid: " << parameters.getFaceId());
+
+  shared_ptr<Face> faceToRemove = m_getFace(parameters.getFaceId());
+  if (static_cast<bool>(faceToRemove))
+    {
+      shared_ptr<fib::Entry> entry = m_managedFib.findExactMatch(parameters.getName());
+      if (static_cast<bool>(entry))
+        {
+          entry->removeNextHop(faceToRemove);
+          NFD_LOG_DEBUG("remove-nexthop result: OK prefix: " << parameters.getName()
+                        << " faceid: " << parameters.getFaceId());
+
+          if (!entry->hasNextHops())
+            {
+              m_managedFib.erase(*entry);
+            }
+        }
+      else
+        {
+          NFD_LOG_DEBUG("remove-nexthop result: OK, but entry for face id "
+                        << parameters.getFaceId() << " not found");
+        }
+    }
+  else
+    {
+      NFD_LOG_DEBUG("remove-nexthop result: OK, but face id "
+                    << parameters.getFaceId() << " not found");
+    }
+
+  setResponse(response, 200, "Success", parameters.wireEncode());
+}
+
+void
+FibManager::listEntries(const Interest& request)
+{
+  const Name& command = request.getName();
+  const size_t commandNComps = command.size();
+
+  if (commandNComps < LIST_COMMAND_NCOMPS ||
+      !LIST_COMMAND_PREFIX.isPrefixOf(command))
+    {
+      NFD_LOG_DEBUG("command result: malformed");
+      sendResponse(command, 400, "Malformed command");
+      return;
+    }
+
+  m_fibEnumerationPublisher.publish();
+}
+
+} // namespace nfd
diff --git a/NFD/daemon/mgmt/fib-manager.hpp b/NFD/daemon/mgmt/fib-manager.hpp
new file mode 100644
index 0000000..c8c21ea
--- /dev/null
+++ b/NFD/daemon/mgmt/fib-manager.hpp
@@ -0,0 +1,114 @@
+/* -*- 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_DAEMON_MGMT_FIB_MANAGER_HPP
+#define NFD_DAEMON_MGMT_FIB_MANAGER_HPP
+
+#include "common.hpp"
+#include "mgmt/manager-base.hpp"
+#include "mgmt/fib-enumeration-publisher.hpp"
+
+namespace nfd {
+
+class Face;
+class Forwarder;
+class Fib;
+
+const std::string FIB_PRIVILEGE = "fib"; // config file privilege name
+
+class FibManager : public ManagerBase
+{
+public:
+
+  FibManager(Fib& fib,
+             function<shared_ptr<Face>(FaceId)> getFace,
+             shared_ptr<InternalFace> face,
+             ndn::KeyChain& keyChain);
+
+  virtual
+  ~FibManager();
+
+  void
+  onFibRequest(const Interest& request);
+
+private:
+
+  void
+  onValidatedFibRequest(const shared_ptr<const Interest>& request);
+
+  void
+  addNextHop(ControlParameters& parameters,
+             ControlResponse& response);
+
+  void
+  removeNextHop(ControlParameters& parameters,
+                ControlResponse& response);
+
+  void
+  listEntries(const Interest& request);
+
+private:
+
+  Fib& m_managedFib;
+  function<shared_ptr<Face>(FaceId)> m_getFace;
+  FibEnumerationPublisher m_fibEnumerationPublisher;
+
+  typedef function<void(FibManager*,
+                        ControlParameters&,
+                        ControlResponse&)> SignedVerbProcessor;
+
+  typedef std::map<Name::Component, SignedVerbProcessor> SignedVerbDispatchTable;
+
+  typedef std::pair<Name::Component, SignedVerbProcessor> SignedVerbAndProcessor;
+
+  typedef function<void(FibManager*, const Interest&)> UnsignedVerbProcessor;
+
+  typedef std::map<Name::Component, UnsignedVerbProcessor> UnsignedVerbDispatchTable;
+  typedef std::pair<Name::Component, UnsignedVerbProcessor> UnsignedVerbAndProcessor;
+
+
+  const SignedVerbDispatchTable m_signedVerbDispatch;
+  const UnsignedVerbDispatchTable m_unsignedVerbDispatch;
+
+  static const Name COMMAND_PREFIX; // /localhost/nfd/fib
+
+  // number of components in an invalid, but not malformed, unsigned command.
+  // (/localhost/nfd/fib + verb + parameters) = 5
+  static const size_t COMMAND_UNSIGNED_NCOMPS;
+
+  // number of components in a valid signed Interest.
+  // UNSIGNED_NCOMPS + 4 command Interest components = 9
+  static const size_t COMMAND_SIGNED_NCOMPS;
+
+  static const SignedVerbAndProcessor SIGNED_COMMAND_VERBS[];
+  static const UnsignedVerbAndProcessor UNSIGNED_COMMAND_VERBS[];
+
+  static const Name LIST_COMMAND_PREFIX;
+  static const size_t LIST_COMMAND_NCOMPS;
+};
+
+} // namespace nfd
+
+#endif // NFD_DAEMON_MGMT_FIB_MANAGER_HPP
diff --git a/NFD/daemon/mgmt/general-config-section.cpp b/NFD/daemon/mgmt/general-config-section.cpp
new file mode 100644
index 0000000..02f45e8
--- /dev/null
+++ b/NFD/daemon/mgmt/general-config-section.cpp
@@ -0,0 +1,106 @@
+/* -*- 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
+ *
+ * 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 "general-config-section.hpp"
+
+#include "common.hpp"
+#include "core/logger.hpp"
+#include "core/privilege-helper.hpp"
+#include "core/config-file.hpp"
+
+namespace nfd {
+
+namespace general {
+
+NFD_LOG_INIT("GeneralConfigSection");
+
+static void
+onConfig(const ConfigSection& configSection,
+         bool isDryRun,
+         const std::string& filename)
+{
+  // general
+  // {
+  //    ; user "ndn-user"
+  //    ; group "ndn-user"
+  // }
+
+  std::string user;
+  std::string group;
+
+  for (ConfigSection::const_iterator i = configSection.begin();
+       i != configSection.end();
+       ++i)
+    {
+      if (i->first == "user")
+        {
+          try
+            {
+              user = i->second.get_value<std::string>("user");
+
+              if (user.empty())
+                {
+                  throw ConfigFile::Error("Invalid value for \"user\""
+                                          " in \"general\" section");
+                }
+            }
+          catch (const boost::property_tree::ptree_error& error)
+            {
+              throw ConfigFile::Error("Invalid value for \"user\""
+                                      " in \"general\" section");
+            }
+        }
+      else if (i->first == "group")
+        {
+          try
+            {
+              group = i->second.get_value<std::string>("group");
+
+              if (group.empty())
+                {
+                  throw ConfigFile::Error("Invalid value for \"group\""
+                                          " in \"general\" section");
+                }
+            }
+          catch (const boost::property_tree::ptree_error& error)
+            {
+              throw ConfigFile::Error("Invalid value for \"group\""
+                                      " in \"general\" section");
+            }
+        }
+    }
+  NFD_LOG_TRACE("using user \"" << user << "\" group \"" << group << "\"");
+
+  PrivilegeHelper::initialize(user, group);
+}
+
+void
+setConfigFile(ConfigFile& configFile)
+{
+  configFile.addSectionHandler("general", &onConfig);
+}
+
+} // namespace general
+
+} // namespace nfd
diff --git a/NFD/daemon/mgmt/general-config-section.hpp b/NFD/daemon/mgmt/general-config-section.hpp
new file mode 100644
index 0000000..6ce5473
--- /dev/null
+++ b/NFD/daemon/mgmt/general-config-section.hpp
@@ -0,0 +1,41 @@
+/* -*- 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
+ *
+ * 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_MGMT_GENERAL_CONFIG_SECTION_HPP
+#define NFD_MGMT_GENERAL_CONFIG_SECTION_HPP
+
+namespace nfd {
+
+class ConfigFile;
+
+namespace general {
+
+void
+setConfigFile(ConfigFile& configFile);
+
+} // namespace general
+
+} // namespace nfd
+
+#endif // NFD_MGMT_GENERAL_CONFIG_SECTION_HPP
diff --git a/NFD/daemon/mgmt/internal-face.cpp b/NFD/daemon/mgmt/internal-face.cpp
new file mode 100644
index 0000000..9fad328
--- /dev/null
+++ b/NFD/daemon/mgmt/internal-face.cpp
@@ -0,0 +1,153 @@
+/* -*- 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/>.
+ */
+
+#include "internal-face.hpp"
+#include "core/logger.hpp"
+#include "core/global-io.hpp"
+
+namespace nfd {
+
+NFD_LOG_INIT("InternalFace");
+
+InternalFace::InternalFace()
+  : Face(FaceUri("internal://"), FaceUri("internal://"), true)
+{
+}
+
+void
+InternalFace::sendInterest(const Interest& interest)
+{
+  onSendInterest(interest);
+
+  // Invoke .processInterest a bit later,
+  // to avoid potential problems in forwarding pipelines.
+  getGlobalIoService().post(bind(&InternalFace::processInterest,
+                                 this, interest.shared_from_this()));
+}
+
+void
+InternalFace::processInterest(const shared_ptr<const Interest>& interest)
+{
+  if (m_interestFilters.size() == 0)
+    {
+      NFD_LOG_DEBUG("no Interest filters to match against");
+      return;
+    }
+
+  const Name& interestName(interest->getName());
+  NFD_LOG_DEBUG("received Interest: " << interestName);
+
+  std::map<Name, OnInterest>::const_iterator filter =
+    m_interestFilters.lower_bound(interestName);
+
+  // lower_bound gives us the first Name that is
+  // an exact match OR ordered after interestName.
+  //
+  // If we reach the end of the map, then we need
+  // only check if the before-end element is a match.
+  //
+  // If we match an element, then the current
+  // position or the previous element are potential
+  // matches.
+  //
+  // If we hit begin, the element is either an exact
+  // match or there is no matching prefix in the map.
+
+
+  if (filter == m_interestFilters.end() && filter != m_interestFilters.begin())
+    {
+      // We hit the end, check if the previous element
+      // is a match
+      --filter;
+      if (filter->first.isPrefixOf(interestName))
+        {
+          NFD_LOG_DEBUG("found Interest filter for " << filter->first << " (before end match)");
+          filter->second(interestName, *interest);
+        }
+      else
+        {
+          NFD_LOG_DEBUG("no Interest filter found for " << interestName << " (before end)");
+        }
+    }
+  else if (filter->first == interestName)
+    {
+      NFD_LOG_DEBUG("found Interest filter for " << filter->first << " (exact match)");
+      filter->second(interestName, *interest);
+    }
+  else if (filter != m_interestFilters.begin())
+    {
+      // the element we found is canonically
+      // ordered after interestName.
+      // Check the previous element.
+      --filter;
+      if (filter->first.isPrefixOf(interestName))
+        {
+          NFD_LOG_DEBUG("found Interest filter for " << filter->first << " (previous match)");
+          filter->second(interestName, *interest);
+        }
+      else
+        {
+          NFD_LOG_DEBUG("no Interest filter found for " << interestName << " (previous)");
+        }
+    }
+  else
+    {
+      NFD_LOG_DEBUG("no Interest filter found for " << interestName << " (begin)");
+    }
+  //Drop Interest
+}
+
+void
+InternalFace::sendData(const Data& data)
+{
+  onSendData(data);
+}
+
+void
+InternalFace::close()
+{
+  throw Error("Internal face cannot be closed");
+}
+
+void
+InternalFace::setInterestFilter(const Name& filter,
+                                OnInterest onInterest)
+{
+  NFD_LOG_INFO("registering callback for " << filter);
+  m_interestFilters[filter] = onInterest;
+}
+
+void
+InternalFace::put(const Data& data)
+{
+  onReceiveData(data);
+}
+
+InternalFace::~InternalFace()
+{
+
+}
+
+} // namespace nfd
diff --git a/NFD/daemon/mgmt/internal-face.hpp b/NFD/daemon/mgmt/internal-face.hpp
new file mode 100644
index 0000000..c35676f
--- /dev/null
+++ b/NFD/daemon/mgmt/internal-face.hpp
@@ -0,0 +1,98 @@
+/* -*- 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_DAEMON_MGMT_INTERNAL_FACE_HPP
+#define NFD_DAEMON_MGMT_INTERNAL_FACE_HPP
+
+#include "face/face.hpp"
+#include "app-face.hpp"
+
+#include "command-validator.hpp"
+
+namespace nfd {
+
+class InternalFace : public Face, public AppFace
+{
+public:
+  /**
+   * \brief InternalFace-related error
+   */
+  class Error : public Face::Error
+  {
+  public:
+    explicit
+    Error(const std::string& what)
+      : Face::Error(what)
+    {
+    }
+  };
+
+  InternalFace();
+
+  CommandValidator&
+  getValidator();
+
+  virtual
+  ~InternalFace();
+
+  // Overridden Face methods for forwarder
+
+  virtual void
+  sendInterest(const Interest& interest);
+
+  virtual void
+  sendData(const Data& data);
+
+  virtual void
+  close();
+
+  // Methods implementing AppFace interface. Do not invoke from forwarder.
+
+  virtual void
+  setInterestFilter(const Name& filter,
+                    OnInterest onInterest);
+
+  virtual void
+  put(const Data& data);
+
+private:
+  void
+  processInterest(const shared_ptr<const Interest>& interest);
+
+private:
+  std::map<Name, OnInterest> m_interestFilters;
+  CommandValidator m_validator;
+};
+
+inline CommandValidator&
+InternalFace::getValidator()
+{
+  return m_validator;
+}
+
+
+} // namespace nfd
+
+#endif // NFD_DAEMON_MGMT_INTERNAL_FACE_HPP
diff --git a/NFD/daemon/mgmt/manager-base.cpp b/NFD/daemon/mgmt/manager-base.cpp
new file mode 100644
index 0000000..cf75d04
--- /dev/null
+++ b/NFD/daemon/mgmt/manager-base.cpp
@@ -0,0 +1,144 @@
+/* -*- 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/>.
+ */
+
+#include "manager-base.hpp"
+#include "core/logger.hpp"
+
+namespace nfd {
+
+NFD_LOG_INIT("ManagerBase");
+
+ManagerBase::ManagerBase(shared_ptr<InternalFace> face, const std::string& privilege,
+                         ndn::KeyChain& keyChain)
+  : m_face(face)
+  , m_keyChain(keyChain)
+{
+  face->getValidator().addSupportedPrivilege(privilege);
+}
+
+ManagerBase::~ManagerBase()
+{
+
+}
+
+bool
+ManagerBase::extractParameters(const Name::Component& parameterComponent,
+                               ControlParameters& extractedParameters)
+{
+  try
+    {
+      Block rawParameters = parameterComponent.blockFromValue();
+      extractedParameters.wireDecode(rawParameters);
+    }
+  catch (const tlv::Error&)
+    {
+      return false;
+    }
+
+  NFD_LOG_DEBUG("Parameters parsed OK");
+  return true;
+}
+
+void
+ManagerBase::sendResponse(const Name& name,
+                          uint32_t code,
+                          const std::string& text)
+{
+  ControlResponse response(code, text);
+  sendResponse(name, response);
+}
+
+void
+ManagerBase::sendResponse(const Name& name,
+                          uint32_t code,
+                          const std::string& text,
+                          const Block& body)
+{
+  ControlResponse response(code, text);
+  response.setBody(body);
+  sendResponse(name, response);
+}
+
+void
+ManagerBase::sendResponse(const Name& name,
+                          const ControlResponse& response)
+{
+  NFD_LOG_DEBUG("responding"
+                << " name: " << name
+                << " code: " << response.getCode()
+                << " text: " << response.getText());
+
+  const Block& encodedControl = response.wireEncode();
+
+  shared_ptr<Data> responseData(make_shared<Data>(name));
+  responseData->setContent(encodedControl);
+
+  m_keyChain.sign(*responseData);
+  m_face->put(*responseData);
+}
+
+void
+ManagerBase::sendNack(const Name& name)
+{
+  NFD_LOG_DEBUG("responding NACK to " << name);
+
+  ndn::MetaInfo meta;
+  meta.setType(tlv::ContentType_Nack);
+
+  shared_ptr<Data> responseData(make_shared<Data>(name));
+  responseData->setMetaInfo(meta);
+
+  m_keyChain.sign(*responseData);
+  m_face->put(*responseData);
+}
+
+bool
+ManagerBase::validateParameters(const ControlCommand& command,
+                                ControlParameters& parameters)
+{
+  try
+    {
+      command.validateRequest(parameters);
+    }
+  catch (const ControlCommand::ArgumentError& error)
+    {
+      return false;
+    }
+
+  command.applyDefaultsToRequest(parameters);
+
+  return true;
+}
+
+void
+ManagerBase::onCommandValidationFailed(const shared_ptr<const Interest>& command,
+                                       const std::string& error)
+{
+  NFD_LOG_DEBUG("command result: unauthorized command: " << *command << " (" << error << ")");
+  sendResponse(command->getName(), 403, "Unauthorized command");
+}
+
+
+} // namespace nfd
diff --git a/NFD/daemon/mgmt/manager-base.hpp b/NFD/daemon/mgmt/manager-base.hpp
new file mode 100644
index 0000000..1611615
--- /dev/null
+++ b/NFD/daemon/mgmt/manager-base.hpp
@@ -0,0 +1,169 @@
+/* -*- 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_DAEMON_MGMT_MANAGER_BASE_HPP
+#define NFD_DAEMON_MGMT_MANAGER_BASE_HPP
+
+#include "common.hpp"
+
+#include "mgmt/command-validator.hpp"
+#include "mgmt/internal-face.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>
+
+namespace nfd {
+
+using ndn::nfd::ControlCommand;
+using ndn::nfd::ControlResponse;
+using ndn::nfd::ControlParameters;
+
+class InternalFace;
+
+class ManagerBase
+{
+public:
+
+  struct Error : public std::runtime_error
+  {
+    Error(const std::string& what) : std::runtime_error(what) {}
+  };
+
+  ManagerBase(shared_ptr<InternalFace> face,
+              const std::string& privilege,
+              ndn::KeyChain& keyChain);
+
+  virtual
+  ~ManagerBase();
+
+  void
+  onCommandValidationFailed(const shared_ptr<const Interest>& command,
+                            const std::string& error);
+
+protected:
+
+  static bool
+  extractParameters(const Name::Component& parameterComponent,
+                    ControlParameters& extractedParameters);
+
+  void
+  setResponse(ControlResponse& response,
+              uint32_t code,
+              const std::string& text);
+  void
+  setResponse(ControlResponse& response,
+              uint32_t code,
+              const std::string& text,
+              const Block& body);
+
+  void
+  sendResponse(const Name& name,
+               const ControlResponse& response);
+
+  void
+  sendResponse(const Name& name,
+               uint32_t code,
+               const std::string& text);
+
+  void
+  sendResponse(const Name& name,
+               uint32_t code,
+               const std::string& text,
+               const Block& body);
+
+  void
+  sendNack(const Name& name);
+
+  virtual bool
+  validateParameters(const ControlCommand& command,
+                     ControlParameters& parameters);
+
+PUBLIC_WITH_TESTS_ELSE_PROTECTED:
+  void
+  addInterestRule(const std::string& regex,
+                  const ndn::IdentityCertificate& certificate);
+
+  void
+  addInterestRule(const std::string& regex,
+                  const Name& keyName,
+                  const ndn::PublicKey& publicKey);
+
+  void
+  validate(const Interest& interest,
+           const ndn::OnInterestValidated& onValidated,
+           const ndn::OnInterestValidationFailed& onValidationFailed);
+
+protected:
+  shared_ptr<InternalFace> m_face;
+  ndn::KeyChain& m_keyChain;
+};
+
+inline void
+ManagerBase::setResponse(ControlResponse& response,
+                         uint32_t code,
+                         const std::string& text)
+{
+  response.setCode(code);
+  response.setText(text);
+}
+
+inline void
+ManagerBase::setResponse(ControlResponse& response,
+                         uint32_t code,
+                         const std::string& text,
+                         const Block& body)
+{
+  setResponse(response, code, text);
+  response.setBody(body);
+}
+
+inline void
+ManagerBase::addInterestRule(const std::string& regex,
+                             const ndn::IdentityCertificate& certificate)
+{
+  m_face->getValidator().addInterestRule(regex, certificate);
+}
+
+inline void
+ManagerBase::addInterestRule(const std::string& regex,
+                             const Name& keyName,
+                             const ndn::PublicKey& publicKey)
+{
+  m_face->getValidator().addInterestRule(regex, keyName, publicKey);
+}
+
+inline void
+ManagerBase::validate(const Interest& interest,
+                      const ndn::OnInterestValidated& onValidated,
+                      const ndn::OnInterestValidationFailed& onValidationFailed)
+{
+  m_face->getValidator().validate(interest, onValidated, onValidationFailed);
+}
+
+
+} // namespace nfd
+
+#endif // NFD_DAEMON_MGMT_MANAGER_BASE_HPP
diff --git a/NFD/daemon/mgmt/status-server.cpp b/NFD/daemon/mgmt/status-server.cpp
new file mode 100644
index 0000000..c8ce5b4
--- /dev/null
+++ b/NFD/daemon/mgmt/status-server.cpp
@@ -0,0 +1,81 @@
+/* -*- 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/>.
+ */
+
+#include "status-server.hpp"
+#include "fw/forwarder.hpp"
+#include "version.hpp"
+
+namespace nfd {
+
+const Name StatusServer::DATASET_PREFIX = "ndn:/localhost/nfd/status";
+const time::milliseconds StatusServer::RESPONSE_FRESHNESS = time::milliseconds(5000);
+
+StatusServer::StatusServer(shared_ptr<AppFace> face, Forwarder& forwarder, ndn::KeyChain& keyChain)
+  : m_face(face)
+  , m_forwarder(forwarder)
+  , m_startTimestamp(time::system_clock::now())
+  , m_keyChain(keyChain)
+{
+  m_face->setInterestFilter(DATASET_PREFIX, bind(&StatusServer::onInterest, this, _2));
+}
+
+void
+StatusServer::onInterest(const Interest& interest) const
+{
+  Name name(DATASET_PREFIX);
+  name.appendVersion();
+  name.appendSegment(0);
+
+  shared_ptr<Data> data = make_shared<Data>(name);
+  data->setFreshnessPeriod(RESPONSE_FRESHNESS);
+
+  shared_ptr<ndn::nfd::ForwarderStatus> status = this->collectStatus();
+  data->setContent(status->wireEncode());
+
+  m_keyChain.sign(*data);
+  m_face->put(*data);
+}
+
+shared_ptr<ndn::nfd::ForwarderStatus>
+StatusServer::collectStatus() const
+{
+  shared_ptr<ndn::nfd::ForwarderStatus> status = make_shared<ndn::nfd::ForwarderStatus>();
+
+  status->setNfdVersion(NFD_VERSION_BUILD_STRING);
+  status->setStartTimestamp(m_startTimestamp);
+  status->setCurrentTimestamp(time::system_clock::now());
+
+  status->setNNameTreeEntries(m_forwarder.getNameTree().size());
+  status->setNFibEntries(m_forwarder.getFib().size());
+  status->setNPitEntries(m_forwarder.getPit().size());
+  status->setNMeasurementsEntries(m_forwarder.getMeasurements().size());
+  status->setNCsEntries(m_forwarder.getCs().size());
+
+  m_forwarder.getCounters().copyTo(*status);
+
+  return status;
+}
+
+} // namespace nfd
diff --git a/NFD/daemon/mgmt/status-server.hpp b/NFD/daemon/mgmt/status-server.hpp
new file mode 100644
index 0000000..16dbf8e
--- /dev/null
+++ b/NFD/daemon/mgmt/status-server.hpp
@@ -0,0 +1,60 @@
+/* -*- 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_DAEMON_MGMT_STATUS_SERVER_HPP
+#define NFD_DAEMON_MGMT_STATUS_SERVER_HPP
+
+#include "mgmt/app-face.hpp"
+#include <ndn-cxx/management/nfd-forwarder-status.hpp>
+
+namespace nfd {
+
+class Forwarder;
+
+class StatusServer : noncopyable
+{
+public:
+  StatusServer(shared_ptr<AppFace> face, Forwarder& forwarder, ndn::KeyChain& keyChain);
+
+private:
+  void
+  onInterest(const Interest& interest) const;
+
+  shared_ptr<ndn::nfd::ForwarderStatus>
+  collectStatus() const;
+
+private:
+  static const Name DATASET_PREFIX;
+  static const time::milliseconds RESPONSE_FRESHNESS;
+
+  shared_ptr<AppFace> m_face;
+  Forwarder& m_forwarder;
+  time::system_clock::TimePoint m_startTimestamp;
+  ndn::KeyChain& m_keyChain;
+};
+
+} // namespace nfd
+
+#endif // NFD_DAEMON_MGMT_STATUS_SERVER_HPP
diff --git a/NFD/daemon/mgmt/strategy-choice-manager.cpp b/NFD/daemon/mgmt/strategy-choice-manager.cpp
new file mode 100644
index 0000000..4f2728c
--- /dev/null
+++ b/NFD/daemon/mgmt/strategy-choice-manager.cpp
@@ -0,0 +1,214 @@
+/* -*- 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/>.
+ */
+
+#include "strategy-choice-manager.hpp"
+#include "table/strategy-choice.hpp"
+#include "core/logger.hpp"
+#include "mgmt/app-face.hpp"
+
+namespace nfd {
+
+NFD_LOG_INIT("StrategyChoiceManager");
+
+const Name StrategyChoiceManager::COMMAND_PREFIX = "/localhost/nfd/strategy-choice";
+
+const size_t StrategyChoiceManager::COMMAND_UNSIGNED_NCOMPS =
+  StrategyChoiceManager::COMMAND_PREFIX.size() +
+  1 + // verb
+  1;  // verb parameters
+
+const size_t StrategyChoiceManager::COMMAND_SIGNED_NCOMPS =
+  StrategyChoiceManager::COMMAND_UNSIGNED_NCOMPS +
+  4; // (timestamp, nonce, signed info tlv, signature tlv)
+
+const Name StrategyChoiceManager::LIST_DATASET_PREFIX("/localhost/nfd/strategy-choice/list");
+
+StrategyChoiceManager::StrategyChoiceManager(StrategyChoice& strategyChoice,
+                                             shared_ptr<InternalFace> face,
+                                             ndn::KeyChain& keyChain)
+  : ManagerBase(face, STRATEGY_CHOICE_PRIVILEGE, keyChain)
+  , m_strategyChoice(strategyChoice)
+  , m_listPublisher(strategyChoice, *m_face, LIST_DATASET_PREFIX, keyChain)
+{
+  face->setInterestFilter("/localhost/nfd/strategy-choice",
+                          bind(&StrategyChoiceManager::onStrategyChoiceRequest, this, _2));
+}
+
+StrategyChoiceManager::~StrategyChoiceManager()
+{
+
+}
+
+void
+StrategyChoiceManager::onStrategyChoiceRequest(const Interest& request)
+{
+  const Name& command = request.getName();
+  const size_t commandNComps = command.size();
+
+  if (command == LIST_DATASET_PREFIX)
+    {
+      listStrategies(request);
+      return;
+    }
+  else if (commandNComps <= COMMAND_PREFIX.size())
+    {
+      // command is too short to have a verb
+      NFD_LOG_DEBUG("command result: malformed");
+      sendResponse(command, 400, "Malformed command");
+      return;
+    }
+
+  if (COMMAND_UNSIGNED_NCOMPS <= commandNComps &&
+      commandNComps < COMMAND_SIGNED_NCOMPS)
+    {
+      NFD_LOG_DEBUG("command result: unsigned verb: " << command);
+      sendResponse(command, 401, "Signature required");
+
+      return;
+    }
+  else if (commandNComps < COMMAND_SIGNED_NCOMPS ||
+           !COMMAND_PREFIX.isPrefixOf(command))
+    {
+      NFD_LOG_DEBUG("command result: malformed");
+      sendResponse(command, 400, "Malformed command");
+      return;
+    }
+
+  validate(request,
+           bind(&StrategyChoiceManager::onValidatedStrategyChoiceRequest, this, _1),
+           bind(&ManagerBase::onCommandValidationFailed, this, _1, _2));
+}
+
+void
+StrategyChoiceManager::listStrategies(const Interest& request)
+{
+  m_listPublisher.publish();
+}
+
+void
+StrategyChoiceManager::onValidatedStrategyChoiceRequest(const shared_ptr<const Interest>& request)
+{
+  static const Name::Component VERB_SET("set");
+  static const Name::Component VERB_UNSET("unset");
+
+  const Name& command = request->getName();
+  const Name::Component& parameterComponent = command[COMMAND_PREFIX.size() + 1];
+
+  ControlParameters parameters;
+  if (!extractParameters(parameterComponent, parameters))
+    {
+      sendResponse(command, 400, "Malformed command");
+      return;
+    }
+
+  const Name::Component& verb = command.at(COMMAND_PREFIX.size());
+  ControlResponse response;
+  if (verb == VERB_SET)
+    {
+      setStrategy(parameters, response);
+    }
+  else if (verb == VERB_UNSET)
+    {
+      unsetStrategy(parameters, response);
+    }
+  else
+    {
+      NFD_LOG_DEBUG("command result: unsupported verb: " << verb);
+      setResponse(response, 501, "Unsupported command");
+    }
+
+  sendResponse(command, response);
+}
+
+void
+StrategyChoiceManager::setStrategy(ControlParameters& parameters,
+                                   ControlResponse& response)
+{
+  ndn::nfd::StrategyChoiceSetCommand command;
+
+  if (!validateParameters(command, parameters))
+    {
+      NFD_LOG_DEBUG("strategy-choice result: FAIL reason: malformed");
+      setResponse(response, 400, "Malformed command");
+      return;
+    }
+
+  const Name& prefix = parameters.getName();
+  const Name& selectedStrategy = parameters.getStrategy();
+
+  if (!m_strategyChoice.hasStrategy(selectedStrategy))
+    {
+      NFD_LOG_DEBUG("strategy-choice result: FAIL reason: unknown-strategy: "
+                    << parameters.getStrategy());
+      setResponse(response, 504, "Unsupported strategy");
+      return;
+    }
+
+  if (m_strategyChoice.insert(prefix, selectedStrategy))
+    {
+      NFD_LOG_DEBUG("strategy-choice result: SUCCESS");
+      auto currentStrategyChoice = m_strategyChoice.get(prefix);
+      BOOST_ASSERT(currentStrategyChoice.first);
+      parameters.setStrategy(currentStrategyChoice.second);
+      setResponse(response, 200, "Success", parameters.wireEncode());
+    }
+  else
+    {
+      NFD_LOG_DEBUG("strategy-choice result: FAIL reason: not-installed");
+      setResponse(response, 405, "Strategy not installed");
+    }
+}
+
+void
+StrategyChoiceManager::unsetStrategy(ControlParameters& parameters,
+                                     ControlResponse& response)
+{
+  ndn::nfd::StrategyChoiceUnsetCommand command;
+
+  if (!validateParameters(command, parameters))
+    {
+      static const Name ROOT_PREFIX;
+      if (parameters.hasName() && parameters.getName() == ROOT_PREFIX)
+        {
+          NFD_LOG_DEBUG("strategy-choice result: FAIL reason: unset-root");
+          setResponse(response, 403, "Cannot unset root prefix strategy");
+        }
+      else
+        {
+          NFD_LOG_DEBUG("strategy-choice result: FAIL reason: malformed");
+          setResponse(response, 400, "Malformed command");
+        }
+      return;
+    }
+
+  m_strategyChoice.erase(parameters.getName());
+
+  NFD_LOG_DEBUG("strategy-choice result: SUCCESS");
+  setResponse(response, 200, "Success", parameters.wireEncode());
+}
+
+
+
+} // namespace nfd
diff --git a/NFD/daemon/mgmt/strategy-choice-manager.hpp b/NFD/daemon/mgmt/strategy-choice-manager.hpp
new file mode 100644
index 0000000..c2ccd53
--- /dev/null
+++ b/NFD/daemon/mgmt/strategy-choice-manager.hpp
@@ -0,0 +1,91 @@
+/* -*- 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_DAEMON_MGMT_STRATEGY_CHOICE_MANAGER_HPP
+#define NFD_DAEMON_MGMT_STRATEGY_CHOICE_MANAGER_HPP
+
+#include "mgmt/manager-base.hpp"
+#include "mgmt/strategy-choice-publisher.hpp"
+
+#include <ndn-cxx/management/nfd-control-parameters.hpp>
+
+namespace nfd {
+
+const std::string STRATEGY_CHOICE_PRIVILEGE = "strategy-choice";
+
+class StrategyChoice;
+
+class StrategyChoiceManager : public ManagerBase
+{
+public:
+  StrategyChoiceManager(StrategyChoice& strategyChoice,
+                        shared_ptr<InternalFace> face,
+                        ndn::KeyChain& keyChain);
+
+  virtual
+  ~StrategyChoiceManager();
+
+  void
+  onStrategyChoiceRequest(const Interest& request);
+
+PUBLIC_WITH_TESTS_ELSE_PRIVATE:
+
+  void
+  listStrategies(const Interest& request);
+
+  void
+  onValidatedStrategyChoiceRequest(const shared_ptr<const Interest>& request);
+
+  void
+  setStrategy(ControlParameters& parameters,
+              ControlResponse& response);
+
+  void
+  unsetStrategy(ControlParameters& parameters,
+                ControlResponse& response);
+
+private:
+
+  StrategyChoice& m_strategyChoice;
+
+  StrategyChoicePublisher m_listPublisher;
+
+  static const Name COMMAND_PREFIX; // /localhost/nfd/strategy-choice
+
+  // number of components in an invalid, but not malformed, unsigned command.
+  // (/localhost/nfd/strategy-choice + verb + parameters) = 5
+  static const size_t COMMAND_UNSIGNED_NCOMPS;
+
+  // number of components in a valid signed Interest.
+  // (see UNSIGNED_NCOMPS), 9 with signed Interest support.
+  static const size_t COMMAND_SIGNED_NCOMPS;
+
+  static const Name LIST_DATASET_PREFIX; // /localhost/nfd/strategy-choice/list
+
+};
+
+} // namespace nfd
+
+#endif // NFD_DAEMON_MGMT_STRATEGY_CHOICE_MANAGER_HPP
diff --git a/NFD/daemon/mgmt/strategy-choice-publisher.cpp b/NFD/daemon/mgmt/strategy-choice-publisher.cpp
new file mode 100644
index 0000000..163ac3a
--- /dev/null
+++ b/NFD/daemon/mgmt/strategy-choice-publisher.cpp
@@ -0,0 +1,72 @@
+/* -*- 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/>.
+ **/
+
+#include "strategy-choice-publisher.hpp"
+#include "core/logger.hpp"
+#include "table/strategy-choice.hpp"
+
+#include <ndn-cxx/management/nfd-strategy-choice.hpp>
+
+namespace nfd {
+
+NFD_LOG_INIT("StrategyChoicePublisher");
+
+
+StrategyChoicePublisher::StrategyChoicePublisher(const StrategyChoice& strategyChoice,
+                                                 AppFace& face,
+                                                 const Name& prefix,
+                                                 ndn::KeyChain& keyChain)
+  : SegmentPublisher(face, prefix, keyChain)
+  , m_strategyChoice(strategyChoice)
+{
+
+}
+
+StrategyChoicePublisher::~StrategyChoicePublisher()
+{
+
+}
+
+size_t
+StrategyChoicePublisher::generate(ndn::EncodingBuffer& outBuffer)
+{
+  size_t totalLength = 0;
+
+  for (StrategyChoice::const_iterator i = m_strategyChoice.begin();
+       i != m_strategyChoice.end();
+       ++i)
+    {
+      ndn::nfd::StrategyChoice entry;
+
+      entry.setName(i->getPrefix())
+           .setStrategy(i->getStrategyName());
+
+      totalLength += entry.wireEncode(outBuffer);
+    }
+
+  return totalLength;
+}
+
+} // namespace nfd
diff --git a/NFD/daemon/mgmt/strategy-choice-publisher.hpp b/NFD/daemon/mgmt/strategy-choice-publisher.hpp
new file mode 100644
index 0000000..8322b63
--- /dev/null
+++ b/NFD/daemon/mgmt/strategy-choice-publisher.hpp
@@ -0,0 +1,60 @@
+/* -*- 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_DAEMON_MGMT_STRATEGY_CHOICE_PUBLISHER_HPP
+#define NFD_DAEMON_MGMT_STRATEGY_CHOICE_PUBLISHER_HPP
+
+#include "core/segment-publisher.hpp"
+#include "mgmt/app-face.hpp"
+
+namespace nfd {
+
+class StrategyChoice;
+
+class StrategyChoicePublisher : public SegmentPublisher<AppFace>
+{
+public:
+  StrategyChoicePublisher(const StrategyChoice& strategyChoice,
+                          AppFace& face,
+                          const Name& prefix,
+                          ndn::KeyChain& keyChain);
+
+  virtual
+  ~StrategyChoicePublisher();
+
+protected:
+
+  virtual size_t
+  generate(ndn::EncodingBuffer& outBuffer);
+
+private:
+
+  const StrategyChoice& m_strategyChoice;
+
+};
+
+} // namespace nfd
+
+#endif // NFD_DAEMON_MGMT_STRATEGY_CHOICE_PUBLISHER_HPP
diff --git a/NFD/daemon/mgmt/tables-config-section.cpp b/NFD/daemon/mgmt/tables-config-section.cpp
new file mode 100644
index 0000000..861ed7f
--- /dev/null
+++ b/NFD/daemon/mgmt/tables-config-section.cpp
@@ -0,0 +1,184 @@
+/* -*- 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/>.
+ */
+
+#include "tables-config-section.hpp"
+
+#include "common.hpp"
+#include "core/logger.hpp"
+#include "core/config-file.hpp"
+
+namespace nfd {
+
+NFD_LOG_INIT("TablesConfigSection");
+
+const size_t TablesConfigSection::DEFAULT_CS_MAX_PACKETS = 65536;
+
+TablesConfigSection::TablesConfigSection(Cs& cs,
+                                         Pit& pit,
+                                         Fib& fib,
+                                         StrategyChoice& strategyChoice,
+                                         Measurements& measurements)
+  : m_cs(cs)
+  // , m_pit(pit)
+  // , m_fib(fib)
+  , m_strategyChoice(strategyChoice)
+  // , m_measurements(measurements)
+  , m_areTablesConfigured(false)
+{
+
+}
+
+void
+TablesConfigSection::setConfigFile(ConfigFile& configFile)
+{
+  configFile.addSectionHandler("tables",
+                               bind(&TablesConfigSection::onConfig, this, _1, _2, _3));
+}
+
+
+void
+TablesConfigSection::ensureTablesAreConfigured()
+{
+  if (m_areTablesConfigured)
+    {
+      return;
+    }
+
+  NFD_LOG_INFO("Setting CS max packets to " << DEFAULT_CS_MAX_PACKETS);
+  m_cs.setLimit(DEFAULT_CS_MAX_PACKETS);
+
+  m_areTablesConfigured = true;
+}
+
+void
+TablesConfigSection::onConfig(const ConfigSection& configSection,
+                              bool isDryRun,
+                              const std::string& filename)
+{
+  // tables
+  // {
+  //    cs_max_packets 65536
+  //
+  //    strategy_choice
+  //    {
+  //       /               /localhost/nfd/strategy/best-route
+  //       /localhost      /localhost/nfd/strategy/broadcast
+  //       /localhost/nfd  /localhost/nfd/strategy/best-route
+  //       /ndn/broadcast  /localhost/nfd/strategy/broadcast
+  //    }
+  // }
+
+  size_t nCsMaxPackets = DEFAULT_CS_MAX_PACKETS;
+
+  boost::optional<const ConfigSection&> csMaxPacketsNode =
+    configSection.get_child_optional("cs_max_packets");
+
+  if (csMaxPacketsNode)
+    {
+      boost::optional<size_t> valCsMaxPackets =
+        configSection.get_optional<size_t>("cs_max_packets");
+
+      if (!valCsMaxPackets)
+        {
+          throw ConfigFile::Error("Invalid value for option \"cs_max_packets\""
+                                  " in \"tables\" section");
+        }
+
+      nCsMaxPackets = *valCsMaxPackets;
+    }
+
+  boost::optional<const ConfigSection&> strategyChoiceSection =
+    configSection.get_child_optional("strategy_choice");
+
+  if (strategyChoiceSection)
+    {
+      processSectionStrategyChoice(*strategyChoiceSection, isDryRun);
+    }
+
+  if (!isDryRun)
+    {
+      NFD_LOG_INFO("Setting CS max packets to " << nCsMaxPackets);
+
+      m_cs.setLimit(nCsMaxPackets);
+      m_areTablesConfigured = true;
+    }
+}
+
+void
+TablesConfigSection::processSectionStrategyChoice(const ConfigSection& configSection,
+                                                  bool isDryRun)
+{
+  // strategy_choice
+  // {
+  //   /               /localhost/nfd/strategy/best-route
+  //   /localhost      /localhost/nfd/strategy/broadcast
+  //   /localhost/nfd  /localhost/nfd/strategy/best-route
+  //   /ndn/broadcast  /localhost/nfd/strategy/broadcast
+  // }
+
+  std::map<Name, Name> choices;
+
+  for (const auto& prefixAndStrategy : configSection)
+    {
+      const Name prefix(prefixAndStrategy.first);
+      if (choices.find(prefix) != choices.end())
+        {
+          throw ConfigFile::Error("Duplicate strategy choice for prefix \"" +
+                                  prefix.toUri() + "\" in \"strategy_choice\" section");
+        }
+
+      const std::string strategyString(prefixAndStrategy.second.get_value<std::string>());
+      if (strategyString.empty())
+        {
+          throw ConfigFile::Error("Invalid strategy choice \"\" for prefix \"" +
+                                  prefix.toUri() + "\" in \"strategy_choice\" section");
+        }
+
+      const Name strategyName(strategyString);
+      if (!m_strategyChoice.hasStrategy(strategyName))
+        {
+          throw ConfigFile::Error("Invalid strategy choice \"" +
+                                  strategyName.toUri() + "\" for prefix \"" +
+                                  prefix.toUri() + "\" in \"strategy_choice\" section");
+        }
+
+      choices[prefix] = strategyName;
+    }
+
+
+  for (const auto& prefixAndStrategy : choices)
+    {
+      if (!isDryRun && !m_strategyChoice.insert(prefixAndStrategy.first, prefixAndStrategy.second))
+        {
+          throw ConfigFile::Error("Failed to set strategy \"" +
+                                  prefixAndStrategy.second.toUri() + "\" for prefix \"" +
+                                  prefixAndStrategy.first.toUri() + "\" in \"strategy_choicev\"");
+        }
+    }
+}
+
+
+
+} // namespace nfd
diff --git a/NFD/daemon/mgmt/tables-config-section.hpp b/NFD/daemon/mgmt/tables-config-section.hpp
new file mode 100644
index 0000000..3f132f7
--- /dev/null
+++ b/NFD/daemon/mgmt/tables-config-section.hpp
@@ -0,0 +1,81 @@
+/* -*- 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_MGMT_TABLES_CONFIG_SECTION_HPP
+#define NFD_MGMT_TABLES_CONFIG_SECTION_HPP
+
+#include "table/fib.hpp"
+#include "table/pit.hpp"
+#include "table/cs.hpp"
+#include "table/measurements.hpp"
+#include "table/strategy-choice.hpp"
+
+#include "core/config-file.hpp"
+
+namespace nfd {
+
+class TablesConfigSection
+{
+public:
+  TablesConfigSection(Cs& cs,
+                      Pit& pit,
+                      Fib& fib,
+                      StrategyChoice& strategyChoice,
+                      Measurements& measurements);
+
+  void
+  setConfigFile(ConfigFile& configFile);
+
+  void
+  ensureTablesAreConfigured();
+
+private:
+
+  void
+  onConfig(const ConfigSection& configSection,
+           bool isDryRun,
+           const std::string& filename);
+
+  void
+  processSectionStrategyChoice(const ConfigSection& configSection,
+                               bool isDryRun);
+
+private:
+  Cs& m_cs;
+  // Pit& m_pit;
+  // Fib& m_fib;
+  StrategyChoice& m_strategyChoice;
+  // Measurements& m_measurements;
+
+  bool m_areTablesConfigured;
+
+private:
+
+  static const size_t DEFAULT_CS_MAX_PACKETS;
+};
+
+} // namespace nfd
+
+#endif // NFD_MGMT_TABLES_CONFIG_SECTION_HPP
diff --git a/NFD/daemon/table/cs-entry.cpp b/NFD/daemon/table/cs-entry.cpp
new file mode 100644
index 0000000..0bcdefc
--- /dev/null
+++ b/NFD/daemon/table/cs-entry.cpp
@@ -0,0 +1,73 @@
+/* -*- 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/>.
+ *
+ * \author Ilya Moiseenko <http://ilyamoiseenko.com/>
+ * \author Junxiao Shi <http://www.cs.arizona.edu/people/shijunxiao/>
+ * \author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
+ */
+
+#include "cs-entry.hpp"
+#include "core/logger.hpp"
+
+namespace nfd {
+namespace cs {
+
+NFD_LOG_INIT("CsEntry");
+
+Entry::Entry()
+  : m_isUnsolicited(false)
+{
+}
+
+void
+Entry::setData(const Data& data, bool isUnsolicited)
+{
+  m_isUnsolicited = isUnsolicited;
+  m_dataPacket = data.shared_from_this();
+
+  updateStaleTime();
+}
+
+void
+Entry::updateStaleTime()
+{
+  m_staleAt = time::steady_clock::now() + m_dataPacket->getFreshnessPeriod();
+}
+
+bool
+Entry::isStale() const
+{
+  return m_staleAt < time::steady_clock::now();
+}
+
+void
+Entry::reset()
+{
+  m_staleAt = time::steady_clock::TimePoint();
+  m_dataPacket.reset();
+  m_isUnsolicited = false;
+}
+
+} // namespace cs
+} // namespace nfd
diff --git a/NFD/daemon/table/cs-entry.hpp b/NFD/daemon/table/cs-entry.hpp
new file mode 100644
index 0000000..e922ffd
--- /dev/null
+++ b/NFD/daemon/table/cs-entry.hpp
@@ -0,0 +1,143 @@
+/* -*- 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/>.
+ *
+ * \author Ilya Moiseenko <http://ilyamoiseenko.com/>
+ * \author Junxiao Shi <http://www.cs.arizona.edu/people/shijunxiao/>
+ * \author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
+ */
+
+#ifndef NFD_DAEMON_TABLE_CS_ENTRY_HPP
+#define NFD_DAEMON_TABLE_CS_ENTRY_HPP
+
+#include "common.hpp"
+
+namespace nfd {
+namespace cs {
+
+class Entry;
+
+/** \brief represents a base class for CS entry
+ */
+class Entry : noncopyable
+{
+public:
+  Entry();
+
+  /** \brief returns the name of the Data packet stored in the CS entry
+   *  \return{ NDN name }
+   */
+  const Name&
+  getName() const;
+
+  /** \brief returns the full name (including implicit digest) of the Data packet stored
+   *         in the CS entry
+   *  \return{ NDN name }
+   */
+  const Name&
+  getFullName() const;
+
+  /** \brief Data packet is unsolicited if this particular NDN node
+   *  did not receive an Interest packet for it, or the Interest packet has already expired
+   *  \return{ True if the Data packet is unsolicited; otherwise False  }
+   */
+  bool
+  isUnsolicited() const;
+
+  /** \brief returns the Data packet stored in the CS entry
+   */
+  const Data&
+  getData() const;
+
+  /** \brief changes the content of CS entry and recomputes digest
+   */
+  void
+  setData(const Data& data, bool isUnsolicited);
+
+  /** \brief returns the absolute time when Data becomes expired
+   *  \return{ Time (resolution up to time::milliseconds) }
+   */
+  const time::steady_clock::TimePoint&
+  getStaleTime() const;
+
+  /** \brief refreshes the time when Data becomes expired
+   *  according to the current absolute time.
+   */
+  void
+  updateStaleTime();
+
+  /** \brief checks if the stored Data is stale
+   */
+  bool
+  isStale() const;
+
+  /** \brief clears CS entry
+   *  After reset, *this == Entry()
+   */
+  void
+  reset();
+
+private:
+  time::steady_clock::TimePoint m_staleAt;
+  shared_ptr<const Data> m_dataPacket;
+
+  bool m_isUnsolicited;
+};
+
+inline const Name&
+Entry::getName() const
+{
+  BOOST_ASSERT(m_dataPacket != nullptr);
+  return m_dataPacket->getName();
+}
+
+inline const Name&
+Entry::getFullName() const
+{
+  BOOST_ASSERT(m_dataPacket != nullptr);
+  return m_dataPacket->getFullName();
+}
+
+inline const Data&
+Entry::getData() const
+{
+  BOOST_ASSERT(m_dataPacket != nullptr);
+  return *m_dataPacket;
+}
+
+inline bool
+Entry::isUnsolicited() const
+{
+  return m_isUnsolicited;
+}
+
+inline const time::steady_clock::TimePoint&
+Entry::getStaleTime() const
+{
+  return m_staleAt;
+}
+
+} // namespace cs
+} // namespace nfd
+
+#endif // NFD_DAEMON_TABLE_CS_ENTRY_HPP
diff --git a/NFD/daemon/table/cs-skip-list-entry.cpp b/NFD/daemon/table/cs-skip-list-entry.cpp
new file mode 100644
index 0000000..a05f0d1
--- /dev/null
+++ b/NFD/daemon/table/cs-skip-list-entry.cpp
@@ -0,0 +1,72 @@
+/* -*- 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/>.
+ *
+ * \author Ilya Moiseenko <http://ilyamoiseenko.com/>
+ * \author Junxiao Shi <http://www.cs.arizona.edu/people/shijunxiao/>
+ * \author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
+ */
+
+#include "cs-skip-list-entry.hpp"
+#include "core/logger.hpp"
+
+namespace nfd {
+namespace cs {
+namespace skip_list {
+
+NFD_LOG_INIT("CsSkipListEntry");
+
+void
+Entry::release()
+{
+  BOOST_ASSERT(m_layerIterators.empty());
+
+  reset();
+}
+
+void
+Entry::setIterator(int layer, const Entry::LayerIterators::mapped_type& layerIterator)
+{
+  m_layerIterators[layer] = layerIterator;
+}
+
+void
+Entry::removeIterator(int layer)
+{
+  m_layerIterators.erase(layer);
+}
+
+void
+Entry::printIterators() const
+{
+  for (LayerIterators::const_iterator it = m_layerIterators.begin();
+       it != m_layerIterators.end();
+       ++it)
+    {
+      NFD_LOG_TRACE("[" << it->first << "]" << " " << &(*it->second));
+    }
+}
+
+} // namespace skip_list
+} // namespace cs
+} // namespace nfd
diff --git a/NFD/daemon/table/cs-skip-list-entry.hpp b/NFD/daemon/table/cs-skip-list-entry.hpp
new file mode 100644
index 0000000..1328cc0
--- /dev/null
+++ b/NFD/daemon/table/cs-skip-list-entry.hpp
@@ -0,0 +1,89 @@
+/* -*- 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/>.
+ *
+ * \author Ilya Moiseenko <http://ilyamoiseenko.com/>
+ * \author Junxiao Shi <http://www.cs.arizona.edu/people/shijunxiao/>
+ * \author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
+ */
+
+#ifndef NFD_DAEMON_TABLE_CS_SKIP_LIST_ENTRY_HPP
+#define NFD_DAEMON_TABLE_CS_SKIP_LIST_ENTRY_HPP
+
+#include "common.hpp"
+#include "cs-entry.hpp"
+
+namespace nfd {
+namespace cs {
+namespace skip_list {
+
+/** \brief represents an entry in a CS with skip list implementation
+ */
+class Entry : public cs::Entry
+{
+public:
+  typedef std::map<int, std::list<Entry*>::iterator > LayerIterators;
+
+  Entry() = default;
+
+  /** \brief releases reference counts on shared objects
+   */
+  void
+  release();
+
+  /** \brief saves the iterator pointing to the CS entry on a specific layer of skip list
+   */
+  void
+  setIterator(int layer, const LayerIterators::mapped_type& layerIterator);
+
+  /** \brief removes the iterator pointing to the CS entry on a specific layer of skip list
+   */
+  void
+  removeIterator(int layer);
+
+  /** \brief returns the table containing <layer, iterator> pairs.
+   */
+  const LayerIterators&
+  getIterators() const;
+
+private:
+  /** \brief prints <layer, iterator> pairs.
+   */
+  void
+  printIterators() const;
+
+private:
+  LayerIterators m_layerIterators;
+};
+
+inline const Entry::LayerIterators&
+Entry::getIterators() const
+{
+  return m_layerIterators;
+}
+
+} // namespace skip_list
+} // namespace cs
+} // namespace nfd
+
+#endif // NFD_DAEMON_TABLE_CS_SKIP_LIST_ENTRY_HPP
diff --git a/NFD/daemon/table/cs.cpp b/NFD/daemon/table/cs.cpp
new file mode 100644
index 0000000..d976def
--- /dev/null
+++ b/NFD/daemon/table/cs.cpp
@@ -0,0 +1,847 @@
+/* -*- 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/>.
+ *
+ * \author Ilya Moiseenko <http://ilyamoiseenko.com/>
+ * \author Junxiao Shi <http://www.cs.arizona.edu/people/shijunxiao/>
+ * \author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
+ */
+
+#include "cs.hpp"
+#include "core/logger.hpp"
+#include "core/random.hpp"
+
+#include <ndn-cxx/util/crypto.hpp>
+#include <ndn-cxx/security/signature-sha256-with-rsa.hpp>
+
+#include <boost/random/bernoulli_distribution.hpp>
+#include <boost/concept/assert.hpp>
+#include <boost/concept_check.hpp>
+#include <type_traits>
+
+/// max skip list layers
+static const size_t SKIPLIST_MAX_LAYERS = 32;
+/// probability for an entry in layer N to appear also in layer N+1
+static const double SKIPLIST_PROBABILITY = 0.25;
+
+NFD_LOG_INIT("ContentStore");
+
+namespace nfd {
+
+// http://en.cppreference.com/w/cpp/concept/ForwardIterator
+BOOST_CONCEPT_ASSERT((boost::ForwardIterator<Cs::const_iterator>));
+// boost::ForwardIterator follows SGI standard http://www.sgi.com/tech/stl/ForwardIterator.html,
+// which doesn't require DefaultConstructible
+#ifdef HAVE_IS_DEFAULT_CONSTRUCTIBLE
+static_assert(std::is_default_constructible<Cs::const_iterator>::value,
+              "Cs::const_iterator must be default-constructible");
+#else
+BOOST_CONCEPT_ASSERT((boost::DefaultConstructible<Cs::const_iterator>));
+#endif // HAVE_IS_DEFAULT_CONSTRUCTIBLE
+
+Cs::Cs(size_t nMaxPackets)
+  : m_nMaxPackets(nMaxPackets)
+  , m_nPackets(0)
+{
+  SkipListLayer* zeroLayer = new SkipListLayer();
+  m_skipList.push_back(zeroLayer);
+
+  for (size_t i = 0; i < m_nMaxPackets; i++)
+    m_freeCsEntries.push(new cs::skip_list::Entry());
+}
+
+Cs::~Cs()
+{
+  // evict all items from CS
+  while (evictItem())
+    ;
+
+  BOOST_ASSERT(m_freeCsEntries.size() == m_nMaxPackets);
+
+  while (!m_freeCsEntries.empty())
+    {
+      delete m_freeCsEntries.front();
+      m_freeCsEntries.pop();
+    }
+}
+
+size_t
+Cs::size() const
+{
+  return m_nPackets; // size of the first layer in a skip list
+}
+
+void
+Cs::setLimit(size_t nMaxPackets)
+{
+  size_t oldNMaxPackets = m_nMaxPackets;
+  m_nMaxPackets = nMaxPackets;
+
+  while (size() > m_nMaxPackets) {
+    evictItem();
+  }
+
+  if (m_nMaxPackets >= oldNMaxPackets) {
+    for (size_t i = oldNMaxPackets; i < m_nMaxPackets; i++) {
+      m_freeCsEntries.push(new cs::skip_list::Entry());
+    }
+  }
+  else {
+    for (size_t i = oldNMaxPackets; i > m_nMaxPackets; i--) {
+      delete m_freeCsEntries.front();
+      m_freeCsEntries.pop();
+    }
+  }
+}
+
+size_t
+Cs::getLimit() const
+{
+  return m_nMaxPackets;
+}
+
+//Reference: "Skip Lists: A Probabilistic Alternative to Balanced Trees" by W.Pugh
+std::pair<cs::skip_list::Entry*, bool>
+Cs::insertToSkipList(const Data& data, bool isUnsolicited)
+{
+  NFD_LOG_TRACE("insertToSkipList() " << data.getFullName() << ", "
+                << "skipList size " << size());
+
+  BOOST_ASSERT(m_cleanupIndex.size() <= size());
+  BOOST_ASSERT(m_freeCsEntries.size() > 0);
+
+  // take entry for the memory pool
+  cs::skip_list::Entry* entry = m_freeCsEntries.front();
+  m_freeCsEntries.pop();
+  m_nPackets++;
+  entry->setData(data, isUnsolicited);
+
+  bool insertInFront = false;
+  bool isIterated = false;
+  SkipList::reverse_iterator topLayer = m_skipList.rbegin();
+  SkipListLayer::iterator updateTable[SKIPLIST_MAX_LAYERS];
+  SkipListLayer::iterator head = (*topLayer)->begin();
+
+  if (!(*topLayer)->empty())
+    {
+      //start from the upper layer towards bottom
+      int layer = m_skipList.size() - 1;
+      for (SkipList::reverse_iterator rit = topLayer; rit != m_skipList.rend(); ++rit)
+        {
+          //if we didn't do any iterations on the higher layers, start from the begin() again
+          if (!isIterated)
+            head = (*rit)->begin();
+
+          updateTable[layer] = head;
+
+          if (head != (*rit)->end())
+            {
+              // it can happen when begin() contains the element in front of which we need to insert
+              if (!isIterated && ((*head)->getFullName() >= entry->getFullName()))
+                {
+                  --updateTable[layer];
+                  insertInFront = true;
+                }
+              else
+                {
+                  SkipListLayer::iterator it = head;
+
+                  while ((*it)->getFullName() < entry->getFullName())
+                    {
+                      head = it;
+                      updateTable[layer] = it;
+                      isIterated = true;
+
+                      ++it;
+                      if (it == (*rit)->end())
+                        break;
+                    }
+                }
+            }
+
+          if (layer > 0)
+            head = (*head)->getIterators().find(layer - 1)->second; // move HEAD to the lower layer
+
+          layer--;
+        }
+    }
+  else
+    {
+      updateTable[0] = (*topLayer)->begin(); //initialization
+    }
+
+  head = updateTable[0];
+  ++head; // look at the next slot to check if it contains a duplicate
+
+  bool isCsEmpty = (size() == 0);
+  bool isInBoundaries = (head != (*m_skipList.begin())->end());
+  bool isNameIdentical = false;
+  if (!isCsEmpty && isInBoundaries)
+    {
+      isNameIdentical = (*head)->getFullName() == entry->getFullName();
+    }
+
+  //check if this is a duplicate packet
+  if (isNameIdentical)
+    {
+      NFD_LOG_TRACE("Duplicate name (with digest)");
+
+      (*head)->setData(data, isUnsolicited); //updates stale time
+
+      // new entry not needed, returning to the pool
+      entry->release();
+      m_freeCsEntries.push(entry);
+      m_nPackets--;
+
+      return std::make_pair(*head, false);
+    }
+
+  NFD_LOG_TRACE("Not a duplicate");
+
+  size_t randomLayer = pickRandomLayer();
+
+  while (m_skipList.size() < randomLayer + 1)
+    {
+      SkipListLayer* newLayer = new SkipListLayer();
+      m_skipList.push_back(newLayer);
+
+      updateTable[(m_skipList.size() - 1)] = newLayer->begin();
+    }
+
+  size_t layer = 0;
+  for (SkipList::iterator i = m_skipList.begin();
+       i != m_skipList.end() && layer <= randomLayer; ++i)
+    {
+      if (updateTable[layer] == (*i)->end() && !insertInFront)
+        {
+          (*i)->push_back(entry);
+          SkipListLayer::iterator last = (*i)->end();
+          --last;
+          entry->setIterator(layer, last);
+
+          NFD_LOG_TRACE("pushback " << &(*last));
+        }
+      else if (updateTable[layer] == (*i)->end() && insertInFront)
+        {
+          (*i)->push_front(entry);
+          entry->setIterator(layer, (*i)->begin());
+
+          NFD_LOG_TRACE("pushfront ");
+        }
+      else
+        {
+          NFD_LOG_TRACE("insertafter");
+          ++updateTable[layer]; // insert after
+          SkipListLayer::iterator position = (*i)->insert(updateTable[layer], entry);
+          entry->setIterator(layer, position); // save iterator where item was inserted
+        }
+      layer++;
+    }
+
+  return std::make_pair(entry, true);
+}
+
+bool
+Cs::insert(const Data& data, bool isUnsolicited)
+{
+  NFD_LOG_TRACE("insert() " << data.getFullName());
+
+  if (isFull())
+    {
+      evictItem();
+    }
+
+  //pointer and insertion status
+  std::pair<cs::skip_list::Entry*, bool> entry = insertToSkipList(data, isUnsolicited);
+
+  //new entry
+  if (static_cast<bool>(entry.first) && (entry.second == true))
+    {
+      m_cleanupIndex.push_back(entry.first);
+      return true;
+    }
+
+  return false;
+}
+
+size_t
+Cs::pickRandomLayer() const
+{
+  static boost::random::bernoulli_distribution<> dist(SKIPLIST_PROBABILITY);
+  // TODO rewrite using geometry_distribution
+  size_t layer;
+  for (layer = 0; layer < SKIPLIST_MAX_LAYERS; ++layer) {
+    if (!dist(getGlobalRng())) {
+      break;
+    }
+  }
+  return layer;
+}
+
+bool
+Cs::isFull() const
+{
+  if (size() >= m_nMaxPackets) //size of the first layer vs. max size
+    return true;
+
+  return false;
+}
+
+bool
+Cs::eraseFromSkipList(cs::skip_list::Entry* entry)
+{
+  NFD_LOG_TRACE("eraseFromSkipList() "  << entry->getFullName());
+  NFD_LOG_TRACE("SkipList size " << size());
+
+  bool isErased = false;
+
+  const std::map<int, std::list<cs::skip_list::Entry*>::iterator>& iterators = entry->getIterators();
+
+  if (!iterators.empty())
+    {
+      int layer = 0;
+
+      for (SkipList::iterator it = m_skipList.begin(); it != m_skipList.end(); )
+        {
+          std::map<int, std::list<cs::skip_list::Entry*>::iterator>::const_iterator i = iterators.find(layer);
+
+          if (i != iterators.end())
+            {
+              (*it)->erase(i->second);
+              entry->removeIterator(layer);
+              isErased = true;
+
+              //remove layers that do not contain any elements (starting from the second layer)
+              if ((layer != 0) && (*it)->empty())
+                {
+                  delete *it;
+                  it = m_skipList.erase(it);
+                }
+              else
+                ++it;
+
+              layer++;
+            }
+          else
+            break;
+      }
+    }
+
+  //delete entry;
+  if (isErased)
+  {
+    entry->release();
+    m_freeCsEntries.push(entry);
+    m_nPackets--;
+  }
+
+  return isErased;
+}
+
+bool
+Cs::evictItem()
+{
+  NFD_LOG_TRACE("evictItem()");
+
+  if (!m_cleanupIndex.get<unsolicited>().empty()) {
+    CleanupIndex::index<unsolicited>::type::const_iterator firstSolicited =
+      m_cleanupIndex.get<unsolicited>().upper_bound(false);
+
+    if (firstSolicited != m_cleanupIndex.get<unsolicited>().begin()) {
+      --firstSolicited;
+      BOOST_ASSERT((*firstSolicited)->isUnsolicited());
+      NFD_LOG_TRACE("Evict from unsolicited queue");
+
+      eraseFromSkipList(*firstSolicited);
+      m_cleanupIndex.get<unsolicited>().erase(firstSolicited);
+      return true;
+    }
+    else {
+      BOOST_ASSERT(!(*m_cleanupIndex.get<unsolicited>().begin())->isUnsolicited());
+    }
+  }
+
+  if (!m_cleanupIndex.get<byStaleness>().empty() &&
+      (*m_cleanupIndex.get<byStaleness>().begin())->getStaleTime() < time::steady_clock::now())
+  {
+    NFD_LOG_TRACE("Evict from staleness queue");
+
+    eraseFromSkipList(*m_cleanupIndex.get<byStaleness>().begin());
+    m_cleanupIndex.get<byStaleness>().erase(m_cleanupIndex.get<byStaleness>().begin());
+    return true;
+  }
+
+  if (!m_cleanupIndex.get<byArrival>().empty())
+  {
+    NFD_LOG_TRACE("Evict from arrival queue");
+
+    eraseFromSkipList(*m_cleanupIndex.get<byArrival>().begin());
+    m_cleanupIndex.get<byArrival>().erase(m_cleanupIndex.get<byArrival>().begin());
+    return true;
+  }
+
+  return false;
+}
+
+const Data*
+Cs::find(const Interest& interest) const
+{
+  NFD_LOG_TRACE("find() " << interest.getName());
+
+  bool isIterated = false;
+  SkipList::const_reverse_iterator topLayer = m_skipList.rbegin();
+  SkipListLayer::iterator head = (*topLayer)->begin();
+
+  if (!(*topLayer)->empty())
+    {
+      //start from the upper layer towards bottom
+      int layer = m_skipList.size() - 1;
+      for (SkipList::const_reverse_iterator rit = topLayer; rit != m_skipList.rend(); ++rit)
+        {
+          //if we didn't do any iterations on the higher layers, start from the begin() again
+          if (!isIterated)
+            head = (*rit)->begin();
+
+          if (head != (*rit)->end())
+            {
+              // it happens when begin() contains the element we want to find
+              if (!isIterated && (interest.getName().isPrefixOf((*head)->getFullName())))
+                {
+                  if (layer > 0)
+                    {
+                      layer--;
+                      continue; // try lower layer
+                    }
+                  else
+                    {
+                      isIterated = true;
+                    }
+                }
+              else
+                {
+                  SkipListLayer::iterator it = head;
+
+                  while ((*it)->getFullName() < interest.getName())
+                    {
+                      NFD_LOG_TRACE((*it)->getFullName() << " < " << interest.getName());
+                      head = it;
+                      isIterated = true;
+
+                      ++it;
+                      if (it == (*rit)->end())
+                        break;
+                    }
+                }
+            }
+
+          if (layer > 0)
+            {
+              head = (*head)->getIterators().find(layer - 1)->second; // move HEAD to the lower layer
+            }
+          else //if we reached the first layer
+            {
+              if (isIterated)
+                return selectChild(interest, head);
+            }
+
+          layer--;
+        }
+    }
+
+  return 0;
+}
+
+const Data*
+Cs::selectChild(const Interest& interest, SkipListLayer::iterator startingPoint) const
+{
+  BOOST_ASSERT(startingPoint != (*m_skipList.begin())->end());
+
+  if (startingPoint != (*m_skipList.begin())->begin())
+    {
+      BOOST_ASSERT((*startingPoint)->getFullName() < interest.getName());
+    }
+
+  NFD_LOG_TRACE("selectChild() " << interest.getChildSelector() << " "
+                << (*startingPoint)->getFullName());
+
+  bool hasLeftmostSelector = (interest.getChildSelector() <= 0);
+  bool hasRightmostSelector = !hasLeftmostSelector;
+
+  if (hasLeftmostSelector)
+    {
+      bool doesInterestContainDigest = recognizeInterestWithDigest(interest, *startingPoint);
+      bool isInPrefix = false;
+
+      if (doesInterestContainDigest)
+        {
+          isInPrefix = interest.getName().getPrefix(-1).isPrefixOf((*startingPoint)->getFullName());
+        }
+      else
+        {
+          isInPrefix = interest.getName().isPrefixOf((*startingPoint)->getFullName());
+        }
+
+      if (isInPrefix)
+        {
+          if (doesComplyWithSelectors(interest, *startingPoint, doesInterestContainDigest))
+            {
+              return &(*startingPoint)->getData();
+            }
+        }
+    }
+
+  //iterate to the right
+  SkipListLayer::iterator rightmost = startingPoint;
+  if (startingPoint != (*m_skipList.begin())->end())
+    {
+      SkipListLayer::iterator rightmostCandidate = startingPoint;
+      Name currentChildPrefix("");
+
+      while (true)
+        {
+          ++rightmostCandidate;
+
+          bool isInBoundaries = (rightmostCandidate != (*m_skipList.begin())->end());
+          bool isInPrefix = false;
+          bool doesInterestContainDigest = false;
+          if (isInBoundaries)
+            {
+              doesInterestContainDigest = recognizeInterestWithDigest(interest,
+                                                                      *rightmostCandidate);
+
+              if (doesInterestContainDigest)
+                {
+                  isInPrefix = interest.getName().getPrefix(-1)
+                                 .isPrefixOf((*rightmostCandidate)->getFullName());
+                }
+              else
+                {
+                  isInPrefix = interest.getName().isPrefixOf((*rightmostCandidate)->getFullName());
+                }
+            }
+
+          if (isInPrefix)
+            {
+              if (doesComplyWithSelectors(interest, *rightmostCandidate, doesInterestContainDigest))
+                {
+                  if (hasLeftmostSelector)
+                    {
+                      return &(*rightmostCandidate)->getData();
+                    }
+
+                  if (hasRightmostSelector)
+                    {
+                      if (doesInterestContainDigest)
+                        {
+                          // get prefix which is one component longer than Interest name
+                          // (without digest)
+                          const Name& childPrefix = (*rightmostCandidate)->getFullName()
+                                                      .getPrefix(interest.getName().size());
+                          NFD_LOG_TRACE("Child prefix" << childPrefix);
+
+                          if (currentChildPrefix.empty() || (childPrefix != currentChildPrefix))
+                            {
+                              currentChildPrefix = childPrefix;
+                              rightmost = rightmostCandidate;
+                            }
+                        }
+                      else
+                        {
+                          // get prefix which is one component longer than Interest name
+                          const Name& childPrefix = (*rightmostCandidate)->getFullName()
+                                                      .getPrefix(interest.getName().size() + 1);
+                          NFD_LOG_TRACE("Child prefix" << childPrefix);
+
+                          if (currentChildPrefix.empty() || (childPrefix != currentChildPrefix))
+                            {
+                              currentChildPrefix = childPrefix;
+                              rightmost = rightmostCandidate;
+                            }
+                        }
+                    }
+                }
+            }
+          else
+            break;
+        }
+    }
+
+  if (rightmost != startingPoint)
+    {
+      return &(*rightmost)->getData();
+    }
+
+  if (hasRightmostSelector) // if rightmost was not found, try starting point
+    {
+      bool doesInterestContainDigest = recognizeInterestWithDigest(interest, *startingPoint);
+      bool isInPrefix = false;
+
+      if (doesInterestContainDigest)
+        {
+          isInPrefix = interest.getName().getPrefix(-1).isPrefixOf((*startingPoint)->getFullName());
+        }
+      else
+        {
+          isInPrefix = interest.getName().isPrefixOf((*startingPoint)->getFullName());
+        }
+
+      if (isInPrefix)
+        {
+          if (doesComplyWithSelectors(interest, *startingPoint, doesInterestContainDigest))
+            {
+              return &(*startingPoint)->getData();
+            }
+        }
+    }
+
+  return 0;
+}
+
+bool
+Cs::doesComplyWithSelectors(const Interest& interest,
+                            cs::skip_list::Entry* entry,
+                            bool doesInterestContainDigest) const
+{
+  NFD_LOG_TRACE("doesComplyWithSelectors()");
+
+  /// \todo The following detection is not correct
+  ///       1. If Interest name ends with 32-octet component doesn't mean that this component is
+  ///          digest
+  ///       2. Only min/max selectors (both 0) can be specified, all other selectors do not
+  ///          make sense for interests with digest (though not sure if we need to enforce this)
+
+  if (doesInterestContainDigest)
+    {
+      if (interest.getName().get(-1) != entry->getFullName().get(-1))
+        {
+          NFD_LOG_TRACE("violates implicit digest");
+          return false;
+        }
+    }
+
+  if (!doesInterestContainDigest)
+    {
+      if (interest.getMinSuffixComponents() >= 0)
+        {
+          size_t minDataNameLength = interest.getName().size() + interest.getMinSuffixComponents();
+
+          bool isSatisfied = (minDataNameLength <= entry->getFullName().size());
+          if (!isSatisfied)
+            {
+              NFD_LOG_TRACE("violates minComponents");
+              return false;
+            }
+        }
+
+      if (interest.getMaxSuffixComponents() >= 0)
+        {
+          size_t maxDataNameLength = interest.getName().size() + interest.getMaxSuffixComponents();
+
+          bool isSatisfied = (maxDataNameLength >= entry->getFullName().size());
+          if (!isSatisfied)
+            {
+              NFD_LOG_TRACE("violates maxComponents");
+              return false;
+            }
+        }
+    }
+
+  if (interest.getMustBeFresh() && entry->getStaleTime() < time::steady_clock::now())
+    {
+      NFD_LOG_TRACE("violates mustBeFresh");
+      return false;
+    }
+
+  if (!interest.getPublisherPublicKeyLocator().empty())
+    {
+      if (entry->getData().getSignature().getType() == ndn::Signature::Sha256WithRsa)
+        {
+          ndn::SignatureSha256WithRsa rsaSignature(entry->getData().getSignature());
+          if (rsaSignature.getKeyLocator() != interest.getPublisherPublicKeyLocator())
+            {
+              NFD_LOG_TRACE("violates publisher key selector");
+              return false;
+            }
+        }
+      else
+        {
+          NFD_LOG_TRACE("violates publisher key selector");
+          return false;
+        }
+    }
+
+  if (doesInterestContainDigest)
+    {
+      const ndn::name::Component& lastComponent = entry->getFullName().get(-1);
+
+      if (!lastComponent.empty())
+        {
+          if (interest.getExclude().isExcluded(lastComponent))
+            {
+              NFD_LOG_TRACE("violates exclusion");
+              return false;
+            }
+        }
+    }
+  else
+    {
+      if (entry->getFullName().size() >= interest.getName().size() + 1)
+        {
+          const ndn::name::Component& nextComponent = entry->getFullName()
+                                                        .get(interest.getName().size());
+          if (!nextComponent.empty())
+            {
+              if (interest.getExclude().isExcluded(nextComponent))
+                {
+                  NFD_LOG_TRACE("violates exclusion");
+                  return false;
+                }
+            }
+        }
+    }
+
+  NFD_LOG_TRACE("complies");
+  return true;
+}
+
+bool
+Cs::recognizeInterestWithDigest(const Interest& interest, cs::skip_list::Entry* entry) const
+{
+  // only when min selector is not specified or specified with value of 0
+  // and Interest's name length is exactly the length of the name of CS entry
+  if (interest.getMinSuffixComponents() <= 0 &&
+      interest.getName().size() == (entry->getFullName().size()))
+    {
+      const ndn::name::Component& last = interest.getName().get(-1);
+      if (last.value_size() == ndn::crypto::SHA256_DIGEST_SIZE)
+        {
+          NFD_LOG_TRACE("digest recognized");
+          return true;
+        }
+    }
+
+  return false;
+}
+
+void
+Cs::erase(const Name& exactName)
+{
+  NFD_LOG_TRACE("insert() " << exactName << ", "
+                << "skipList size " << size());
+
+  bool isIterated = false;
+  SkipListLayer::iterator updateTable[SKIPLIST_MAX_LAYERS];
+  SkipList::reverse_iterator topLayer = m_skipList.rbegin();
+  SkipListLayer::iterator head = (*topLayer)->begin();
+
+  if (!(*topLayer)->empty())
+    {
+      //start from the upper layer towards bottom
+      int layer = m_skipList.size() - 1;
+      for (SkipList::reverse_iterator rit = topLayer; rit != m_skipList.rend(); ++rit)
+        {
+          //if we didn't do any iterations on the higher layers, start from the begin() again
+          if (!isIterated)
+            head = (*rit)->begin();
+
+          updateTable[layer] = head;
+
+          if (head != (*rit)->end())
+            {
+              // it can happen when begin() contains the element we want to remove
+              if (!isIterated && ((*head)->getFullName() == exactName))
+                {
+                  cs::skip_list::Entry* entryToDelete = *head;
+                  NFD_LOG_TRACE("Found target " << entryToDelete->getFullName());
+                  eraseFromSkipList(entryToDelete);
+                  // head can become invalid after eraseFromSkipList
+                  m_cleanupIndex.remove(entryToDelete);
+                  return;
+                }
+              else
+                {
+                  SkipListLayer::iterator it = head;
+
+                  while ((*it)->getFullName() < exactName)
+                    {
+                      head = it;
+                      updateTable[layer] = it;
+                      isIterated = true;
+
+                      ++it;
+                      if (it == (*rit)->end())
+                        break;
+                    }
+                }
+            }
+
+          if (layer > 0)
+            head = (*head)->getIterators().find(layer - 1)->second; // move HEAD to the lower layer
+
+          layer--;
+        }
+    }
+  else
+    {
+      return;
+    }
+
+  head = updateTable[0];
+  ++head; // look at the next slot to check if it contains the item we want to remove
+
+  bool isCsEmpty = (size() == 0);
+  bool isInBoundaries = (head != (*m_skipList.begin())->end());
+  bool isNameIdentical = false;
+  if (!isCsEmpty && isInBoundaries)
+    {
+      NFD_LOG_TRACE("Identical? " << (*head)->getFullName());
+      isNameIdentical = (*head)->getFullName() == exactName;
+    }
+
+  if (isNameIdentical)
+    {
+      cs::skip_list::Entry* entryToDelete = *head;
+      NFD_LOG_TRACE("Found target " << entryToDelete->getFullName());
+      eraseFromSkipList(entryToDelete);
+      // head can become invalid after eraseFromSkipList
+      m_cleanupIndex.remove(entryToDelete);
+    }
+}
+
+void
+Cs::printSkipList() const
+{
+  NFD_LOG_TRACE("print()");
+  //start from the upper layer towards bottom
+  int layer = m_skipList.size() - 1;
+  for (SkipList::const_reverse_iterator rit = m_skipList.rbegin(); rit != m_skipList.rend(); ++rit)
+    {
+      for (SkipListLayer::iterator it = (*rit)->begin(); it != (*rit)->end(); ++it)
+        {
+          NFD_LOG_TRACE("Layer " << layer << " " << (*it)->getFullName());
+        }
+      layer--;
+    }
+}
+
+} //namespace nfd
diff --git a/NFD/daemon/table/cs.hpp b/NFD/daemon/table/cs.hpp
new file mode 100644
index 0000000..0d989cd
--- /dev/null
+++ b/NFD/daemon/table/cs.hpp
@@ -0,0 +1,348 @@
+/* -*- 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/>.
+ *
+ * \author Ilya Moiseenko <http://ilyamoiseenko.com/>
+ * \author Junxiao Shi <http://www.cs.arizona.edu/people/shijunxiao/>
+ * \author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
+ */
+
+#ifndef NFD_DAEMON_TABLE_CS_HPP
+#define NFD_DAEMON_TABLE_CS_HPP
+
+#include "common.hpp"
+#include "cs-skip-list-entry.hpp"
+
+#include <boost/multi_index/member.hpp>
+#include <boost/multi_index_container.hpp>
+#include <boost/multi_index/ordered_index.hpp>
+#include <boost/multi_index/sequenced_index.hpp>
+#include <boost/multi_index/identity.hpp>
+
+#include <queue>
+
+namespace nfd {
+
+typedef std::list<cs::skip_list::Entry*> SkipListLayer;
+typedef std::list<SkipListLayer*> SkipList;
+
+class StalenessComparator
+{
+public:
+  bool
+  operator()(const cs::skip_list::Entry* entry1, const cs::skip_list::Entry* entry2) const
+  {
+    return entry1->getStaleTime() < entry2->getStaleTime();
+  }
+};
+
+class UnsolicitedComparator
+{
+public:
+  bool
+  operator()(const cs::skip_list::Entry* entry1, const cs::skip_list::Entry* entry2) const
+  {
+    return entry1->isUnsolicited();
+  }
+
+  bool
+  operator()(bool isUnsolicited, const cs::skip_list::Entry* entry) const
+  {
+    if (isUnsolicited)
+      return true;
+    else
+      return !entry->isUnsolicited();
+  }
+};
+
+// tags
+class unsolicited;
+class byStaleness;
+class byArrival;
+
+typedef boost::multi_index_container<
+  cs::skip_list::Entry*,
+  boost::multi_index::indexed_by<
+
+    // by arrival (FIFO)
+    boost::multi_index::sequenced<
+      boost::multi_index::tag<byArrival>
+    >,
+
+    // index by staleness time
+    boost::multi_index::ordered_non_unique<
+      boost::multi_index::tag<byStaleness>,
+      boost::multi_index::identity<cs::skip_list::Entry*>,
+      StalenessComparator
+    >,
+
+    // unsolicited Data is in the front
+    boost::multi_index::ordered_non_unique<
+      boost::multi_index::tag<unsolicited>,
+      boost::multi_index::identity<cs::skip_list::Entry*>,
+      UnsolicitedComparator
+    >
+
+  >
+> CleanupIndex;
+
+/** \brief represents Content Store
+ */
+class Cs : noncopyable
+{
+public:
+  explicit
+  Cs(size_t nMaxPackets = 10);
+
+  ~Cs();
+
+  /** \brief inserts a Data packet
+   *  This method does not consider the payload of the Data packet.
+   *
+   *  Packets are considered duplicate if the name matches.
+   *  The new Data packet with the identical name, but a different payload
+   *  is not placed in the Content Store
+   *  \return{ whether the Data is added }
+   */
+  bool
+  insert(const Data& data, bool isUnsolicited = false);
+
+  /** \brief finds the best match Data for an Interest
+   *  \return{ the best match, if any; otherwise 0 }
+   */
+  const Data*
+  find(const Interest& interest) const;
+
+  /** \brief deletes CS entry by the exact name
+   */
+  void
+  erase(const Name& exactName);
+
+  /** \brief sets maximum allowed size of Content Store (in packets)
+   */
+  void
+  setLimit(size_t nMaxPackets);
+
+  /** \brief returns maximum allowed size of Content Store (in packets)
+   *  \return{ number of packets that can be stored in Content Store }
+   */
+  size_t
+  getLimit() const;
+
+  /** \brief returns current size of Content Store measured in packets
+   *  \return{ number of packets located in Content Store }
+   */
+  size_t
+  size() const;
+
+public: // enumeration
+  class const_iterator;
+
+  /** \brief returns an iterator pointing to the first CS entry
+   *  \note Iteration order is implementation-specific and is undefined
+   *  \note The returned iterator may get invalidated if CS is modified
+   */
+  const_iterator
+  begin() const;
+
+  /** \brief returns an iterator referring to the past-the-end CS entry
+   *  \note The returned iterator may get invalidated if CS is modified
+   */
+  const_iterator
+  end() const;
+
+  class const_iterator : public std::iterator<std::forward_iterator_tag, const cs::Entry>
+  {
+  public:
+    const_iterator() = default;
+
+    const_iterator(SkipListLayer::const_iterator it);
+
+    ~const_iterator();
+
+    reference
+    operator*() const;
+
+    pointer
+    operator->() const;
+
+    const_iterator&
+    operator++();
+
+    const_iterator
+    operator++(int);
+
+    bool
+    operator==(const const_iterator& other) const;
+
+    bool
+    operator!=(const const_iterator& other) const;
+
+  private:
+    SkipListLayer::const_iterator m_skipListIterator;
+  };
+
+protected:
+  /** \brief removes one Data packet from Content Store based on replacement policy
+   *  \return{ whether the Data was removed }
+   */
+  bool
+  evictItem();
+
+private:
+  /** \brief returns True if the Content Store is at its maximum capacity
+   *  \return{ True if Content Store is full; otherwise False}
+   */
+  bool
+  isFull() const;
+
+  /** \brief Computes the layer where new Content Store Entry is placed
+   *
+   *  Reference: "Skip Lists: A Probabilistic Alternative to Balanced Trees" by W.Pugh
+   *  \return{ returns random layer (number) in a skip list}
+   */
+  size_t
+  pickRandomLayer() const;
+
+  /** \brief Inserts a new Content Store Entry in a skip list
+   *  \return{ returns a pair containing a pointer to the CS Entry,
+   *  and a flag indicating if the entry was newly created (True) or refreshed (False) }
+   */
+  std::pair<cs::skip_list::Entry*, bool>
+  insertToSkipList(const Data& data, bool isUnsolicited = false);
+
+  /** \brief Removes a specific CS Entry from all layers of a skip list
+   *  \return{ returns True if CS Entry was succesfully removed and False if CS Entry was not found}
+   */
+  bool
+  eraseFromSkipList(cs::skip_list::Entry* entry);
+
+  /** \brief Prints contents of the skip list, starting from the top layer
+   */
+  void
+  printSkipList() const;
+
+  /** \brief Implements child selector (leftmost, rightmost, undeclared).
+   *  Operates on the first layer of a skip list.
+   *
+   *  startingPoint must be less than Interest Name.
+   *  startingPoint can be equal to Interest Name only when the item is in the begin() position.
+   *
+   *  Iterates toward greater Names, terminates when CS entry falls out of Interest prefix.
+   *  When childSelector = leftmost, returns first CS entry that satisfies other selectors.
+   *  When childSelector = rightmost, it goes till the end, and returns CS entry that satisfies
+   *  other selectors. Returned CS entry is the leftmost child of the rightmost child.
+   *  \return{ the best match, if any; otherwise 0 }
+   */
+  const Data*
+  selectChild(const Interest& interest, SkipListLayer::iterator startingPoint) const;
+
+  /** \brief checks if Content Store entry satisfies Interest selectors (MinSuffixComponents,
+   *  MaxSuffixComponents, Implicit Digest, MustBeFresh)
+   *  \return{ true if satisfies all selectors; false otherwise }
+   */
+  bool
+  doesComplyWithSelectors(const Interest& interest,
+                          cs::skip_list::Entry* entry,
+                          bool doesInterestContainDigest) const;
+
+  /** \brief interprets minSuffixComponent and name lengths to understand if Interest contains
+   *  implicit digest of the data
+   *  \return{ True if Interest name contains digest; False otherwise }
+   */
+  bool
+  recognizeInterestWithDigest(const Interest& interest, cs::skip_list::Entry* entry) const;
+
+private:
+  SkipList m_skipList;
+  CleanupIndex m_cleanupIndex;
+  size_t m_nMaxPackets; // user defined maximum size of the Content Store in packets
+  size_t m_nPackets;    // current number of packets in Content Store
+  std::queue<cs::skip_list::Entry*> m_freeCsEntries; // memory pool
+};
+
+inline Cs::const_iterator
+Cs::begin() const
+{
+  return const_iterator(m_skipList.front()->begin());
+}
+
+inline Cs::const_iterator
+Cs::end() const
+{
+  return const_iterator(m_skipList.front()->end());
+}
+
+inline
+Cs::const_iterator::const_iterator(SkipListLayer::const_iterator it)
+  : m_skipListIterator(it)
+{
+}
+
+inline
+Cs::const_iterator::~const_iterator()
+{
+}
+
+inline Cs::const_iterator&
+Cs::const_iterator::operator++()
+{
+  ++m_skipListIterator;
+  return *this;
+}
+
+inline Cs::const_iterator
+Cs::const_iterator::operator++(int)
+{
+  Cs::const_iterator temp(*this);
+  ++(*this);
+  return temp;
+}
+
+inline Cs::const_iterator::reference
+Cs::const_iterator::operator*() const
+{
+  return *(this->operator->());
+}
+
+inline Cs::const_iterator::pointer
+Cs::const_iterator::operator->() const
+{
+  return *m_skipListIterator;
+}
+
+inline bool
+Cs::const_iterator::operator==(const Cs::const_iterator& other) const
+{
+  return m_skipListIterator == other.m_skipListIterator;
+}
+
+inline bool
+Cs::const_iterator::operator!=(const Cs::const_iterator& other) const
+{
+  return !(*this == other);
+}
+
+} // namespace nfd
+
+#endif // NFD_DAEMON_TABLE_CS_HPP
diff --git a/NFD/daemon/table/dead-nonce-list.cpp b/NFD/daemon/table/dead-nonce-list.cpp
new file mode 100644
index 0000000..be6afd8
--- /dev/null
+++ b/NFD/daemon/table/dead-nonce-list.cpp
@@ -0,0 +1,171 @@
+/* -*- 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/>.
+ */
+
+#include "dead-nonce-list.hpp"
+#include "core/city-hash.hpp"
+#include "core/logger.hpp"
+
+NFD_LOG_INIT("DeadNonceList");
+
+namespace nfd {
+
+const time::nanoseconds DeadNonceList::DEFAULT_LIFETIME = time::seconds(6);
+const time::nanoseconds DeadNonceList::MIN_LIFETIME = time::milliseconds(1);
+const size_t DeadNonceList::INITIAL_CAPACITY = (1 << 7);
+const size_t DeadNonceList::MIN_CAPACITY = (1 << 3);
+const size_t DeadNonceList::MAX_CAPACITY = (1 << 24);
+const DeadNonceList::Entry DeadNonceList::MARK = 0;
+const size_t DeadNonceList::EXPECTED_MARK_COUNT = 5;
+const double DeadNonceList::CAPACITY_UP = 1.2;
+const double DeadNonceList::CAPACITY_DOWN = 0.9;
+const size_t DeadNonceList::EVICT_LIMIT = (1 << 6);
+
+DeadNonceList::DeadNonceList(const time::nanoseconds& lifetime)
+  : m_lifetime(lifetime)
+  , m_queue(m_index.get<0>())
+  , m_ht(m_index.get<1>())
+  , m_capacity(INITIAL_CAPACITY)
+  , m_markInterval(m_lifetime / EXPECTED_MARK_COUNT)
+  , m_adjustCapacityInterval(m_lifetime)
+{
+  if (m_lifetime < MIN_LIFETIME) {
+    throw std::invalid_argument("lifetime is less than MIN_LIFETIME");
+  }
+
+  for (size_t i = 0; i < EXPECTED_MARK_COUNT; ++i) {
+    m_queue.push_back(MARK);
+  }
+
+  m_markEvent = scheduler::schedule(m_markInterval, bind(&DeadNonceList::mark, this));
+  m_adjustCapacityEvent = scheduler::schedule(m_adjustCapacityInterval,
+                                              bind(&DeadNonceList::adjustCapacity, this));
+}
+
+DeadNonceList::~DeadNonceList()
+{
+  scheduler::cancel(m_markEvent);
+  scheduler::cancel(m_adjustCapacityEvent);
+
+  BOOST_ASSERT_MSG(DEFAULT_LIFETIME >= MIN_LIFETIME, "DEFAULT_LIFETIME is too small");
+  static_assert(INITIAL_CAPACITY >= MIN_CAPACITY, "INITIAL_CAPACITY is too small");
+  static_assert(INITIAL_CAPACITY <= MAX_CAPACITY, "INITIAL_CAPACITY is too large");
+  BOOST_ASSERT_MSG(static_cast<size_t>(MIN_CAPACITY * CAPACITY_UP) > MIN_CAPACITY,
+                   "CAPACITY_UP must be able to increase from MIN_CAPACITY");
+  BOOST_ASSERT_MSG(static_cast<size_t>(MAX_CAPACITY * CAPACITY_DOWN) < MAX_CAPACITY,
+                   "CAPACITY_DOWN must be able to decrease from MAX_CAPACITY");
+  BOOST_ASSERT_MSG(CAPACITY_UP > 1.0, "CAPACITY_UP must adjust up");
+  BOOST_ASSERT_MSG(CAPACITY_DOWN < 1.0, "CAPACITY_DOWN must adjust down");
+  static_assert(EVICT_LIMIT >= 1, "EVICT_LIMIT must be at least 1");
+}
+
+size_t
+DeadNonceList::size() const
+{
+  return m_queue.size() - this->countMarks();
+}
+
+bool
+DeadNonceList::has(const Name& name, uint32_t nonce) const
+{
+  Entry entry = DeadNonceList::makeEntry(name, nonce);
+  return m_ht.find(entry) != m_ht.end();
+}
+
+void
+DeadNonceList::add(const Name& name, uint32_t nonce)
+{
+  Entry entry = DeadNonceList::makeEntry(name, nonce);
+  m_queue.push_back(entry);
+
+  this->evictEntries();
+}
+
+DeadNonceList::Entry
+DeadNonceList::makeEntry(const Name& name, uint32_t nonce)
+{
+  Block nameWire = name.wireEncode();
+  return CityHash64WithSeed(reinterpret_cast<const char*>(nameWire.wire()), nameWire.size(),
+                            static_cast<uint64_t>(nonce));
+}
+
+size_t
+DeadNonceList::countMarks() const
+{
+  return m_ht.count(MARK);
+}
+
+void
+DeadNonceList::mark()
+{
+  m_queue.push_back(MARK);
+  size_t nMarks = this->countMarks();
+  m_actualMarkCounts.insert(nMarks);
+
+  NFD_LOG_DEBUG("mark nMarks=" << nMarks);
+
+  scheduler::schedule(m_markInterval, bind(&DeadNonceList::mark, this));
+}
+
+void
+DeadNonceList::adjustCapacity()
+{
+  std::pair<std::multiset<size_t>::iterator, std::multiset<size_t>::iterator> equalRange =
+    m_actualMarkCounts.equal_range(EXPECTED_MARK_COUNT);
+
+  if (equalRange.second == m_actualMarkCounts.begin()) {
+    // all counts are above expected count, adjust down
+    m_capacity = std::max(MIN_CAPACITY,
+                          static_cast<size_t>(m_capacity * CAPACITY_DOWN));
+    NFD_LOG_DEBUG("adjustCapacity DOWN capacity=" << m_capacity);
+  }
+  else if (equalRange.first == m_actualMarkCounts.end()) {
+    // all counts are below expected count, adjust up
+    m_capacity = std::min(MAX_CAPACITY,
+                          static_cast<size_t>(m_capacity * CAPACITY_UP));
+    NFD_LOG_DEBUG("adjustCapacity UP capacity=" << m_capacity);
+  }
+
+  m_actualMarkCounts.clear();
+
+  this->evictEntries();
+
+  m_adjustCapacityEvent = scheduler::schedule(m_adjustCapacityInterval,
+                                              bind(&DeadNonceList::adjustCapacity, this));
+}
+
+void
+DeadNonceList::evictEntries()
+{
+  ssize_t nOverCapacity = m_queue.size() - m_capacity;
+  if (nOverCapacity <= 0) // not over capacity
+    return;
+
+  for (ssize_t nEvict = std::min<ssize_t>(nOverCapacity, EVICT_LIMIT); nEvict > 0; --nEvict) {
+    m_queue.erase(m_queue.begin());
+  }
+  BOOST_ASSERT(m_queue.size() >= m_capacity);
+}
+
+} // namespace nfd
diff --git a/NFD/daemon/table/dead-nonce-list.hpp b/NFD/daemon/table/dead-nonce-list.hpp
new file mode 100644
index 0000000..1c7f7cc
--- /dev/null
+++ b/NFD/daemon/table/dead-nonce-list.hpp
@@ -0,0 +1,222 @@
+/* -*- 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_DAEMON_TABLE_DEAD_NONCE_LIST_HPP
+#define NFD_DAEMON_TABLE_DEAD_NONCE_LIST_HPP
+
+#include "common.hpp"
+#include <boost/multi_index_container.hpp>
+#include <boost/multi_index/sequenced_index.hpp>
+#include <boost/multi_index/hashed_index.hpp>
+#include "core/scheduler.hpp"
+
+namespace nfd {
+
+/** \brief represents the Dead Nonce list
+ *
+ *  The Dead Nonce List is a global table that supplements PIT for loop detection.
+ *  When a Nonce is erased (dead) from PIT entry, the Nonce and the Interest Name is added to
+ *  Dead Nonce List, and kept for a duration in which most loops are expected to have occured.
+ *
+ *  To reduce memory usage, the Interest Name and Nonce are stored as a 64-bit hash.
+ *  There could be false positives (non-looping Interest could be considered looping),
+ *  but the probability is small, and the error is recoverable when consumer retransmits
+ *  with a different Nonce.
+ *
+ *  To reduce memory usage, entries do not have associated timestamps. Instead,
+ *  lifetime of entries is controlled by dynamically adjusting the capacity of the container.
+ *  At fixed intervals, the MARK, an entry with a special value, is inserted into the container.
+ *  The number of MARKs stored in the container reflects the lifetime of entries,
+ *  because MARKs are inserted at fixed intervals.
+ */
+class DeadNonceList : noncopyable
+{
+public:
+  /** \brief constructs the Dead Nonce List
+   *  \param lifetime duration of the expected lifetime of each nonce,
+   *         must be no less than MIN_LIFETIME.
+   *         This should be set to the duration in which most loops would have occured.
+   *         A loop cannot be detected if delay of the cycle is greater than lifetime.
+   *  \throw std::invalid_argument if lifetime is less than MIN_LIFETIME
+   */
+  explicit
+  DeadNonceList(const time::nanoseconds& lifetime = DEFAULT_LIFETIME);
+
+  ~DeadNonceList();
+
+  /** \brief determines if name+nonce exists
+   *  \return true if name+nonce exists
+   */
+  bool
+  has(const Name& name, uint32_t nonce) const;
+
+  /** \brief records name+nonce
+   */
+  void
+  add(const Name& name, uint32_t nonce);
+
+  /** \return number of stored Nonces
+   *  \note The return value does not contain non-Nonce entries in the index, if any.
+   */
+  size_t
+  size() const;
+
+  /** \return expected lifetime
+   */
+  const time::nanoseconds&
+  getLifetime() const;
+
+private: // Entry and Index
+  typedef uint64_t Entry;
+
+  static Entry
+  makeEntry(const Name& name, uint32_t nonce);
+
+  typedef boost::multi_index_container<
+    Entry,
+    boost::multi_index::indexed_by<
+      boost::multi_index::sequenced<>,
+      boost::multi_index::hashed_non_unique<
+        boost::multi_index::identity<Entry>
+      >
+    >
+  > Index;
+
+  typedef Index::nth_index<0>::type Queue;
+  typedef Index::nth_index<1>::type Hashtable;
+
+private: // actual lifetime estimation and capacity control
+  /** \return number of MARKs in the index
+   */
+  size_t
+  countMarks() const;
+
+  /** \brief add a MARK, then record number of MARKs in m_actualMarkCounts
+   */
+  void
+  mark();
+
+  /** \brief adjust capacity according to m_actualMarkCounts
+   *
+   *  If all counts are above EXPECTED_MARK_COUNT, reduce capacity to m_capacity * CAPACITY_DOWN.
+   *  If all counts are below EXPECTED_MARK_COUNT, increase capacity to m_capacity * CAPACITY_UP.
+   */
+  void
+  adjustCapacity();
+
+  /** \brief evict some entries if index is over capacity
+   */
+  void
+  evictEntries();
+
+public:
+  /// default entry lifetime
+  static const time::nanoseconds DEFAULT_LIFETIME;
+
+  /// minimum entry lifetime
+  static const time::nanoseconds MIN_LIFETIME;
+
+private:
+  time::nanoseconds m_lifetime;
+  Index m_index;
+  Queue& m_queue;
+  Hashtable& m_ht;
+
+PUBLIC_WITH_TESTS_ELSE_PRIVATE: // actual lifetime estimation and capacity control
+
+  // ---- current capacity and hard limits
+
+  /** \brief current capacity of index
+   *
+   *  The index size is maintained to be near this capacity.
+   *
+   *  The capacity is adjusted so that every Entry is expected to be kept for m_lifetime.
+   *  This is achieved by mark() and adjustCapacity().
+   */
+  size_t m_capacity;
+
+  static const size_t INITIAL_CAPACITY;
+
+  /** \brief minimum capacity
+   *
+   *  This is to ensure correct algorithm operations.
+   */
+  static const size_t MIN_CAPACITY;
+
+  /** \brief maximum capacity
+   *
+   *  This is to limit memory usage.
+   */
+  static const size_t MAX_CAPACITY;
+
+  // ---- actual entry lifetime estimation
+
+  /** \brief the MARK for capacity
+   *
+   *  The MARK doesn't have a distinct type.
+   *  Entry is a hash. The hash function should have non-invertible property,
+   *  so it's unlikely for a usual Entry to have collision with the MARK.
+   */
+  static const Entry MARK;
+
+  /** \brief expected number of MARKs in the index
+   */
+  static const size_t EXPECTED_MARK_COUNT;
+
+  /** \brief number of MARKs in the index after each MARK insertion
+   *
+   *  adjustCapacity uses this to determine whether and how to adjust capcity,
+   *  and then clears this list.
+   */
+  std::multiset<size_t> m_actualMarkCounts;
+
+  time::nanoseconds m_markInterval;
+
+  scheduler::EventId m_markEvent;
+
+  // ---- capacity adjustments
+
+  static const double CAPACITY_UP;
+
+  static const double CAPACITY_DOWN;
+
+  time::nanoseconds m_adjustCapacityInterval;
+
+  scheduler::EventId m_adjustCapacityEvent;
+
+  /** \brief maximum number of entries to evict at each operation if index is over capacity
+   */
+  static const size_t EVICT_LIMIT;
+};
+
+inline const time::nanoseconds&
+DeadNonceList::getLifetime() const
+{
+  return m_lifetime;
+}
+
+} // namespace nfd
+
+#endif // NFD_DAEMON_TABLE_DEAD_NONCE_LIST_HPP
diff --git a/NFD/daemon/table/fib-entry.cpp b/NFD/daemon/table/fib-entry.cpp
new file mode 100644
index 0000000..b2ea76c
--- /dev/null
+++ b/NFD/daemon/table/fib-entry.cpp
@@ -0,0 +1,85 @@
+/* -*- 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/>.
+ */
+
+#include "fib-entry.hpp"
+
+namespace nfd {
+namespace fib {
+
+Entry::Entry(const Name& prefix)
+  : m_prefix(prefix)
+{
+}
+
+NextHopList::iterator
+Entry::findNextHop(Face& face)
+{
+  return std::find_if(m_nextHops.begin(), m_nextHops.end(),
+                      [&face] (const NextHop& nexthop) {
+                        return nexthop.getFace().get() == &face;
+                      });
+}
+
+bool
+Entry::hasNextHop(shared_ptr<Face> face) const
+{
+  return const_cast<Entry*>(this)->findNextHop(*face) != m_nextHops.end();
+}
+
+void
+Entry::addNextHop(shared_ptr<Face> face, uint64_t cost)
+{
+  auto it = this->findNextHop(*face);
+  if (it == m_nextHops.end()) {
+    m_nextHops.push_back(fib::NextHop(face));
+    it = m_nextHops.end();
+    --it;
+  }
+  // now it refers to the NextHop for face
+
+  it->setCost(cost);
+
+  this->sortNextHops();
+}
+
+void
+Entry::removeNextHop(shared_ptr<Face> face)
+{
+  auto it = this->findNextHop(*face);
+  if (it != m_nextHops.end()) {
+    m_nextHops.erase(it);
+  }
+}
+
+void
+Entry::sortNextHops()
+{
+  std::sort(m_nextHops.begin(), m_nextHops.end(),
+            [] (const NextHop& a, const NextHop& b) { return a.getCost() < b.getCost(); });
+}
+
+
+} // namespace fib
+} // namespace nfd
diff --git a/NFD/daemon/table/fib-entry.hpp b/NFD/daemon/table/fib-entry.hpp
new file mode 100644
index 0000000..4a6116e
--- /dev/null
+++ b/NFD/daemon/table/fib-entry.hpp
@@ -0,0 +1,135 @@
+/* -*- 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_DAEMON_TABLE_FIB_ENTRY_HPP
+#define NFD_DAEMON_TABLE_FIB_ENTRY_HPP
+
+#include "fib-nexthop.hpp"
+
+namespace nfd {
+
+class NameTree;
+namespace name_tree {
+class Entry;
+}
+
+namespace fib {
+
+/** \class NextHopList
+ *  \brief represents a collection of nexthops
+ *
+ *  This type has these methods as public API:
+ *    iterator<NextHop> begin()
+ *    iterator<NextHop> end()
+ *    size_t size()
+ */
+typedef std::vector<fib::NextHop> NextHopList;
+
+/** \class Entry
+ *  \brief represents a FIB entry
+ */
+class Entry : noncopyable
+{
+public:
+  explicit
+  Entry(const Name& prefix);
+
+  const Name&
+  getPrefix() const;
+
+  const NextHopList&
+  getNextHops() const;
+
+  /** \return whether this Entry has any NextHop record
+   */
+  bool
+  hasNextHops() const;
+
+  /** \return whether there is a NextHop record for face
+   *
+   *  \todo change parameter type to Face&
+   */
+  bool
+  hasNextHop(shared_ptr<Face> face) const;
+
+  /** \brief adds a NextHop record
+   *
+   *  If a NextHop record for face already exists, its cost is updated.
+   *  \note shared_ptr is passed by value because this function will take shared ownership
+   */
+  void
+  addNextHop(shared_ptr<Face> face, uint64_t cost);
+
+  /** \brief removes a NextHop record
+   *
+   *  If no NextHop record for face exists, do nothing.
+   *
+   *  \todo change parameter type to Face&
+   */
+  void
+  removeNextHop(shared_ptr<Face> face);
+
+private:
+  /** @note This method is non-const because normal iterator is needed by callers.
+   */
+  NextHopList::iterator
+  findNextHop(Face& face);
+
+  /// sorts the nexthop list
+  void
+  sortNextHops();
+
+private:
+  Name m_prefix;
+  NextHopList m_nextHops;
+
+  shared_ptr<name_tree::Entry> m_nameTreeEntry;
+  friend class nfd::NameTree;
+  friend class nfd::name_tree::Entry;
+};
+
+
+inline const Name&
+Entry::getPrefix() const
+{
+  return m_prefix;
+}
+
+inline const NextHopList&
+Entry::getNextHops() const
+{
+  return m_nextHops;
+}
+
+inline bool
+Entry::hasNextHops() const
+{
+  return !m_nextHops.empty();
+}
+
+} // namespace fib
+} // namespace nfd
+
+#endif // NFD_DAEMON_TABLE_FIB_ENTRY_HPP
diff --git a/NFD/daemon/table/fib-nexthop.cpp b/NFD/daemon/table/fib-nexthop.cpp
new file mode 100644
index 0000000..235fc79
--- /dev/null
+++ b/NFD/daemon/table/fib-nexthop.cpp
@@ -0,0 +1,56 @@
+/* -*- 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/>.
+ */
+
+#include "fib-nexthop.hpp"
+
+namespace nfd {
+namespace fib {
+
+NextHop::NextHop(shared_ptr<Face> face)
+  : m_face(face)
+  , m_cost(0)
+{
+}
+
+const shared_ptr<Face>&
+NextHop::getFace() const
+{
+  return m_face;
+}
+
+void
+NextHop::setCost(uint64_t cost)
+{
+  m_cost = cost;
+}
+
+uint64_t
+NextHop::getCost() const
+{
+  return m_cost;
+}
+
+} // namespace fib
+} // namespace nfd
diff --git a/NFD/daemon/table/fib-nexthop.hpp b/NFD/daemon/table/fib-nexthop.hpp
new file mode 100644
index 0000000..9f7ed93
--- /dev/null
+++ b/NFD/daemon/table/fib-nexthop.hpp
@@ -0,0 +1,61 @@
+/* -*- 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_DAEMON_TABLE_FIB_NEXTHOP_HPP
+#define NFD_DAEMON_TABLE_FIB_NEXTHOP_HPP
+
+#include "common.hpp"
+#include "face/face.hpp"
+
+namespace nfd {
+namespace fib {
+
+/** \class NextHop
+ *  \brief represents a nexthop record in FIB entry
+ */
+class NextHop
+{
+public:
+  explicit
+  NextHop(shared_ptr<Face> face);
+
+  const shared_ptr<Face>&
+  getFace() const;
+
+  void
+  setCost(uint64_t cost);
+
+  uint64_t
+  getCost() const;
+
+private:
+  shared_ptr<Face> m_face;
+  uint64_t m_cost;
+};
+
+} // namespace fib
+} // namespace nfd
+
+#endif // NFD_DAEMON_TABLE_FIB_NEXTHOP_HPP
diff --git a/NFD/daemon/table/fib.cpp b/NFD/daemon/table/fib.cpp
new file mode 100644
index 0000000..ee9e733
--- /dev/null
+++ b/NFD/daemon/table/fib.cpp
@@ -0,0 +1,185 @@
+/* -*- 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/>.
+ */
+
+#include "fib.hpp"
+#include "pit-entry.hpp"
+#include "measurements-entry.hpp"
+
+#include <boost/concept/assert.hpp>
+#include <boost/concept_check.hpp>
+#include <type_traits>
+
+namespace nfd {
+
+const shared_ptr<fib::Entry> Fib::s_emptyEntry = make_shared<fib::Entry>(Name());
+
+// http://en.cppreference.com/w/cpp/concept/ForwardIterator
+BOOST_CONCEPT_ASSERT((boost::ForwardIterator<Fib::const_iterator>));
+// boost::ForwardIterator follows SGI standard http://www.sgi.com/tech/stl/ForwardIterator.html,
+// which doesn't require DefaultConstructible
+#ifdef HAVE_IS_DEFAULT_CONSTRUCTIBLE
+static_assert(std::is_default_constructible<Fib::const_iterator>::value,
+              "Fib::const_iterator must be default-constructible");
+#else
+BOOST_CONCEPT_ASSERT((boost::DefaultConstructible<Fib::const_iterator>));
+#endif // HAVE_IS_DEFAULT_CONSTRUCTIBLE
+
+Fib::Fib(NameTree& nameTree)
+  : m_nameTree(nameTree)
+  , m_nItems(0)
+{
+}
+
+Fib::~Fib()
+{
+}
+
+static inline bool
+predicate_NameTreeEntry_hasFibEntry(const name_tree::Entry& entry)
+{
+  return static_cast<bool>(entry.getFibEntry());
+}
+
+shared_ptr<fib::Entry>
+Fib::findLongestPrefixMatch(const Name& prefix) const
+{
+  shared_ptr<name_tree::Entry> nameTreeEntry =
+    m_nameTree.findLongestPrefixMatch(prefix, &predicate_NameTreeEntry_hasFibEntry);
+  if (static_cast<bool>(nameTreeEntry)) {
+    return nameTreeEntry->getFibEntry();
+  }
+  return s_emptyEntry;
+}
+
+shared_ptr<fib::Entry>
+Fib::findLongestPrefixMatch(shared_ptr<name_tree::Entry> nameTreeEntry) const
+{
+  shared_ptr<fib::Entry> entry = nameTreeEntry->getFibEntry();
+  if (static_cast<bool>(entry))
+    return entry;
+  nameTreeEntry = m_nameTree.findLongestPrefixMatch(nameTreeEntry,
+                                                    &predicate_NameTreeEntry_hasFibEntry);
+  if (static_cast<bool>(nameTreeEntry)) {
+    return nameTreeEntry->getFibEntry();
+  }
+  return s_emptyEntry;
+}
+
+shared_ptr<fib::Entry>
+Fib::findLongestPrefixMatch(const pit::Entry& pitEntry) const
+{
+  shared_ptr<name_tree::Entry> nameTreeEntry = m_nameTree.get(pitEntry);
+
+  BOOST_ASSERT(static_cast<bool>(nameTreeEntry));
+
+  return findLongestPrefixMatch(nameTreeEntry);
+}
+
+shared_ptr<fib::Entry>
+Fib::findLongestPrefixMatch(const measurements::Entry& measurementsEntry) const
+{
+  shared_ptr<name_tree::Entry> nameTreeEntry = m_nameTree.get(measurementsEntry);
+
+  BOOST_ASSERT(static_cast<bool>(nameTreeEntry));
+
+  return findLongestPrefixMatch(nameTreeEntry);
+}
+
+shared_ptr<fib::Entry>
+Fib::findExactMatch(const Name& prefix) const
+{
+  shared_ptr<name_tree::Entry> nameTreeEntry = m_nameTree.findExactMatch(prefix);
+  if (static_cast<bool>(nameTreeEntry))
+    return nameTreeEntry->getFibEntry();
+  return shared_ptr<fib::Entry>();
+}
+
+std::pair<shared_ptr<fib::Entry>, bool>
+Fib::insert(const Name& prefix)
+{
+  shared_ptr<name_tree::Entry> nameTreeEntry = m_nameTree.lookup(prefix);
+  shared_ptr<fib::Entry> entry = nameTreeEntry->getFibEntry();
+  if (static_cast<bool>(entry))
+    return std::make_pair(entry, false);
+  entry = make_shared<fib::Entry>(prefix);
+  nameTreeEntry->setFibEntry(entry);
+  ++m_nItems;
+  return std::make_pair(entry, true);
+}
+
+void
+Fib::erase(shared_ptr<name_tree::Entry> nameTreeEntry)
+{
+  nameTreeEntry->setFibEntry(shared_ptr<fib::Entry>());
+  m_nameTree.eraseEntryIfEmpty(nameTreeEntry);
+  --m_nItems;
+}
+
+void
+Fib::erase(const Name& prefix)
+{
+  shared_ptr<name_tree::Entry> nameTreeEntry = m_nameTree.findExactMatch(prefix);
+  if (static_cast<bool>(nameTreeEntry)) {
+    this->erase(nameTreeEntry);
+  }
+}
+
+void
+Fib::erase(const fib::Entry& entry)
+{
+  shared_ptr<name_tree::Entry> nameTreeEntry = m_nameTree.get(entry);
+  if (static_cast<bool>(nameTreeEntry)) {
+    this->erase(nameTreeEntry);
+  }
+}
+
+void
+Fib::removeNextHopFromAllEntries(shared_ptr<Face> face)
+{
+  std::list<fib::Entry*> toErase;
+
+  auto&& enumerable = m_nameTree.fullEnumerate(&predicate_NameTreeEntry_hasFibEntry);
+  for (const name_tree::Entry& nte : enumerable) {
+    shared_ptr<fib::Entry> entry = nte.getFibEntry();
+    entry->removeNextHop(face);
+    if (!entry->hasNextHops()) {
+      toErase.push_back(entry.get());
+      // entry needs to be erased, but we must wait until the enumeration ends,
+      // because otherwise NameTree iterator would be invalidated
+    }
+  }
+
+  for (fib::Entry* entry : toErase) {
+    this->erase(*entry);
+  }
+}
+
+Fib::const_iterator
+Fib::begin() const
+{
+  return const_iterator(m_nameTree.fullEnumerate(&predicate_NameTreeEntry_hasFibEntry).begin());
+}
+
+} // namespace nfd
diff --git a/NFD/daemon/table/fib.hpp b/NFD/daemon/table/fib.hpp
new file mode 100644
index 0000000..34a2009
--- /dev/null
+++ b/NFD/daemon/table/fib.hpp
@@ -0,0 +1,230 @@
+/* -*- 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_DAEMON_TABLE_FIB_HPP
+#define NFD_DAEMON_TABLE_FIB_HPP
+
+#include "fib-entry.hpp"
+#include "name-tree.hpp"
+
+namespace nfd {
+
+namespace measurements {
+class Entry;
+}
+namespace pit {
+class Entry;
+}
+
+/** \class Fib
+ *  \brief represents the FIB
+ */
+class Fib : noncopyable
+{
+public:
+  explicit
+  Fib(NameTree& nameTree);
+
+  ~Fib();
+
+  size_t
+  size() const;
+
+public: // lookup
+  /// performs a longest prefix match
+  shared_ptr<fib::Entry>
+  findLongestPrefixMatch(const Name& prefix) const;
+
+  /// performs a longest prefix match
+  shared_ptr<fib::Entry>
+  findLongestPrefixMatch(const pit::Entry& pitEntry) const;
+
+  /// performs a longest prefix match
+  shared_ptr<fib::Entry>
+  findLongestPrefixMatch(const measurements::Entry& measurementsEntry) const;
+
+  shared_ptr<fib::Entry>
+  findExactMatch(const Name& prefix) const;
+
+public: // mutation
+  /** \brief inserts a FIB entry for prefix
+   *  If an entry for exact same prefix exists, that entry is returned.
+   *  \return{ the entry, and true for new entry, false for existing entry }
+   */
+  std::pair<shared_ptr<fib::Entry>, bool>
+  insert(const Name& prefix);
+
+  void
+  erase(const Name& prefix);
+
+  void
+  erase(const fib::Entry& entry);
+
+  /** \brief removes the NextHop record for face in all entrites
+   *
+   *  This is usually invoked when face goes away.
+   *  Removing the last NextHop in a FIB entry will erase the FIB entry.
+   *
+   *  \todo change parameter type to Face&
+   */
+  void
+  removeNextHopFromAllEntries(shared_ptr<Face> face);
+
+public: // enumeration
+  class const_iterator;
+
+  /** \brief returns an iterator pointing to the first FIB entry
+   *  \note Iteration order is implementation-specific and is undefined
+   *  \note The returned iterator may get invalidated if FIB or another NameTree-based
+   *        table is modified
+   */
+  const_iterator
+  begin() const;
+
+  /** \brief returns an iterator referring to the past-the-end FIB entry
+   *  \note The returned iterator may get invalidated if FIB or another NameTree-based
+   *        table is modified
+   */
+  const_iterator
+  end() const;
+
+  class const_iterator : public std::iterator<std::forward_iterator_tag, const fib::Entry>
+  {
+  public:
+    const_iterator() = default;
+
+    explicit
+    const_iterator(const NameTree::const_iterator& it);
+
+    ~const_iterator();
+
+    const fib::Entry&
+    operator*() const;
+
+    shared_ptr<fib::Entry>
+    operator->() const;
+
+    const_iterator&
+    operator++();
+
+    const_iterator
+    operator++(int);
+
+    bool
+    operator==(const const_iterator& other) const;
+
+    bool
+    operator!=(const const_iterator& other) const;
+
+  private:
+    NameTree::const_iterator m_nameTreeIterator;
+  };
+
+private:
+  shared_ptr<fib::Entry>
+  findLongestPrefixMatch(shared_ptr<name_tree::Entry> nameTreeEntry) const;
+
+  void
+  erase(shared_ptr<name_tree::Entry> nameTreeEntry);
+
+private:
+  NameTree& m_nameTree;
+  size_t m_nItems;
+
+  /** \brief The empty FIB entry.
+   *
+   *  This entry has no nexthops.
+   *  It is returned by findLongestPrefixMatch if nothing is matched.
+   *  Returning empty entry instead of nullptr makes forwarding and strategy implementation easier.
+   */
+  static const shared_ptr<fib::Entry> s_emptyEntry;
+};
+
+inline size_t
+Fib::size() const
+{
+  return m_nItems;
+}
+
+inline Fib::const_iterator
+Fib::end() const
+{
+  return const_iterator(m_nameTree.end());
+}
+
+inline
+Fib::const_iterator::const_iterator(const NameTree::const_iterator& it)
+  : m_nameTreeIterator(it)
+{
+}
+
+inline
+Fib::const_iterator::~const_iterator()
+{
+}
+
+inline
+Fib::const_iterator
+Fib::const_iterator::operator++(int)
+{
+  Fib::const_iterator temp(*this);
+  ++(*this);
+  return temp;
+}
+
+inline Fib::const_iterator&
+Fib::const_iterator::operator++()
+{
+  ++m_nameTreeIterator;
+  return *this;
+}
+
+inline const fib::Entry&
+Fib::const_iterator::operator*() const
+{
+  return *this->operator->();
+}
+
+inline shared_ptr<fib::Entry>
+Fib::const_iterator::operator->() const
+{
+  return m_nameTreeIterator->getFibEntry();
+}
+
+inline bool
+Fib::const_iterator::operator==(const Fib::const_iterator& other) const
+{
+  return m_nameTreeIterator == other.m_nameTreeIterator;
+}
+
+inline bool
+Fib::const_iterator::operator!=(const Fib::const_iterator& other) const
+{
+  return m_nameTreeIterator != other.m_nameTreeIterator;
+}
+
+} // namespace nfd
+
+#endif // NFD_DAEMON_TABLE_FIB_HPP
diff --git a/NFD/daemon/table/measurements-accessor.cpp b/NFD/daemon/table/measurements-accessor.cpp
new file mode 100644
index 0000000..81b1432
--- /dev/null
+++ b/NFD/daemon/table/measurements-accessor.cpp
@@ -0,0 +1,59 @@
+/* -*- 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/>.
+ */
+
+#include "measurements-accessor.hpp"
+
+namespace nfd {
+
+using fw::Strategy;
+
+MeasurementsAccessor::MeasurementsAccessor(Measurements& measurements,
+                                           StrategyChoice& strategyChoice,
+                                           Strategy* strategy)
+  : m_measurements(measurements)
+  , m_strategyChoice(strategyChoice)
+  , m_strategy(strategy)
+{
+}
+
+MeasurementsAccessor::~MeasurementsAccessor()
+{
+}
+
+shared_ptr<measurements::Entry>
+MeasurementsAccessor::filter(const shared_ptr<measurements::Entry>& entry)
+{
+  if (!static_cast<bool>(entry)) {
+    return entry;
+  }
+
+  Strategy& effectiveStrategy = m_strategyChoice.findEffectiveStrategy(*entry);
+  if (&effectiveStrategy == m_strategy) {
+    return entry;
+  }
+  return shared_ptr<measurements::Entry>();
+}
+
+} // namespace nfd
diff --git a/NFD/daemon/table/measurements-accessor.hpp b/NFD/daemon/table/measurements-accessor.hpp
new file mode 100644
index 0000000..872cff2
--- /dev/null
+++ b/NFD/daemon/table/measurements-accessor.hpp
@@ -0,0 +1,122 @@
+/* -*- 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_DAEMON_TABLE_MEASUREMENTS_ACCESSOR_HPP
+#define NFD_DAEMON_TABLE_MEASUREMENTS_ACCESSOR_HPP
+
+#include "measurements.hpp"
+#include "strategy-choice.hpp"
+
+namespace nfd {
+
+namespace fw {
+class Strategy;
+}
+
+/** \brief allows Strategy to access portion of Measurements table under its namespace
+ */
+class MeasurementsAccessor : noncopyable
+{
+public:
+  MeasurementsAccessor(Measurements& measurements, StrategyChoice& strategyChoice,
+                       fw::Strategy* strategy);
+
+  ~MeasurementsAccessor();
+
+  /** \brief find or insert a Measurements entry for name
+   */
+  shared_ptr<measurements::Entry>
+  get(const Name& name);
+
+  /** \brief find or insert a Measurements entry for fibEntry->getPrefix()
+   */
+  shared_ptr<measurements::Entry>
+  get(const fib::Entry& fibEntry);
+
+  /** \brief find or insert a Measurements entry for pitEntry->getName()
+   */
+  shared_ptr<measurements::Entry>
+  get(const pit::Entry& pitEntry);
+
+  /** \brief find or insert a Measurements entry for child's parent
+   *  \retval nullptr if child is the root entry
+   */
+  shared_ptr<measurements::Entry>
+  getParent(const measurements::Entry& child);
+
+  /** \brief extend lifetime of an entry
+   *
+   *  The entry will be kept until at least now()+lifetime.
+   */
+  void
+  extendLifetime(measurements::Entry& entry, const time::nanoseconds& lifetime);
+
+private:
+  /** \brief perform access control to Measurements entry
+   *  \return entry if strategy has access to namespace, otherwise nullptr
+   */
+  shared_ptr<measurements::Entry>
+  filter(const shared_ptr<measurements::Entry>& entry);
+
+private:
+  Measurements& m_measurements;
+  StrategyChoice& m_strategyChoice;
+  fw::Strategy* m_strategy;
+};
+
+inline shared_ptr<measurements::Entry>
+MeasurementsAccessor::get(const Name& name)
+{
+  return this->filter(m_measurements.get(name));
+}
+
+inline shared_ptr<measurements::Entry>
+MeasurementsAccessor::get(const fib::Entry& fibEntry)
+{
+  return this->filter(m_measurements.get(fibEntry));
+}
+
+inline shared_ptr<measurements::Entry>
+MeasurementsAccessor::get(const pit::Entry& pitEntry)
+{
+  return this->filter(m_measurements.get(pitEntry));
+}
+
+inline shared_ptr<measurements::Entry>
+MeasurementsAccessor::getParent(const measurements::Entry& child)
+{
+  return this->filter(m_measurements.getParent(child));
+}
+
+inline void
+MeasurementsAccessor::extendLifetime(measurements::Entry& entry,
+                                     const time::nanoseconds& lifetime)
+{
+  m_measurements.extendLifetime(entry, lifetime);
+}
+
+} // namespace nfd
+
+#endif // NFD_DAEMON_TABLE_MEASUREMENTS_ACCESSOR_HPP
diff --git a/NFD/daemon/table/measurements-entry.cpp b/NFD/daemon/table/measurements-entry.cpp
new file mode 100644
index 0000000..6858399
--- /dev/null
+++ b/NFD/daemon/table/measurements-entry.cpp
@@ -0,0 +1,37 @@
+/* -*- 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
+ *
+ * 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 "measurements-entry.hpp"
+
+namespace nfd {
+namespace measurements {
+
+Entry::Entry(const Name& name)
+  : m_name(name)
+  , m_expiry(time::steady_clock::TimePoint::min())
+{
+}
+
+} // namespace measurements
+} // namespace nfd
diff --git a/NFD/daemon/table/measurements-entry.hpp b/NFD/daemon/table/measurements-entry.hpp
new file mode 100644
index 0000000..6593ee9
--- /dev/null
+++ b/NFD/daemon/table/measurements-entry.hpp
@@ -0,0 +1,78 @@
+/* -*- 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
+ *
+ * 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_TABLE_MEASUREMENTS_ENTRY_HPP
+#define NFD_DAEMON_TABLE_MEASUREMENTS_ENTRY_HPP
+
+#include "common.hpp"
+#include "strategy-info-host.hpp"
+#include "core/scheduler.hpp"
+
+namespace nfd {
+
+class NameTree;
+
+namespace name_tree {
+class Entry;
+}
+
+class Measurements;
+
+namespace measurements {
+
+/** \class Entry
+ *  \brief represents a Measurements entry
+ */
+class Entry : public StrategyInfoHost, noncopyable
+{
+public:
+  explicit
+  Entry(const Name& name);
+
+  const Name&
+  getName() const;
+
+private:
+  Name m_name;
+
+private: // lifetime
+  time::steady_clock::TimePoint m_expiry;
+  EventId m_cleanup;
+  shared_ptr<name_tree::Entry> m_nameTreeEntry;
+
+  friend class nfd::NameTree;
+  friend class nfd::name_tree::Entry;
+  friend class nfd::Measurements;
+};
+
+inline const Name&
+Entry::getName() const
+{
+  return m_name;
+}
+
+} // namespace measurements
+} // namespace nfd
+
+#endif // NFD_DAEMON_TABLE_MEASUREMENTS_ENTRY_HPP
diff --git a/NFD/daemon/table/measurements.cpp b/NFD/daemon/table/measurements.cpp
new file mode 100644
index 0000000..8a24306
--- /dev/null
+++ b/NFD/daemon/table/measurements.cpp
@@ -0,0 +1,144 @@
+/* -*- 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/>.
+ */
+
+#include "measurements.hpp"
+#include "name-tree.hpp"
+#include "pit-entry.hpp"
+#include "fib-entry.hpp"
+
+namespace nfd {
+
+Measurements::Measurements(NameTree& nameTree)
+  : m_nameTree(nameTree)
+  , m_nItems(0)
+{
+}
+
+shared_ptr<measurements::Entry>
+Measurements::get(name_tree::Entry& nte)
+{
+  shared_ptr<measurements::Entry> entry = nte.getMeasurementsEntry();
+  if (entry != nullptr)
+    return entry;
+
+  entry = make_shared<measurements::Entry>(nte.getPrefix());
+  nte.setMeasurementsEntry(entry);
+  ++m_nItems;
+
+  entry->m_expiry = time::steady_clock::now() + getInitialLifetime();
+  entry->m_cleanup = scheduler::schedule(getInitialLifetime(),
+                                         bind(&Measurements::cleanup, this, ref(*entry)));
+
+  return entry;
+}
+
+shared_ptr<measurements::Entry>
+Measurements::get(const Name& name)
+{
+  shared_ptr<name_tree::Entry> nte = m_nameTree.lookup(name);
+  return this->get(*nte);
+}
+
+shared_ptr<measurements::Entry>
+Measurements::get(const fib::Entry& fibEntry)
+{
+  shared_ptr<name_tree::Entry> nte = m_nameTree.get(fibEntry);
+  return this->get(*nte);
+}
+
+shared_ptr<measurements::Entry>
+Measurements::get(const pit::Entry& pitEntry)
+{
+  shared_ptr<name_tree::Entry> nte = m_nameTree.get(pitEntry);
+  return this->get(*nte);
+}
+
+shared_ptr<measurements::Entry>
+Measurements::getParent(const measurements::Entry& child)
+{
+  if (child.getName().size() == 0) { // the root entry
+    return nullptr;
+  }
+
+  shared_ptr<name_tree::Entry> nteChild = m_nameTree.get(child);
+  shared_ptr<name_tree::Entry> nte = nteChild->getParent();
+  BOOST_ASSERT(nte != nullptr);
+  return this->get(*nte);
+}
+
+shared_ptr<measurements::Entry>
+Measurements::findLongestPrefixMatch(const Name& name) const
+{
+  shared_ptr<name_tree::Entry> nte = m_nameTree.findLongestPrefixMatch(name,
+      [] (const name_tree::Entry& nte) { return nte.getMeasurementsEntry() != nullptr; });
+  if (nte != nullptr) {
+    return nte->getMeasurementsEntry();
+  }
+  return nullptr;
+}
+
+shared_ptr<measurements::Entry>
+Measurements::findExactMatch(const Name& name) const
+{
+  shared_ptr<name_tree::Entry> nte = m_nameTree.lookup(name);
+  if (nte != nullptr)
+    return nte->getMeasurementsEntry();
+  return nullptr;
+}
+
+void
+Measurements::extendLifetime(measurements::Entry& entry,
+                             const time::nanoseconds& lifetime)
+{
+  shared_ptr<name_tree::Entry> nte = m_nameTree.get(entry);
+  if (nte == nullptr ||
+      nte->getMeasurementsEntry().get() != &entry) {
+    // entry is already gone; it is a dangling reference
+    return;
+  }
+
+  time::steady_clock::TimePoint expiry = time::steady_clock::now() + lifetime;
+  if (entry.m_expiry >= expiry) {
+    // has longer lifetime, not extending
+    return;
+  }
+
+  scheduler::cancel(entry.m_cleanup);
+  entry.m_expiry = expiry;
+  entry.m_cleanup = scheduler::schedule(lifetime, bind(&Measurements::cleanup, this, ref(entry)));
+}
+
+void
+Measurements::cleanup(measurements::Entry& entry)
+{
+  shared_ptr<name_tree::Entry> nte = m_nameTree.get(entry);
+  if (nte != nullptr) {
+    nte->setMeasurementsEntry(nullptr);
+    m_nameTree.eraseEntryIfEmpty(nte);
+    m_nItems--;
+  }
+}
+
+} // namespace nfd
diff --git a/NFD/daemon/table/measurements.hpp b/NFD/daemon/table/measurements.hpp
new file mode 100644
index 0000000..71d8db5
--- /dev/null
+++ b/NFD/daemon/table/measurements.hpp
@@ -0,0 +1,123 @@
+/* -*- 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_DAEMON_TABLE_MEASUREMENTS_HPP
+#define NFD_DAEMON_TABLE_MEASUREMENTS_HPP
+
+#include "measurements-entry.hpp"
+#include "name-tree.hpp"
+
+namespace nfd {
+
+namespace fib {
+class Entry;
+}
+
+namespace pit {
+class Entry;
+}
+
+
+/** \class Measurement
+ *  \brief represents the Measurements table
+ */
+class Measurements : noncopyable
+{
+public:
+  explicit
+  Measurements(NameTree& nametree);
+
+  /** \brief find or insert a Measurements entry for name
+   */
+  shared_ptr<measurements::Entry>
+  get(const Name& name);
+
+  /** \brief find or insert a Measurements entry for fibEntry->getPrefix()
+   */
+  shared_ptr<measurements::Entry>
+  get(const fib::Entry& fibEntry);
+
+  /** \brief find or insert a Measurements entry for pitEntry->getName()
+   */
+  shared_ptr<measurements::Entry>
+  get(const pit::Entry& pitEntry);
+
+  /** \brief find or insert a Measurements entry for child's parent
+   *  \retval nullptr if child is the root entry
+   */
+  shared_ptr<measurements::Entry>
+  getParent(const measurements::Entry& child);
+
+  /** \brief perform a longest prefix match
+   */
+  shared_ptr<measurements::Entry>
+  findLongestPrefixMatch(const Name& name) const;
+
+  /** \brief perform an exact match
+   */
+  shared_ptr<measurements::Entry>
+  findExactMatch(const Name& name) const;
+
+  static time::nanoseconds
+  getInitialLifetime();
+
+  /** \brief extend lifetime of an entry
+   *
+   *  The entry will be kept until at least now()+lifetime.
+   */
+  void
+  extendLifetime(measurements::Entry& entry, const time::nanoseconds& lifetime);
+
+  size_t
+  size() const;
+
+private:
+  void
+  cleanup(measurements::Entry& entry);
+
+  shared_ptr<measurements::Entry>
+  get(name_tree::Entry& nte);
+
+private:
+  NameTree& m_nameTree;
+  size_t m_nItems;
+  static const time::nanoseconds s_defaultLifetime;
+};
+
+inline time::nanoseconds
+Measurements::getInitialLifetime()
+{
+  return time::seconds(4);
+}
+
+inline size_t
+Measurements::size() const
+{
+  return m_nItems;
+}
+
+} // namespace nfd
+
+#endif // NFD_DAEMON_TABLE_MEASUREMENTS_HPP
diff --git a/NFD/daemon/table/name-tree-entry.cpp b/NFD/daemon/table/name-tree-entry.cpp
new file mode 100644
index 0000000..017dbfe
--- /dev/null
+++ b/NFD/daemon/table/name-tree-entry.cpp
@@ -0,0 +1,141 @@
+/* -*- 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/>.
+ */
+
+#include "name-tree-entry.hpp"
+
+namespace nfd {
+namespace name_tree {
+
+Node::Node()
+  : m_prev(0)
+  , m_next(0)
+{
+}
+
+Node::~Node()
+{
+  // erase the Name Tree Nodes that were created to
+  // resolve hash collisions
+  // So before erasing a single node, make sure its m_next == 0
+  // See eraseEntryIfEmpty in name-tree.cpp
+  if (m_next != 0)
+    delete m_next;
+}
+
+Entry::Entry(const Name& name)
+  : m_hash(0)
+  , m_prefix(name)
+{
+}
+
+Entry::~Entry()
+{
+}
+
+bool
+Entry::isEmpty() const
+{
+  return m_children.empty() &&
+         !static_cast<bool>(m_fibEntry) &&
+         m_pitEntries.empty() &&
+         !static_cast<bool>(m_measurementsEntry) &&
+         !static_cast<bool>(m_strategyChoiceEntry);
+}
+
+void
+Entry::setFibEntry(shared_ptr<fib::Entry> fibEntry)
+{
+  if (static_cast<bool>(fibEntry)) {
+    BOOST_ASSERT(!static_cast<bool>(fibEntry->m_nameTreeEntry));
+  }
+
+  if (static_cast<bool>(m_fibEntry)) {
+    m_fibEntry->m_nameTreeEntry.reset();
+  }
+  m_fibEntry = fibEntry;
+  if (static_cast<bool>(m_fibEntry)) {
+    m_fibEntry->m_nameTreeEntry = this->shared_from_this();
+  }
+}
+
+void
+Entry::insertPitEntry(shared_ptr<pit::Entry> pitEntry)
+{
+  BOOST_ASSERT(static_cast<bool>(pitEntry));
+  BOOST_ASSERT(!static_cast<bool>(pitEntry->m_nameTreeEntry));
+
+  m_pitEntries.push_back(pitEntry);
+  pitEntry->m_nameTreeEntry = this->shared_from_this();
+}
+
+void
+Entry::erasePitEntry(shared_ptr<pit::Entry> pitEntry)
+{
+  BOOST_ASSERT(static_cast<bool>(pitEntry));
+  BOOST_ASSERT(pitEntry->m_nameTreeEntry.get() == this);
+
+  std::vector<shared_ptr<pit::Entry> >::iterator it =
+    std::find(m_pitEntries.begin(), m_pitEntries.end(), pitEntry);
+  BOOST_ASSERT(it != m_pitEntries.end());
+
+  *it = m_pitEntries.back();
+  m_pitEntries.pop_back();
+  pitEntry->m_nameTreeEntry.reset();
+}
+
+void
+Entry::setMeasurementsEntry(shared_ptr<measurements::Entry> measurementsEntry)
+{
+  if (static_cast<bool>(measurementsEntry)) {
+    BOOST_ASSERT(!static_cast<bool>(measurementsEntry->m_nameTreeEntry));
+  }
+
+  if (static_cast<bool>(m_measurementsEntry)) {
+    m_measurementsEntry->m_nameTreeEntry.reset();
+  }
+  m_measurementsEntry = measurementsEntry;
+  if (static_cast<bool>(m_measurementsEntry)) {
+    m_measurementsEntry->m_nameTreeEntry = this->shared_from_this();
+  }
+}
+
+void
+Entry::setStrategyChoiceEntry(shared_ptr<strategy_choice::Entry> strategyChoiceEntry)
+{
+  if (static_cast<bool>(strategyChoiceEntry)) {
+    BOOST_ASSERT(!static_cast<bool>(strategyChoiceEntry->m_nameTreeEntry));
+  }
+
+  if (static_cast<bool>(m_strategyChoiceEntry)) {
+    m_strategyChoiceEntry->m_nameTreeEntry.reset();
+  }
+  m_strategyChoiceEntry = strategyChoiceEntry;
+  if (static_cast<bool>(m_strategyChoiceEntry)) {
+    m_strategyChoiceEntry->m_nameTreeEntry = this->shared_from_this();
+  }
+}
+
+} // namespace name_tree
+} // namespace nfd
diff --git a/NFD/daemon/table/name-tree-entry.hpp b/NFD/daemon/table/name-tree-entry.hpp
new file mode 100644
index 0000000..d0e3587
--- /dev/null
+++ b/NFD/daemon/table/name-tree-entry.hpp
@@ -0,0 +1,223 @@
+/* -*- 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_DAEMON_TABLE_NAME_TREE_ENTRY_HPP
+#define NFD_DAEMON_TABLE_NAME_TREE_ENTRY_HPP
+
+#include "common.hpp"
+#include "table/fib-entry.hpp"
+#include "table/pit-entry.hpp"
+#include "table/measurements-entry.hpp"
+#include "table/strategy-choice-entry.hpp"
+
+namespace nfd {
+
+class NameTree;
+
+namespace name_tree {
+
+// Forward declarations
+class Node;
+class Entry;
+
+/**
+ * \brief Name Tree Node Class
+ */
+class Node
+{
+public:
+  Node();
+
+  ~Node();
+
+public:
+  // variables are in public as this is just a data structure
+  shared_ptr<Entry> m_entry; // Name Tree Entry (i.e., Name Prefix Entry)
+  Node* m_prev; // Previous Name Tree Node (to resolve hash collision)
+  Node* m_next; // Next Name Tree Node (to resolve hash collision)
+};
+
+/**
+ * \brief Name Tree Entry Class
+ */
+class Entry : public enable_shared_from_this<Entry>, noncopyable
+{
+public:
+  explicit
+  Entry(const Name& prefix);
+
+  ~Entry();
+
+  const Name&
+  getPrefix() const;
+
+  void
+  setHash(size_t hash);
+
+  size_t
+  getHash() const;
+
+  void
+  setParent(shared_ptr<Entry> parent);
+
+  shared_ptr<Entry>
+  getParent() const;
+
+  std::vector<shared_ptr<Entry> >&
+  getChildren();
+
+  bool
+  hasChildren() const;
+
+  bool
+  isEmpty() const;
+
+public: // attached table entries
+  void
+  setFibEntry(shared_ptr<fib::Entry> fibEntry);
+
+  shared_ptr<fib::Entry>
+  getFibEntry() const;
+
+  void
+  insertPitEntry(shared_ptr<pit::Entry> pitEntry);
+
+  void
+  erasePitEntry(shared_ptr<pit::Entry> pitEntry);
+
+  bool
+  hasPitEntries() const;
+
+  const std::vector<shared_ptr<pit::Entry> >&
+  getPitEntries() const;
+
+  void
+  setMeasurementsEntry(shared_ptr<measurements::Entry> measurementsEntry);
+
+  shared_ptr<measurements::Entry>
+  getMeasurementsEntry() const;
+
+  void
+  setStrategyChoiceEntry(shared_ptr<strategy_choice::Entry> strategyChoiceEntry);
+
+  shared_ptr<strategy_choice::Entry>
+  getStrategyChoiceEntry() const;
+
+private:
+  // Benefits of storing m_hash
+  // 1. m_hash is compared before m_prefix is compared
+  // 2. fast hash table resize support
+  size_t m_hash;
+  Name m_prefix;
+  shared_ptr<Entry> m_parent;     // Pointing to the parent entry.
+  std::vector<shared_ptr<Entry> > m_children; // Children pointers.
+  shared_ptr<fib::Entry> m_fibEntry;
+  std::vector<shared_ptr<pit::Entry> > m_pitEntries;
+  shared_ptr<measurements::Entry> m_measurementsEntry;
+  shared_ptr<strategy_choice::Entry> m_strategyChoiceEntry;
+
+  // get the Name Tree Node that is associated with this Name Tree Entry
+  Node* m_node;
+
+  // Make private members accessible by Name Tree
+  friend class nfd::NameTree;
+};
+
+inline const Name&
+Entry::getPrefix() const
+{
+  return m_prefix;
+}
+
+inline size_t
+Entry::getHash() const
+{
+  return m_hash;
+}
+
+inline void
+Entry::setHash(size_t hash)
+{
+  m_hash = hash;
+}
+
+inline shared_ptr<Entry>
+Entry::getParent() const
+{
+  return m_parent;
+}
+
+inline void
+Entry::setParent(shared_ptr<Entry> parent)
+{
+  m_parent = parent;
+}
+
+inline std::vector<shared_ptr<name_tree::Entry> >&
+Entry::getChildren()
+{
+  return m_children;
+}
+
+inline bool
+Entry::hasChildren() const
+{
+  return !m_children.empty();
+}
+
+inline shared_ptr<fib::Entry>
+Entry::getFibEntry() const
+{
+  return m_fibEntry;
+}
+
+inline bool
+Entry::hasPitEntries() const
+{
+  return !m_pitEntries.empty();
+}
+
+inline const std::vector<shared_ptr<pit::Entry> >&
+Entry::getPitEntries() const
+{
+  return m_pitEntries;
+}
+
+inline shared_ptr<measurements::Entry>
+Entry::getMeasurementsEntry() const
+{
+  return m_measurementsEntry;
+}
+
+inline shared_ptr<strategy_choice::Entry>
+Entry::getStrategyChoiceEntry() const
+{
+  return m_strategyChoiceEntry;
+}
+
+} // namespace name_tree
+} // namespace nfd
+
+#endif // NFD_DAEMON_TABLE_NAME_TREE_ENTRY_HPP
diff --git a/NFD/daemon/table/name-tree.cpp b/NFD/daemon/table/name-tree.cpp
new file mode 100644
index 0000000..3ac469d
--- /dev/null
+++ b/NFD/daemon/table/name-tree.cpp
@@ -0,0 +1,776 @@
+/* -*- 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/>.
+ */
+
+#include "name-tree.hpp"
+#include "core/logger.hpp"
+#include "core/city-hash.hpp"
+
+#include <boost/concept/assert.hpp>
+#include <boost/concept_check.hpp>
+#include <type_traits>
+
+namespace nfd {
+
+NFD_LOG_INIT("NameTree");
+
+// http://en.cppreference.com/w/cpp/concept/ForwardIterator
+BOOST_CONCEPT_ASSERT((boost::ForwardIterator<NameTree::const_iterator>));
+// boost::ForwardIterator follows SGI standard http://www.sgi.com/tech/stl/ForwardIterator.html,
+// which doesn't require DefaultConstructible
+#ifdef HAVE_IS_DEFAULT_CONSTRUCTIBLE
+static_assert(std::is_default_constructible<NameTree::const_iterator>::value,
+              "NameTree::const_iterator must be default-constructible");
+#else
+BOOST_CONCEPT_ASSERT((boost::DefaultConstructible<NameTree::const_iterator>));
+#endif // HAVE_IS_DEFAULT_CONSTRUCTIBLE
+
+namespace name_tree {
+
+class Hash32
+{
+public:
+  static size_t
+  compute(const char* buffer, size_t length)
+  {
+    return static_cast<size_t>(CityHash32(buffer, length));
+  }
+};
+
+class Hash64
+{
+public:
+  static size_t
+  compute(const char* buffer, size_t length)
+  {
+    return static_cast<size_t>(CityHash64(buffer, length));
+  }
+};
+
+typedef boost::mpl::if_c<sizeof(size_t) >= 8, Hash64, Hash32>::type CityHash;
+
+// Interface of different hash functions
+size_t
+computeHash(const Name& prefix)
+{
+  prefix.wireEncode();  // guarantees prefix's wire buffer is not empty
+
+  size_t hashValue = 0;
+  size_t hashUpdate = 0;
+
+  for (Name::const_iterator it = prefix.begin(); it != prefix.end(); it++)
+    {
+      const char* wireFormat = reinterpret_cast<const char*>( it->wire() );
+      hashUpdate = CityHash::compute(wireFormat, it->size());
+      hashValue ^= hashUpdate;
+    }
+
+  return hashValue;
+}
+
+std::vector<size_t>
+computeHashSet(const Name& prefix)
+{
+  prefix.wireEncode();  // guarantees prefix's wire buffer is not empty
+
+  size_t hashValue = 0;
+  size_t hashUpdate = 0;
+
+  std::vector<size_t> hashValueSet;
+  hashValueSet.push_back(hashValue);
+
+  for (Name::const_iterator it = prefix.begin(); it != prefix.end(); it++)
+    {
+      const char* wireFormat = reinterpret_cast<const char*>( it->wire() );
+      hashUpdate = CityHash::compute(wireFormat, it->size());
+      hashValue ^= hashUpdate;
+      hashValueSet.push_back(hashValue);
+    }
+
+  return hashValueSet;
+}
+
+} // namespace name_tree
+
+NameTree::NameTree(size_t nBuckets)
+  : m_nItems(0)
+  , m_nBuckets(nBuckets)
+  , m_minNBuckets(nBuckets)
+  , m_enlargeLoadFactor(0.5)       // more than 50% buckets loaded
+  , m_enlargeFactor(2)       // double the hash table size
+  , m_shrinkLoadFactor(0.1) // less than 10% buckets loaded
+  , m_shrinkFactor(0.5)     // reduce the number of buckets by half
+  , m_endIterator(FULL_ENUMERATE_TYPE, *this, m_end)
+{
+  m_enlargeThreshold = static_cast<size_t>(m_enlargeLoadFactor *
+                                          static_cast<double>(m_nBuckets));
+
+  m_shrinkThreshold = static_cast<size_t>(m_shrinkLoadFactor *
+                                          static_cast<double>(m_nBuckets));
+
+  // array of node pointers
+  m_buckets = new name_tree::Node*[m_nBuckets];
+  // Initialize the pointer array
+  for (size_t i = 0; i < m_nBuckets; i++)
+    m_buckets[i] = 0;
+}
+
+NameTree::~NameTree()
+{
+  for (size_t i = 0; i < m_nBuckets; i++)
+    {
+      if (m_buckets[i] != 0) {
+        delete m_buckets[i];
+      }
+    }
+
+  delete [] m_buckets;
+}
+
+// insert() is a private function, and called by only lookup()
+std::pair<shared_ptr<name_tree::Entry>, bool>
+NameTree::insert(const Name& prefix)
+{
+  NFD_LOG_TRACE("insert " << prefix);
+
+  size_t hashValue = name_tree::computeHash(prefix);
+  size_t loc = hashValue % m_nBuckets;
+
+  NFD_LOG_TRACE("Name " << prefix << " hash value = " << hashValue << "  location = " << loc);
+
+  // Check if this Name has been stored
+  name_tree::Node* node = m_buckets[loc];
+  name_tree::Node* nodePrev = node;  // initialize nodePrev to node
+
+  for (node = m_buckets[loc]; node != 0; node = node->m_next)
+    {
+      if (static_cast<bool>(node->m_entry))
+        {
+          if (prefix == node->m_entry->m_prefix)
+            {
+              return std::make_pair(node->m_entry, false); // false: old entry
+            }
+        }
+      nodePrev = node;
+    }
+
+  NFD_LOG_TRACE("Did not find " << prefix << ", need to insert it to the table");
+
+  // If no bucket is empty occupied, we need to create a new node, and it is
+  // linked from nodePrev
+  node = new name_tree::Node();
+  node->m_prev = nodePrev;
+
+  if (nodePrev == 0)
+    {
+      m_buckets[loc] = node;
+    }
+  else
+    {
+      nodePrev->m_next = node;
+    }
+
+  // Create a new Entry
+  shared_ptr<name_tree::Entry> entry(make_shared<name_tree::Entry>(prefix));
+  entry->setHash(hashValue);
+  node->m_entry = entry; // link the Entry to its Node
+  entry->m_node = node; // link the node to Entry. Used in eraseEntryIfEmpty.
+
+  return std::make_pair(entry, true); // true: new entry
+}
+
+// Name Prefix Lookup. Create Name Tree Entry if not found
+shared_ptr<name_tree::Entry>
+NameTree::lookup(const Name& prefix)
+{
+  NFD_LOG_TRACE("lookup " << prefix);
+
+  shared_ptr<name_tree::Entry> entry;
+  shared_ptr<name_tree::Entry> parent;
+
+  for (size_t i = 0; i <= prefix.size(); i++)
+    {
+      Name temp = prefix.getPrefix(i);
+
+      // insert() will create the entry if it does not exist.
+      std::pair<shared_ptr<name_tree::Entry>, bool> ret = insert(temp);
+      entry = ret.first;
+
+      if (ret.second == true)
+        {
+          m_nItems++; // Increase the counter
+          entry->m_parent = parent;
+
+          if (static_cast<bool>(parent))
+            {
+              parent->m_children.push_back(entry);
+            }
+        }
+
+      if (m_nItems > m_enlargeThreshold)
+        {
+          resize(m_enlargeFactor * m_nBuckets);
+        }
+
+      parent = entry;
+    }
+  return entry;
+}
+
+// Exact Match
+shared_ptr<name_tree::Entry>
+NameTree::findExactMatch(const Name& prefix) const
+{
+  NFD_LOG_TRACE("findExactMatch " << prefix);
+
+  size_t hashValue = name_tree::computeHash(prefix);
+  size_t loc = hashValue % m_nBuckets;
+
+  NFD_LOG_TRACE("Name " << prefix << " hash value = " << hashValue <<
+                "  location = " << loc);
+
+  shared_ptr<name_tree::Entry> entry;
+  name_tree::Node* node = 0;
+
+  for (node = m_buckets[loc]; node != 0; node = node->m_next)
+    {
+      entry = node->m_entry;
+      if (static_cast<bool>(entry))
+        {
+          if (hashValue == entry->getHash() && prefix == entry->getPrefix())
+            {
+              return entry;
+            }
+        } // if entry
+    } // for node
+
+  // if not found, the default value of entry (null pointer) will be returned
+  entry.reset();
+  return entry;
+}
+
+// Longest Prefix Match
+shared_ptr<name_tree::Entry>
+NameTree::findLongestPrefixMatch(const Name& prefix, const name_tree::EntrySelector& entrySelector) const
+{
+  NFD_LOG_TRACE("findLongestPrefixMatch " << prefix);
+
+  shared_ptr<name_tree::Entry> entry;
+  std::vector<size_t> hashValueSet = name_tree::computeHashSet(prefix);
+
+  size_t hashValue = 0;
+  size_t loc = 0;
+
+  for (int i = static_cast<int>(prefix.size()); i >= 0; i--)
+    {
+      hashValue = hashValueSet[i];
+      loc = hashValue % m_nBuckets;
+
+      name_tree::Node* node = 0;
+      for (node = m_buckets[loc]; node != 0; node = node->m_next)
+        {
+          entry = node->m_entry;
+          if (static_cast<bool>(entry))
+            {
+              // isPrefixOf() is used to avoid making a copy of the name
+              if (hashValue == entry->getHash() &&
+                  entry->getPrefix().isPrefixOf(prefix) &&
+                  entrySelector(*entry))
+                {
+                  return entry;
+                }
+            } // if entry
+        } // for node
+    }
+
+  // if not found, the default value of entry (null pointer) will be returned
+  entry.reset();
+  return entry;
+}
+
+shared_ptr<name_tree::Entry>
+NameTree::findLongestPrefixMatch(shared_ptr<name_tree::Entry> entry,
+                                 const name_tree::EntrySelector& entrySelector) const
+{
+  while (static_cast<bool>(entry))
+    {
+      if (entrySelector(*entry))
+        return entry;
+      entry = entry->getParent();
+    }
+  return shared_ptr<name_tree::Entry>();
+}
+
+// return {false: this entry is not empty, true: this entry is empty and erased}
+bool
+NameTree::eraseEntryIfEmpty(shared_ptr<name_tree::Entry> entry)
+{
+  BOOST_ASSERT(static_cast<bool>(entry));
+
+  NFD_LOG_TRACE("eraseEntryIfEmpty " << entry->getPrefix());
+
+  // first check if this Entry can be erased
+  if (entry->isEmpty())
+    {
+      // update child-related info in the parent
+      shared_ptr<name_tree::Entry> parent = entry->getParent();
+
+      if (static_cast<bool>(parent))
+        {
+          std::vector<shared_ptr<name_tree::Entry> >& parentChildrenList =
+            parent->getChildren();
+
+          bool isFound = false;
+          size_t size = parentChildrenList.size();
+          for (size_t i = 0; i < size; i++)
+            {
+              if (parentChildrenList[i] == entry)
+                {
+                  parentChildrenList[i] = parentChildrenList[size - 1];
+                  parentChildrenList.pop_back();
+                  isFound = true;
+                  break;
+                }
+            }
+
+          BOOST_VERIFY(isFound == true);
+        }
+
+      // remove this Entry and its Name Tree Node
+      name_tree::Node* node = entry->m_node;
+      name_tree::Node* nodePrev = node->m_prev;
+
+      // configure the previous node
+      if (nodePrev != 0)
+        {
+          // link the previous node to the next node
+          nodePrev->m_next = node->m_next;
+        }
+      else
+        {
+          m_buckets[entry->getHash() % m_nBuckets] = node->m_next;
+        }
+
+      // link the previous node with the next node (skip the erased one)
+      if (node->m_next != 0)
+        {
+          node->m_next->m_prev = nodePrev;
+          node->m_next = 0;
+        }
+
+      BOOST_ASSERT(node->m_next == 0);
+
+      m_nItems--;
+      delete node;
+
+      if (static_cast<bool>(parent))
+        eraseEntryIfEmpty(parent);
+
+      size_t newNBuckets = static_cast<size_t>(m_shrinkFactor *
+                                     static_cast<double>(m_nBuckets));
+
+      if (newNBuckets >= m_minNBuckets && m_nItems < m_shrinkThreshold)
+        {
+          resize(newNBuckets);
+        }
+
+      return true;
+
+    } // if this entry is empty
+
+  return false; // if this entry is not empty
+}
+
+boost::iterator_range<NameTree::const_iterator>
+NameTree::fullEnumerate(const name_tree::EntrySelector& entrySelector) const
+{
+  NFD_LOG_TRACE("fullEnumerate");
+
+  // find the first eligible entry
+  for (size_t i = 0; i < m_nBuckets; i++) {
+    for (name_tree::Node* node = m_buckets[i]; node != 0; node = node->m_next) {
+      if (static_cast<bool>(node->m_entry) && entrySelector(*node->m_entry)) {
+        const_iterator it(FULL_ENUMERATE_TYPE, *this, node->m_entry, entrySelector);
+        return {it, end()};
+      }
+    }
+  }
+
+  // If none of the entry satisfies the requirements, then return the end() iterator.
+  return {end(), end()};
+}
+
+boost::iterator_range<NameTree::const_iterator>
+NameTree::partialEnumerate(const Name& prefix,
+                           const name_tree::EntrySubTreeSelector& entrySubTreeSelector) const
+{
+  // the first step is to process the root node
+  shared_ptr<name_tree::Entry> entry = findExactMatch(prefix);
+  if (!static_cast<bool>(entry)) {
+    return {end(), end()};
+  }
+
+  std::pair<bool, bool>result = entrySubTreeSelector(*entry);
+  const_iterator it(PARTIAL_ENUMERATE_TYPE,
+                    *this,
+                    entry,
+                    name_tree::AnyEntry(),
+                    entrySubTreeSelector);
+
+  it.m_shouldVisitChildren = (result.second && entry->hasChildren());
+
+  if (result.first) {
+    // root node is acceptable
+  }
+  else {
+    // let the ++ operator handle it
+    ++it;
+  }
+  return {it, end()};
+}
+
+boost::iterator_range<NameTree::const_iterator>
+NameTree::findAllMatches(const Name& prefix,
+                         const name_tree::EntrySelector& entrySelector) const
+{
+  NFD_LOG_TRACE("NameTree::findAllMatches" << prefix);
+
+  // As we are using Name Prefix Hash Table, and the current LPM() is
+  // implemented as starting from full name, and reduce the number of
+  // components by 1 each time, we could use it here.
+  // For trie-like design, it could be more efficient by walking down the
+  // trie from the root node.
+
+  shared_ptr<name_tree::Entry> entry = findLongestPrefixMatch(prefix, entrySelector);
+
+  if (static_cast<bool>(entry)) {
+    const_iterator begin(FIND_ALL_MATCHES_TYPE, *this, entry, entrySelector);
+    return {begin, end()};
+  }
+  // If none of the entry satisfies the requirements, then return the end() iterator.
+  return {end(), end()};
+}
+
+// Hash Table Resize
+void
+NameTree::resize(size_t newNBuckets)
+{
+  NFD_LOG_TRACE("resize");
+
+  name_tree::Node** newBuckets = new name_tree::Node*[newNBuckets];
+  size_t count = 0;
+
+  // referenced ccnx hashtb.c hashtb_rehash()
+  name_tree::Node** pp = 0;
+  name_tree::Node* p = 0;
+  name_tree::Node* pre = 0;
+  name_tree::Node* q = 0; // record p->m_next
+  size_t i;
+  uint32_t h;
+  uint32_t b;
+
+  for (i = 0; i < newNBuckets; i++)
+    {
+      newBuckets[i] = 0;
+    }
+
+  for (i = 0; i < m_nBuckets; i++)
+    {
+      for (p = m_buckets[i]; p != 0; p = q)
+        {
+          count++;
+          q = p->m_next;
+          BOOST_ASSERT(static_cast<bool>(p->m_entry));
+          h = p->m_entry->m_hash;
+          b = h % newNBuckets;
+          pre = 0;
+          for (pp = &newBuckets[b]; *pp != 0; pp = &((*pp)->m_next))
+            {
+              pre = *pp;
+              continue;
+            }
+          p->m_prev = pre;
+          p->m_next = *pp; // Actually *pp always == 0 in this case
+          *pp = p;
+        }
+    }
+
+  BOOST_ASSERT(count == m_nItems);
+
+  name_tree::Node** oldBuckets = m_buckets;
+  m_buckets = newBuckets;
+  delete [] oldBuckets;
+
+  m_nBuckets = newNBuckets;
+
+  m_enlargeThreshold = static_cast<size_t>(m_enlargeLoadFactor *
+                                              static_cast<double>(m_nBuckets));
+  m_shrinkThreshold = static_cast<size_t>(m_shrinkLoadFactor *
+                                              static_cast<double>(m_nBuckets));
+}
+
+// For debugging
+void
+NameTree::dump(std::ostream& output) const
+{
+  NFD_LOG_TRACE("dump()");
+
+  name_tree::Node* node = 0;
+  shared_ptr<name_tree::Entry> entry;
+
+  using std::endl;
+
+  for (size_t i = 0; i < m_nBuckets; i++)
+    {
+      for (node = m_buckets[i]; node != 0; node = node->m_next)
+        {
+          entry = node->m_entry;
+
+          // if the Entry exist, dump its information
+          if (static_cast<bool>(entry))
+            {
+              output << "Bucket" << i << "\t" << entry->m_prefix.toUri() << endl;
+              output << "\t\tHash " << entry->m_hash << endl;
+
+              if (static_cast<bool>(entry->m_parent))
+                {
+                  output << "\t\tparent->" << entry->m_parent->m_prefix.toUri();
+                }
+              else
+                {
+                  output << "\t\tROOT";
+                }
+              output << endl;
+
+              if (entry->m_children.size() != 0)
+                {
+                  output << "\t\tchildren = " << entry->m_children.size() << endl;
+
+                  for (size_t j = 0; j < entry->m_children.size(); j++)
+                    {
+                      output << "\t\t\tChild " << j << " " <<
+                        entry->m_children[j]->getPrefix() << endl;
+                    }
+                }
+
+            } // if (static_cast<bool>(entry))
+
+        } // for node
+    } // for int i
+
+  output << "Bucket count = " << m_nBuckets << endl;
+  output << "Stored item = " << m_nItems << endl;
+  output << "--------------------------\n";
+}
+
+NameTree::const_iterator::const_iterator()
+  : m_nameTree(nullptr)
+{
+}
+
+NameTree::const_iterator::const_iterator(NameTree::IteratorType type,
+                            const NameTree& nameTree,
+                            shared_ptr<name_tree::Entry> entry,
+                            const name_tree::EntrySelector& entrySelector,
+                            const name_tree::EntrySubTreeSelector& entrySubTreeSelector)
+  : m_nameTree(&nameTree)
+  , m_entry(entry)
+  , m_subTreeRoot(entry)
+  , m_entrySelector(make_shared<name_tree::EntrySelector>(entrySelector))
+  , m_entrySubTreeSelector(make_shared<name_tree::EntrySubTreeSelector>(entrySubTreeSelector))
+  , m_type(type)
+  , m_shouldVisitChildren(true)
+{
+}
+
+// operator++()
+NameTree::const_iterator
+NameTree::const_iterator::operator++()
+{
+  NFD_LOG_TRACE("const_iterator::operator++()");
+
+  BOOST_ASSERT(m_entry != m_nameTree->m_end);
+
+  if (m_type == FULL_ENUMERATE_TYPE) // fullEnumerate
+    {
+      bool isFound = false;
+      // process the entries in the same bucket first
+      while (m_entry->m_node->m_next != 0)
+        {
+          m_entry = m_entry->m_node->m_next->m_entry;
+          if ((*m_entrySelector)(*m_entry))
+            {
+              isFound = true;
+              return *this;
+            }
+        }
+
+      // process other buckets
+
+      for (int newLocation = m_entry->m_hash % m_nameTree->m_nBuckets + 1;
+           newLocation < static_cast<int>(m_nameTree->m_nBuckets);
+           ++newLocation)
+        {
+          // process each bucket
+          name_tree::Node* node = m_nameTree->m_buckets[newLocation];
+          while (node != 0)
+            {
+              m_entry = node->m_entry;
+              if ((*m_entrySelector)(*m_entry))
+                {
+                  isFound = true;
+                  return *this;
+                }
+              node = node->m_next;
+            }
+        }
+      BOOST_VERIFY(isFound == false);
+      // Reach to the end()
+      m_entry = m_nameTree->m_end;
+      return *this;
+    }
+
+  if (m_type == PARTIAL_ENUMERATE_TYPE) // partialEnumerate
+    {
+      // We use pre-order traversal.
+      // if at the root, it could have already been accepted, or this
+      // iterator was just declared, and root doesn't satisfy the
+      // requirement
+      // The if() section handles this special case
+      // Essentially, we need to check root's fist child, and the rest will
+      // be the same as normal process
+      if (m_entry == m_subTreeRoot)
+        {
+          if (m_shouldVisitChildren)
+            {
+              m_entry = m_entry->getChildren()[0];
+              std::pair<bool, bool> result = ((*m_entrySubTreeSelector)(*m_entry));
+              m_shouldVisitChildren = (result.second && m_entry->hasChildren());
+              if(result.first)
+                {
+                  return *this;
+                }
+              else
+                {
+                  // the first child did not meet the requirement
+                  // the rest of the process can just fall through the while loop
+                  // as normal
+                }
+            }
+          else
+            {
+              // no children, should return end();
+              // just fall through
+            }
+        }
+
+      // The first thing to do is to visit its child, or go to find its possible
+      // siblings
+      while (m_entry != m_subTreeRoot)
+        {
+          if (m_shouldVisitChildren)
+            {
+              // If this subtree should be visited
+              m_entry = m_entry->getChildren()[0];
+              std::pair<bool, bool> result = ((*m_entrySubTreeSelector)(*m_entry));
+              m_shouldVisitChildren = (result.second && m_entry->hasChildren());
+              if (result.first) // if this node is acceptable
+                {
+                  return *this;
+                }
+              else
+                {
+                  // do nothing, as this node is essentially ignored
+                  // send this node to the while loop.
+                }
+            }
+          else
+            {
+              // Should try to find its sibling
+              shared_ptr<name_tree::Entry> parent = m_entry->getParent();
+
+              std::vector<shared_ptr<name_tree::Entry> >& parentChildrenList = parent->getChildren();
+              bool isFound = false;
+              size_t i = 0;
+              for (i = 0; i < parentChildrenList.size(); i++)
+                {
+                  if (parentChildrenList[i] == m_entry)
+                    {
+                      isFound = true;
+                      break;
+                    }
+                }
+
+              BOOST_VERIFY(isFound == true);
+              if (i < parentChildrenList.size() - 1) // m_entry not the last child
+                {
+                  m_entry = parentChildrenList[i + 1];
+                  std::pair<bool, bool> result = ((*m_entrySubTreeSelector)(*m_entry));
+                  m_shouldVisitChildren = (result.second && m_entry->hasChildren());
+                  if (result.first) // if this node is acceptable
+                    {
+                      return *this;
+                    }
+                  else
+                    {
+                      // do nothing, as this node is essentially ignored
+                      // send this node to the while loop.
+                    }
+                }
+              else
+                {
+                  // m_entry is the last child, no more sibling, should try to find parent's sibling
+                  m_shouldVisitChildren = false;
+                  m_entry = parent;
+                }
+            }
+        }
+
+      m_entry = m_nameTree->m_end;
+      return *this;
+    }
+
+  if (m_type == FIND_ALL_MATCHES_TYPE) // findAllMatches
+    {
+      // Assumption: at the beginning, m_entry was initialized with the first
+      // eligible Name Tree entry (i.e., has a PIT entry that can be satisfied
+      // by the Data packet)
+
+      while (static_cast<bool>(m_entry->getParent()))
+        {
+          m_entry = m_entry->getParent();
+          if ((*m_entrySelector)(*m_entry))
+            return *this;
+        }
+
+      // Reach to the end (Root)
+      m_entry = m_nameTree->m_end;
+      return *this;
+    }
+
+  BOOST_ASSERT(false); // unknown type
+  return *this;
+}
+
+} // namespace nfd
diff --git a/NFD/daemon/table/name-tree.hpp b/NFD/daemon/table/name-tree.hpp
new file mode 100644
index 0000000..8d4e437
--- /dev/null
+++ b/NFD/daemon/table/name-tree.hpp
@@ -0,0 +1,417 @@
+/* -*- 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_DAEMON_TABLE_NAME_TREE_HPP
+#define NFD_DAEMON_TABLE_NAME_TREE_HPP
+
+#include "common.hpp"
+#include "name-tree-entry.hpp"
+
+namespace nfd {
+namespace name_tree {
+
+/**
+ * \brief Compute the hash value of the given name prefix's WIRE FORMAT
+ */
+size_t
+computeHash(const Name& prefix);
+
+/**
+ * \brief Incrementally compute hash values
+ * \return Return a vector of hash values, starting from the root prefix
+ */
+std::vector<size_t>
+computeHashSet(const Name& prefix);
+
+/// a predicate to accept or reject an Entry in find operations
+typedef function<bool (const Entry& entry)> EntrySelector;
+
+/**
+ * \brief a predicate to accept or reject an Entry and its children
+ * \return .first indicates whether entry should be accepted;
+ *         .second indicates whether entry's children should be visited
+ */
+typedef function<std::pair<bool,bool> (const Entry& entry)> EntrySubTreeSelector;
+
+struct AnyEntry {
+  bool
+  operator()(const Entry& entry)
+  {
+    return true;
+  }
+};
+
+struct AnyEntrySubTree {
+  std::pair<bool, bool>
+  operator()(const Entry& entry)
+  {
+    return std::make_pair(true, true);
+  }
+};
+
+} // namespace name_tree
+
+/**
+ * \brief Class Name Tree
+ */
+class NameTree : noncopyable
+{
+public:
+  class const_iterator;
+
+  explicit
+  NameTree(size_t nBuckets = 1024);
+
+  ~NameTree();
+
+public: // information
+  /**
+   * \brief Get the number of occupied entries in the Name Tree
+   */
+  size_t
+  size() const;
+
+  /**
+   * \brief Get the number of buckets in the Name Tree (NPHT)
+   * \details The number of buckets is the one that used to create the hash
+   * table, i.e., m_nBuckets.
+   */
+  size_t
+  getNBuckets() const;
+
+  /**
+   * \brief Dump all the information stored in the Name Tree for debugging.
+   */
+  void
+  dump(std::ostream& output) const;
+
+public: // mutation
+  /**
+   * \brief Look for the Name Tree Entry that contains this name prefix.
+   * \details Starts from the shortest name prefix, and then increase the
+   * number of name components by one each time. All non-existing Name Tree
+   * Entries will be created.
+   * \param prefix The querying name prefix.
+   * \return The pointer to the Name Tree Entry that contains this full name
+   * prefix.
+   * \note Existing iterators are unaffected.
+   */
+  shared_ptr<name_tree::Entry>
+  lookup(const Name& prefix);
+
+  /**
+   * \brief Delete a Name Tree Entry if this entry is empty.
+   * \param entry The entry to be deleted if empty.
+   * \note This function must be called after a table entry is detached from Name Tree
+   *       entry. The function deletes a Name Tree entry if nothing is attached to it and
+   *       it has no children, then repeats the same process on its ancestors.
+   * \note Existing iterators, except those pointing to deleted entries, are unaffected.
+   */
+  bool
+  eraseEntryIfEmpty(shared_ptr<name_tree::Entry> entry);
+
+public: // shortcut access
+  /// get NameTree entry from attached FIB entry
+  shared_ptr<name_tree::Entry>
+  get(const fib::Entry& fibEntry) const;
+
+  /// get NameTree entry from attached PIT entry
+  shared_ptr<name_tree::Entry>
+  get(const pit::Entry& pitEntry) const;
+
+  /// get NameTree entry from attached Measurements entry
+  shared_ptr<name_tree::Entry>
+  get(const measurements::Entry& measurementsEntry) const;
+
+  /// get NameTree entry from attached StrategyChoice entry
+  shared_ptr<name_tree::Entry>
+  get(const strategy_choice::Entry& strategyChoiceEntry) const;
+
+public: // matching
+  /**
+   * \brief Exact match lookup for the given name prefix.
+   * \return a null shared_ptr if this prefix is not found;
+   * otherwise return the Name Tree Entry address
+   */
+  shared_ptr<name_tree::Entry>
+  findExactMatch(const Name& prefix) const;
+
+  /**
+   * \brief Longest prefix matching for the given name
+   * \details Starts from the full name string, reduce the number of name component
+   * by one each time, until an Entry is found.
+   */
+  shared_ptr<name_tree::Entry>
+  findLongestPrefixMatch(const Name& prefix,
+                         const name_tree::EntrySelector& entrySelector =
+                         name_tree::AnyEntry()) const;
+
+  shared_ptr<name_tree::Entry>
+  findLongestPrefixMatch(shared_ptr<name_tree::Entry> entry,
+                         const name_tree::EntrySelector& entrySelector =
+                         name_tree::AnyEntry()) const;
+
+  /** \brief Enumerate all the name prefixes that satisfy the prefix and entrySelector
+   *  \return an unspecified type that have .begin() and .end() methods
+   *          and is usable with range-based for
+   *
+   *  Example:
+   *  \code{.cpp}
+   *  auto&& allMatches = nt.findAllMatches(Name("/A/B/C"));
+   *  for (const name_tree::Entry& nte : allMatches) {
+   *    ...
+   *  }
+   *  \endcode
+   *  \note Iteration order is implementation-specific and is undefined
+   *  \note The returned iterator may get invalidated when NameTree is modified
+   */
+  boost::iterator_range<const_iterator>
+  findAllMatches(const Name& prefix,
+                 const name_tree::EntrySelector& entrySelector = name_tree::AnyEntry()) const;
+
+public: // enumeration
+  /** \brief Enumerate all entries, optionally filtered by an EntrySelector.
+   *  \return an unspecified type that have .begin() and .end() methods
+   *          and is usable with range-based for
+   *
+   *  Example:
+   *  \code{.cpp}
+   *  auto&& enumerable = nt.fullEnumerate();
+   *  for (const name_tree::Entry& nte : enumerable) {
+   *    ...
+   *  }
+   *  \endcode
+   *  \note Iteration order is implementation-specific and is undefined
+   *  \note The returned iterator may get invalidated when NameTree is modified
+   */
+  boost::iterator_range<const_iterator>
+  fullEnumerate(const name_tree::EntrySelector& entrySelector = name_tree::AnyEntry()) const;
+
+  /** \brief Enumerate all entries under a prefix, optionally filtered by an EntrySubTreeSelector.
+   *  \return an unspecified type that have .begin() and .end() methods
+   *          and is usable with range-based for
+   *
+   *  Example:
+   *  \code{.cpp}
+   *  auto&& enumerable = nt.partialEnumerate(Name("/A/B/C"));
+   *  for (const name_tree::Entry& nte : enumerable) {
+   *    ...
+   *  }
+   *  \endcode
+   *  \note Iteration order is implementation-specific and is undefined
+   *  \note The returned iterator may get invalidated when NameTree is modified
+   */
+  boost::iterator_range<const_iterator>
+  partialEnumerate(const Name& prefix,
+                   const name_tree::EntrySubTreeSelector& entrySubTreeSelector =
+                         name_tree::AnyEntrySubTree()) const;
+
+  /** \brief Get an iterator pointing to the first NameTree entry
+   *  \note Iteration order is implementation-specific and is undefined
+   *  \note The returned iterator may get invalidated when NameTree is modified
+   */
+  const_iterator
+  begin() const;
+
+  /** \brief Get an iterator referring to the past-the-end FIB entry
+   *  \note Iteration order is implementation-specific and is undefined
+   *  \note The returned iterator may get invalidated when NameTree is modified
+   */
+  const_iterator
+  end() const;
+
+  enum IteratorType {
+    FULL_ENUMERATE_TYPE,
+    PARTIAL_ENUMERATE_TYPE,
+    FIND_ALL_MATCHES_TYPE
+  };
+
+  class const_iterator : public std::iterator<std::forward_iterator_tag, const name_tree::Entry>
+  {
+  public:
+    friend class NameTree;
+
+    const_iterator();
+
+    const_iterator(NameTree::IteratorType type,
+      const NameTree& nameTree,
+      shared_ptr<name_tree::Entry> entry,
+      const name_tree::EntrySelector& entrySelector = name_tree::AnyEntry(),
+      const name_tree::EntrySubTreeSelector& entrySubTreeSelector = name_tree::AnyEntrySubTree());
+
+    ~const_iterator();
+
+    const name_tree::Entry&
+    operator*() const;
+
+    shared_ptr<name_tree::Entry>
+    operator->() const;
+
+    const_iterator
+    operator++();
+
+    const_iterator
+    operator++(int);
+
+    bool
+    operator==(const const_iterator& other) const;
+
+    bool
+    operator!=(const const_iterator& other) const;
+
+  private:
+    const NameTree*                             m_nameTree;
+    shared_ptr<name_tree::Entry>                m_entry;
+    shared_ptr<name_tree::Entry>                m_subTreeRoot;
+    shared_ptr<name_tree::EntrySelector>        m_entrySelector;
+    shared_ptr<name_tree::EntrySubTreeSelector> m_entrySubTreeSelector;
+    NameTree::IteratorType                      m_type;
+    bool                                        m_shouldVisitChildren;
+  };
+
+private:
+  /**
+   * \brief Resize the hash table size when its load factor reaches a threshold.
+   * \details As we are currently using a hand-written hash table implementation
+   * for the Name Tree, the hash table resize() function should be kept in the
+   * name-tree.hpp file.
+   * \param newNBuckets The number of buckets for the new hash table.
+   */
+  void
+  resize(size_t newNBuckets);
+
+private:
+  size_t                        m_nItems;  // Number of items being stored
+  size_t                        m_nBuckets; // Number of hash buckets
+  size_t                        m_minNBuckets; // Minimum number of hash buckets
+  double                        m_enlargeLoadFactor;
+  size_t                        m_enlargeThreshold;
+  int                           m_enlargeFactor;
+  double                        m_shrinkLoadFactor;
+  size_t                        m_shrinkThreshold;
+  double                        m_shrinkFactor;
+  name_tree::Node**             m_buckets; // Name Tree Buckets in the NPHT
+  shared_ptr<name_tree::Entry>  m_end;
+  const_iterator                m_endIterator;
+
+  /**
+   * \brief Create a Name Tree Entry if it does not exist, or return the existing
+   * Name Tree Entry address.
+   * \details Called by lookup() only.
+   * \return The first item is the Name Tree Entry address, the second item is
+   * a bool value indicates whether this is an old entry (false) or a new
+   * entry (true).
+   */
+  std::pair<shared_ptr<name_tree::Entry>, bool>
+  insert(const Name& prefix);
+};
+
+inline NameTree::const_iterator::~const_iterator()
+{
+}
+
+inline size_t
+NameTree::size() const
+{
+  return m_nItems;
+}
+
+inline size_t
+NameTree::getNBuckets() const
+{
+  return m_nBuckets;
+}
+
+inline shared_ptr<name_tree::Entry>
+NameTree::get(const fib::Entry& fibEntry) const
+{
+  return fibEntry.m_nameTreeEntry;
+}
+
+inline shared_ptr<name_tree::Entry>
+NameTree::get(const pit::Entry& pitEntry) const
+{
+  return pitEntry.m_nameTreeEntry;
+}
+
+inline shared_ptr<name_tree::Entry>
+NameTree::get(const measurements::Entry& measurementsEntry) const
+{
+  return measurementsEntry.m_nameTreeEntry;
+}
+
+inline shared_ptr<name_tree::Entry>
+NameTree::get(const strategy_choice::Entry& strategyChoiceEntry) const
+{
+  return strategyChoiceEntry.m_nameTreeEntry;
+}
+
+inline NameTree::const_iterator
+NameTree::begin() const
+{
+  return fullEnumerate().begin();
+}
+
+inline NameTree::const_iterator
+NameTree::end() const
+{
+  return m_endIterator;
+}
+
+inline const name_tree::Entry&
+NameTree::const_iterator::operator*() const
+{
+  return *m_entry;
+}
+
+inline shared_ptr<name_tree::Entry>
+NameTree::const_iterator::operator->() const
+{
+  return m_entry;
+}
+
+inline NameTree::const_iterator
+NameTree::const_iterator::operator++(int)
+{
+  NameTree::const_iterator temp(*this);
+  ++(*this);
+  return temp;
+}
+
+inline bool
+NameTree::const_iterator::operator==(const NameTree::const_iterator& other) const
+{
+  return m_entry == other.m_entry;
+}
+
+inline bool
+NameTree::const_iterator::operator!=(const NameTree::const_iterator& other) const
+{
+  return m_entry != other.m_entry;
+}
+
+} // namespace nfd
+
+#endif // NFD_DAEMON_TABLE_NAME_TREE_HPP
diff --git a/NFD/daemon/table/pit-entry.cpp b/NFD/daemon/table/pit-entry.cpp
new file mode 100644
index 0000000..da7c6ed
--- /dev/null
+++ b/NFD/daemon/table/pit-entry.cpp
@@ -0,0 +1,198 @@
+/* -*- 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/>.
+ */
+
+#include "pit-entry.hpp"
+#include <algorithm>
+
+namespace nfd {
+namespace pit {
+
+const Name Entry::LOCALHOST_NAME("ndn:/localhost");
+const Name Entry::LOCALHOP_NAME("ndn:/localhop");
+
+Entry::Entry(const Interest& interest)
+  : m_interest(interest.shared_from_this())
+{
+}
+
+const Name&
+Entry::getName() const
+{
+  return m_interest->getName();
+}
+
+bool
+Entry::hasLocalInRecord() const
+{
+  return std::any_of(m_inRecords.begin(), m_inRecords.end(),
+                     [] (const InRecord& inRecord) { return inRecord.getFace()->isLocal(); });
+}
+
+bool
+Entry::canForwardTo(const Face& face) const
+{
+  time::steady_clock::TimePoint now = time::steady_clock::now();
+
+  bool hasUnexpiredOutRecord = std::any_of(m_outRecords.begin(), m_outRecords.end(),
+    [&face, &now] (const OutRecord& outRecord) {
+      return outRecord.getFace().get() == &face && outRecord.getExpiry() >= now;
+    });
+  if (hasUnexpiredOutRecord) {
+    return false;
+  }
+
+  bool hasUnexpiredOtherInRecord = std::any_of(m_inRecords.begin(), m_inRecords.end(),
+    [&face, &now] (const InRecord& inRecord) {
+      return inRecord.getFace().get() != &face && inRecord.getExpiry() >= now;
+    });
+  if (!hasUnexpiredOtherInRecord) {
+    return false;
+  }
+
+  return !this->violatesScope(face);
+}
+
+bool
+Entry::violatesScope(const Face& face) const
+{
+  // /localhost scope
+  bool isViolatingLocalhost = !face.isLocal() &&
+                              LOCALHOST_NAME.isPrefixOf(this->getName());
+  if (isViolatingLocalhost) {
+    return true;
+  }
+
+  // /localhop scope
+  bool isViolatingLocalhop = !face.isLocal() &&
+                             LOCALHOP_NAME.isPrefixOf(this->getName()) &&
+                             !this->hasLocalInRecord();
+  if (isViolatingLocalhop) {
+    return true;
+  }
+
+  return false;
+}
+
+int
+Entry::findNonce(uint32_t nonce, const Face& face) const
+{
+  // TODO should we ignore expired in/out records?
+
+  int dnw = DUPLICATE_NONCE_NONE;
+
+  for (const InRecord& inRecord : m_inRecords) {
+    if (inRecord.getLastNonce() == nonce) {
+      if (inRecord.getFace().get() == &face) {
+        dnw |= DUPLICATE_NONCE_IN_SAME;
+      }
+      else {
+        dnw |= DUPLICATE_NONCE_IN_OTHER;
+      }
+    }
+  }
+
+  for (const OutRecord& outRecord : m_outRecords) {
+    if (outRecord.getLastNonce() == nonce) {
+      if (outRecord.getFace().get() == &face) {
+        dnw |= DUPLICATE_NONCE_OUT_SAME;
+      }
+      else {
+        dnw |= DUPLICATE_NONCE_OUT_OTHER;
+      }
+    }
+  }
+
+  return dnw;
+}
+
+InRecordCollection::iterator
+Entry::insertOrUpdateInRecord(shared_ptr<Face> face, const Interest& interest)
+{
+  auto it = std::find_if(m_inRecords.begin(), m_inRecords.end(),
+    [&face] (const InRecord& inRecord) { return inRecord.getFace() == face; });
+  if (it == m_inRecords.end()) {
+    m_inRecords.emplace_front(face);
+    it = m_inRecords.begin();
+  }
+
+  it->update(interest);
+  return it;
+}
+
+InRecordCollection::const_iterator
+Entry::getInRecord(const Face& face) const
+{
+  return std::find_if(m_inRecords.begin(), m_inRecords.end(),
+    [&face] (const InRecord& inRecord) { return inRecord.getFace().get() == &face; });
+}
+
+void
+Entry::deleteInRecords()
+{
+  m_inRecords.clear();
+}
+
+OutRecordCollection::iterator
+Entry::insertOrUpdateOutRecord(shared_ptr<Face> face, const Interest& interest)
+{
+  auto it = std::find_if(m_outRecords.begin(), m_outRecords.end(),
+    [&face] (const OutRecord& outRecord) { return outRecord.getFace() == face; });
+  if (it == m_outRecords.end()) {
+    m_outRecords.emplace_front(face);
+    it = m_outRecords.begin();
+  }
+
+  it->update(interest);
+  return it;
+}
+
+OutRecordCollection::const_iterator
+Entry::getOutRecord(const Face& face) const
+{
+  return std::find_if(m_outRecords.begin(), m_outRecords.end(),
+    [&face] (const OutRecord& outRecord) { return outRecord.getFace().get() == &face; });
+}
+
+void
+Entry::deleteOutRecord(const Face& face)
+{
+  auto it = std::find_if(m_outRecords.begin(), m_outRecords.end(),
+    [&face] (const OutRecord& outRecord) { return outRecord.getFace().get() == &face; });
+  if (it != m_outRecords.end()) {
+    m_outRecords.erase(it);
+  }
+}
+
+bool
+Entry::hasUnexpiredOutRecords() const
+{
+  time::steady_clock::TimePoint now = time::steady_clock::now();
+
+  return std::any_of(m_outRecords.begin(), m_outRecords.end(),
+    [&now] (const OutRecord& outRecord) { return outRecord.getExpiry() >= now; });
+}
+
+} // namespace pit
+} // namespace nfd
diff --git a/NFD/daemon/table/pit-entry.hpp b/NFD/daemon/table/pit-entry.hpp
new file mode 100644
index 0000000..8fcf4a7
--- /dev/null
+++ b/NFD/daemon/table/pit-entry.hpp
@@ -0,0 +1,205 @@
+/* -*- 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_DAEMON_TABLE_PIT_ENTRY_HPP
+#define NFD_DAEMON_TABLE_PIT_ENTRY_HPP
+
+#include "pit-in-record.hpp"
+#include "pit-out-record.hpp"
+#include "core/scheduler.hpp"
+
+namespace nfd {
+
+class NameTree;
+
+namespace name_tree {
+class Entry;
+}
+
+namespace pit {
+
+/** \brief represents an unordered collection of InRecords
+ */
+typedef std::list< InRecord>  InRecordCollection;
+
+/** \brief represents an unordered collection of OutRecords
+ */
+typedef std::list<OutRecord> OutRecordCollection;
+
+/** \brief indicates where duplicate Nonces are found
+ */
+enum DuplicateNonceWhere {
+  DUPLICATE_NONCE_NONE      = 0,
+  /// in-record of same face
+  DUPLICATE_NONCE_IN_SAME   = (1 << 0),
+  /// in-record of other face
+  DUPLICATE_NONCE_IN_OTHER  = (1 << 1),
+  /// out-record of same face
+  DUPLICATE_NONCE_OUT_SAME  = (1 << 2),
+  /// out-record of other face
+  DUPLICATE_NONCE_OUT_OTHER = (1 << 3)
+};
+
+/** \brief represents a PIT entry
+ */
+class Entry : public StrategyInfoHost, noncopyable
+{
+public:
+  explicit
+  Entry(const Interest& interest);
+
+  const Interest&
+  getInterest() const;
+
+  /** \return Interest Name
+   */
+  const Name&
+  getName() const;
+
+  /** \brief decides whether Interest can be forwarded to face
+   *
+   *  \return true if OutRecord of this face does not exist or has expired,
+   *          and there is an InRecord not of this face,
+   *          and scope is not violated
+   */
+  bool
+  canForwardTo(const Face& face) const;
+
+  /** \brief decides whether forwarding Interest to face would violate scope
+   *
+   *  \return true if scope control would be violated
+   *  \note canForwardTo has more comprehensive checks (including scope control)
+   *        and should be used by most strategies. Outgoing Interest pipeline
+   *        should only check scope because some strategy (eg. vehicular) needs
+   *        to retransmit sooner than OutRecord expiry, or forward Interest
+   *        back to incoming face
+   */
+  bool
+  violatesScope(const Face& face) const;
+
+  /** \brief finds where a duplicate Nonce appears
+   *  \return OR'ed DuplicateNonceWhere
+   */
+  int
+  findNonce(uint32_t nonce, const Face& face) const;
+
+public: // InRecord
+  const InRecordCollection&
+  getInRecords() const;
+
+  /** \brief determines whether any InRecord is a local Face
+   *
+   *  \return true if any InRecord is a local Face,
+   *          false if all InRecords are non-local Faces
+   */
+  bool
+  hasLocalInRecord() const;
+
+  /** \brief inserts a InRecord for face, and updates it with interest
+   *
+   *  If InRecord for face exists, the existing one is updated.
+   *  This method does not add the Nonce as a seen Nonce.
+   *  \return an iterator to the InRecord
+   */
+  InRecordCollection::iterator
+  insertOrUpdateInRecord(shared_ptr<Face> face, const Interest& interest);
+
+  /** \brief get the InRecord for face
+   *  \return an iterator to the InRecord, or .end if it does not exist
+   */
+  InRecordCollection::const_iterator
+  getInRecord(const Face& face) const;
+
+  /// deletes all InRecords
+  void
+  deleteInRecords();
+
+public: // OutRecord
+  const OutRecordCollection&
+  getOutRecords() const;
+
+  /** \brief inserts a OutRecord for face, and updates it with interest
+   *
+   *  If OutRecord for face exists, the existing one is updated.
+   *  \return an iterator to the OutRecord
+   */
+  OutRecordCollection::iterator
+  insertOrUpdateOutRecord(shared_ptr<Face> face, const Interest& interest);
+
+  /** \brief get the OutRecord for face
+   *  \return an iterator to the OutRecord, or .end if it does not exist
+   */
+  OutRecordCollection::const_iterator
+  getOutRecord(const Face& face) const;
+
+  /// deletes one OutRecord for face if exists
+  void
+  deleteOutRecord(const Face& face);
+
+  /** \return true if there is one or more unexpired OutRecords
+   */
+  bool
+  hasUnexpiredOutRecords() const;
+
+public:
+  EventId m_unsatisfyTimer;
+  EventId m_stragglerTimer;
+
+private:
+  shared_ptr<const Interest> m_interest;
+  InRecordCollection m_inRecords;
+  OutRecordCollection m_outRecords;
+
+  static const Name LOCALHOST_NAME;
+  static const Name LOCALHOP_NAME;
+
+  shared_ptr<name_tree::Entry> m_nameTreeEntry;
+
+  friend class nfd::NameTree;
+  friend class nfd::name_tree::Entry;
+};
+
+inline const Interest&
+Entry::getInterest() const
+{
+  return *m_interest;
+}
+
+inline const InRecordCollection&
+Entry::getInRecords() const
+{
+  return m_inRecords;
+}
+
+inline const OutRecordCollection&
+Entry::getOutRecords() const
+{
+  return m_outRecords;
+}
+
+} // namespace pit
+} // namespace nfd
+
+#endif // NFD_DAEMON_TABLE_PIT_ENTRY_HPP
diff --git a/NFD/daemon/table/pit-face-record.cpp b/NFD/daemon/table/pit-face-record.cpp
new file mode 100644
index 0000000..c62a1b5
--- /dev/null
+++ b/NFD/daemon/table/pit-face-record.cpp
@@ -0,0 +1,54 @@
+/* -*- 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/>.
+ */
+
+#include "pit-face-record.hpp"
+
+namespace nfd {
+namespace pit {
+
+FaceRecord::FaceRecord(shared_ptr<Face> face)
+  : m_face(face)
+  , m_lastNonce(0)
+  , m_lastRenewed(time::steady_clock::TimePoint::min())
+  , m_expiry(time::steady_clock::TimePoint::min())
+{
+}
+
+void
+FaceRecord::update(const Interest& interest)
+{
+  m_lastNonce = interest.getNonce();
+  m_lastRenewed = time::steady_clock::now();
+
+  time::milliseconds lifetime = interest.getInterestLifetime();
+  if (lifetime < time::milliseconds::zero()) {
+    lifetime = ndn::DEFAULT_INTEREST_LIFETIME;
+  }
+  m_expiry = m_lastRenewed + lifetime;
+}
+
+
+} // namespace pit
+} // namespace nfd
diff --git a/NFD/daemon/table/pit-face-record.hpp b/NFD/daemon/table/pit-face-record.hpp
new file mode 100644
index 0000000..c1d6c77
--- /dev/null
+++ b/NFD/daemon/table/pit-face-record.hpp
@@ -0,0 +1,100 @@
+/* -*- 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_DAEMON_TABLE_PIT_FACE_RECORD_HPP
+#define NFD_DAEMON_TABLE_PIT_FACE_RECORD_HPP
+
+#include "face/face.hpp"
+#include "strategy-info-host.hpp"
+
+namespace nfd {
+namespace pit {
+
+/** \brief contains information about an Interest
+ *         on an incoming or outgoing face
+ *  \note This is an implementation detail to extract common functionality
+ *        of InRecord and OutRecord
+ */
+class FaceRecord : public StrategyInfoHost
+{
+public:
+  explicit
+  FaceRecord(shared_ptr<Face> face);
+
+  shared_ptr<Face>
+  getFace() const;
+
+  uint32_t
+  getLastNonce() const;
+
+  time::steady_clock::TimePoint
+  getLastRenewed() const;
+
+  /** \brief gives the time point this record expires
+   *  \return getLastRenewed() + InterestLifetime
+   */
+  time::steady_clock::TimePoint
+  getExpiry() const;
+
+  /** \brief updates lastNonce, lastRenewed, expiry fields
+   */
+  void
+  update(const Interest& interest);
+
+private:
+  shared_ptr<Face> m_face;
+  uint32_t m_lastNonce;
+  time::steady_clock::TimePoint m_lastRenewed;
+  time::steady_clock::TimePoint m_expiry;
+};
+
+inline shared_ptr<Face>
+FaceRecord::getFace() const
+{
+  return m_face;
+}
+
+inline uint32_t
+FaceRecord::getLastNonce() const
+{
+  return m_lastNonce;
+}
+
+inline time::steady_clock::TimePoint
+FaceRecord::getLastRenewed() const
+{
+  return m_lastRenewed;
+}
+
+inline time::steady_clock::TimePoint
+FaceRecord::getExpiry() const
+{
+  return m_expiry;
+}
+
+} // namespace pit
+} // namespace nfd
+
+#endif // NFD_DAEMON_TABLE_PIT_FACE_RECORD_HPP
diff --git a/NFD/daemon/table/pit-in-record.cpp b/NFD/daemon/table/pit-in-record.cpp
new file mode 100644
index 0000000..b7f0edf
--- /dev/null
+++ b/NFD/daemon/table/pit-in-record.cpp
@@ -0,0 +1,43 @@
+/* -*- 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
+ *
+ * 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 "pit-in-record.hpp"
+
+namespace nfd {
+namespace pit {
+
+InRecord::InRecord(shared_ptr<Face> face)
+  : FaceRecord(face)
+{
+}
+
+void
+InRecord::update(const Interest& interest)
+{
+  this->FaceRecord::update(interest);
+  m_interest = const_cast<Interest&>(interest).shared_from_this();
+}
+
+} // namespace pit
+} // namespace nfd
diff --git a/NFD/daemon/table/pit-in-record.hpp b/NFD/daemon/table/pit-in-record.hpp
new file mode 100644
index 0000000..da56d1f
--- /dev/null
+++ b/NFD/daemon/table/pit-in-record.hpp
@@ -0,0 +1,63 @@
+/* -*- 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_DAEMON_TABLE_PIT_IN_RECORD_HPP
+#define NFD_DAEMON_TABLE_PIT_IN_RECORD_HPP
+
+#include "pit-face-record.hpp"
+
+namespace nfd {
+namespace pit {
+
+/** \class InRecord
+ *  \brief contains information about an Interest from an incoming face
+ */
+class InRecord : public FaceRecord
+{
+public:
+  explicit
+  InRecord(shared_ptr<Face> face);
+
+  void
+  update(const Interest& interest);
+
+  const Interest&
+  getInterest() const;
+
+private:
+  shared_ptr<const Interest> m_interest;
+};
+
+inline const Interest&
+InRecord::getInterest() const
+{
+  BOOST_ASSERT(static_cast<bool>(m_interest));
+  return *m_interest;
+}
+
+} // namespace pit
+} // namespace nfd
+
+#endif // NFD_DAEMON_TABLE_PIT_IN_RECORD_HPP
diff --git a/NFD/daemon/table/pit-out-record.cpp b/NFD/daemon/table/pit-out-record.cpp
new file mode 100644
index 0000000..268b67b
--- /dev/null
+++ b/NFD/daemon/table/pit-out-record.cpp
@@ -0,0 +1,36 @@
+/* -*- 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
+ *
+ * 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 "pit-out-record.hpp"
+
+namespace nfd {
+namespace pit {
+
+OutRecord::OutRecord(shared_ptr<Face> face)
+  : FaceRecord(face)
+{
+}
+
+} // namespace pit
+} // namespace nfd
diff --git a/NFD/daemon/table/pit-out-record.hpp b/NFD/daemon/table/pit-out-record.hpp
new file mode 100644
index 0000000..c75e005
--- /dev/null
+++ b/NFD/daemon/table/pit-out-record.hpp
@@ -0,0 +1,46 @@
+/* -*- 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
+ *
+ * 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_TABLE_PIT_OUT_RECORD_HPP
+#define NFD_DAEMON_TABLE_PIT_OUT_RECORD_HPP
+
+#include "pit-face-record.hpp"
+
+namespace nfd {
+namespace pit {
+
+/** \class OutRecord
+ *  \brief contains information about an Interest toward an outgoing face
+ */
+class OutRecord : public FaceRecord
+{
+public:
+  explicit
+  OutRecord(shared_ptr<Face> face);
+};
+
+} // namespace pit
+} // namespace nfd
+
+#endif // NFD_DAEMON_TABLE_PIT_IN_RECORD_HPP
diff --git a/NFD/daemon/table/pit.cpp b/NFD/daemon/table/pit.cpp
new file mode 100644
index 0000000..5e333aa
--- /dev/null
+++ b/NFD/daemon/table/pit.cpp
@@ -0,0 +1,126 @@
+/* -*- 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/>.
+ */
+
+#include "pit.hpp"
+#include <type_traits>
+
+#include <boost/concept/assert.hpp>
+#include <boost/concept_check.hpp>
+#include <type_traits>
+
+namespace nfd {
+namespace pit {
+
+#if HAVE_IS_MOVE_CONSTRUCTIBLE
+static_assert(std::is_move_constructible<DataMatchResult>::value,
+              "DataMatchResult must be MoveConstructible");
+#endif // HAVE_IS_MOVE_CONSTRUCTIBLE
+
+} // namespace pit
+
+// http://en.cppreference.com/w/cpp/concept/ForwardIterator
+BOOST_CONCEPT_ASSERT((boost::ForwardIterator<Pit::const_iterator>));
+// boost::ForwardIterator follows SGI standard http://www.sgi.com/tech/stl/ForwardIterator.html,
+// which doesn't require DefaultConstructible
+#ifdef HAVE_IS_DEFAULT_CONSTRUCTIBLE
+static_assert(std::is_default_constructible<Pit::const_iterator>::value,
+              "Pit::const_iterator must be default-constructible");
+#else
+BOOST_CONCEPT_ASSERT((boost::DefaultConstructible<Pit::const_iterator>));
+#endif // HAVE_IS_DEFAULT_CONSTRUCTIBLE
+
+Pit::Pit(NameTree& nameTree)
+  : m_nameTree(nameTree)
+  , m_nItems(0)
+{
+}
+
+Pit::~Pit()
+{
+}
+
+std::pair<shared_ptr<pit::Entry>, bool>
+Pit::insert(const Interest& interest)
+{
+  // first lookup() the Interest Name in the NameTree, which will creates all
+  // the intermedia nodes, starting from the shortest prefix.
+  shared_ptr<name_tree::Entry> nameTreeEntry = m_nameTree.lookup(interest.getName());
+  BOOST_ASSERT(static_cast<bool>(nameTreeEntry));
+
+  const std::vector<shared_ptr<pit::Entry>>& pitEntries = nameTreeEntry->getPitEntries();
+
+  // then check if this Interest is already in the PIT entries
+  auto it = std::find_if(pitEntries.begin(), pitEntries.end(),
+                         [&interest] (const shared_ptr<pit::Entry>& entry) {
+                           return entry->getInterest().getName() == interest.getName() &&
+                                  entry->getInterest().getSelectors() == interest.getSelectors();
+                         });
+  if (it != pitEntries.end()) {
+    return { *it, false };
+  }
+
+  shared_ptr<pit::Entry> entry = make_shared<pit::Entry>(interest);
+  nameTreeEntry->insertPitEntry(entry);
+  m_nItems++;
+  return { entry, true };
+}
+
+pit::DataMatchResult
+Pit::findAllDataMatches(const Data& data) const
+{
+  auto&& ntMatches = m_nameTree.findAllMatches(data.getName(),
+    [] (const name_tree::Entry& entry) { return entry.hasPitEntries(); });
+
+  pit::DataMatchResult matches;
+  for (const name_tree::Entry& nte : ntMatches) {
+    for (const shared_ptr<pit::Entry>& pitEntry : nte.getPitEntries()) {
+      if (pitEntry->getInterest().matchesData(data))
+        matches.emplace_back(pitEntry);
+    }
+  }
+
+  return matches;
+}
+
+void
+Pit::erase(shared_ptr<pit::Entry> pitEntry)
+{
+  shared_ptr<name_tree::Entry> nameTreeEntry = m_nameTree.get(*pitEntry);
+  BOOST_ASSERT(static_cast<bool>(nameTreeEntry));
+
+  nameTreeEntry->erasePitEntry(pitEntry);
+  m_nameTree.eraseEntryIfEmpty(nameTreeEntry);
+
+  --m_nItems;
+}
+
+Pit::const_iterator
+Pit::begin() const
+{
+  return const_iterator(m_nameTree.fullEnumerate(
+    [] (const name_tree::Entry& entry) { return entry.hasPitEntries(); }).begin());
+}
+
+} // namespace nfd
diff --git a/NFD/daemon/table/pit.hpp b/NFD/daemon/table/pit.hpp
new file mode 100644
index 0000000..3bdf44f
--- /dev/null
+++ b/NFD/daemon/table/pit.hpp
@@ -0,0 +1,220 @@
+/* -*- 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_DAEMON_TABLE_PIT_HPP
+#define NFD_DAEMON_TABLE_PIT_HPP
+
+#include "name-tree.hpp"
+#include "pit-entry.hpp"
+
+namespace nfd {
+namespace pit {
+
+/** \class DataMatchResult
+ *  \brief an unordered iterable of all PIT entries matching Data
+ *
+ *  This type shall support:
+ *    iterator<shared_ptr<pit::Entry>> begin()
+ *    iterator<shared_ptr<pit::Entry>> end()
+ */
+typedef std::vector<shared_ptr<pit::Entry>> DataMatchResult;
+
+} // namespace pit
+
+/** \brief represents the Interest Table
+ */
+class Pit : noncopyable
+{
+public:
+  explicit
+  Pit(NameTree& nameTree);
+
+  ~Pit();
+
+  /** \return number of entries
+   */
+  size_t
+  size() const;
+
+  /** \brief inserts a PIT entry for Interest
+   *
+   *  If an entry for exact same name and selectors exists, that entry is returned.
+   *  \return the entry, and true for new entry, false for existing entry
+   */
+  std::pair<shared_ptr<pit::Entry>, bool>
+  insert(const Interest& interest);
+
+  /** \brief performs a Data match
+   *  \return an iterable of all PIT entries matching data
+   */
+  pit::DataMatchResult
+  findAllDataMatches(const Data& data) const;
+
+  /**
+   *  \brief erases a PIT Entry
+   */
+  void
+  erase(shared_ptr<pit::Entry> pitEntry);
+
+public: // enumeration
+  class const_iterator;
+
+  /** \brief returns an iterator pointing to the first PIT entry
+   *  \note Iteration order is implementation-specific and is undefined
+   *  \note The returned iterator may get invalidated if PIT or another NameTree-based
+   *        table is modified
+   */
+  const_iterator
+  begin() const;
+
+  /** \brief returns an iterator referring to the past-the-end PIT entry
+   *  \note The returned iterator may get invalidated if PIT or another NameTree-based
+   *        table is modified
+   */
+  const_iterator
+  end() const;
+
+  class const_iterator : public std::iterator<std::forward_iterator_tag, const pit::Entry>
+  {
+  public:
+    const_iterator();
+
+    explicit
+    const_iterator(const NameTree::const_iterator& it);
+
+    ~const_iterator();
+
+    const pit::Entry&
+    operator*() const;
+
+    shared_ptr<pit::Entry>
+    operator->() const;
+
+    const_iterator&
+    operator++();
+
+    const_iterator
+    operator++(int);
+
+    bool
+    operator==(const const_iterator& other) const;
+
+    bool
+    operator!=(const const_iterator& other) const;
+
+  private:
+    NameTree::const_iterator m_nameTreeIterator;
+    /** \brief Index of the current visiting PIT entry in NameTree node
+     *
+     * Index is used to ensure that dereferencing of m_nameTreeIterator happens only when
+     * const_iterator is dereferenced or advanced.
+     */
+    size_t m_iPitEntry;
+  };
+
+private:
+  NameTree& m_nameTree;
+  size_t m_nItems;
+};
+
+inline size_t
+Pit::size() const
+{
+  return m_nItems;
+}
+
+inline Pit::const_iterator
+Pit::end() const
+{
+  return const_iterator(m_nameTree.end());
+}
+
+inline
+Pit::const_iterator::const_iterator()
+  : m_iPitEntry(0)
+{
+}
+
+inline
+Pit::const_iterator::const_iterator(const NameTree::const_iterator& it)
+  : m_nameTreeIterator(it)
+  , m_iPitEntry(0)
+{
+}
+
+inline
+Pit::const_iterator::~const_iterator()
+{
+}
+
+inline Pit::const_iterator
+Pit::const_iterator::operator++(int)
+{
+  Pit::const_iterator temp(*this);
+  ++(*this);
+  return temp;
+}
+
+inline Pit::const_iterator&
+Pit::const_iterator::operator++()
+{
+  ++m_iPitEntry;
+  if (m_iPitEntry < m_nameTreeIterator->getPitEntries().size()) {
+    return *this;
+  }
+
+  ++m_nameTreeIterator;
+  m_iPitEntry = 0;
+  return *this;
+}
+
+inline const pit::Entry&
+Pit::const_iterator::operator*() const
+{
+  return *(this->operator->());
+}
+
+inline shared_ptr<pit::Entry>
+Pit::const_iterator::operator->() const
+{
+  return m_nameTreeIterator->getPitEntries().at(m_iPitEntry);
+}
+
+inline bool
+Pit::const_iterator::operator==(const Pit::const_iterator& other) const
+{
+  return m_nameTreeIterator == other.m_nameTreeIterator &&
+         m_iPitEntry == other.m_iPitEntry;
+}
+
+inline bool
+Pit::const_iterator::operator!=(const Pit::const_iterator& other) const
+{
+  return !(*this == other);
+}
+
+} // namespace nfd
+
+#endif // NFD_DAEMON_TABLE_PIT_HPP
diff --git a/NFD/daemon/table/strategy-choice-entry.cpp b/NFD/daemon/table/strategy-choice-entry.cpp
new file mode 100644
index 0000000..2b7a257
--- /dev/null
+++ b/NFD/daemon/table/strategy-choice-entry.cpp
@@ -0,0 +1,46 @@
+/* -*- 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/>.
+ **/
+
+#include "strategy-choice-entry.hpp"
+#include "core/logger.hpp"
+#include "fw/strategy.hpp"
+
+namespace nfd {
+namespace strategy_choice {
+
+Entry::Entry(const Name& prefix)
+  : m_prefix(prefix)
+  , m_strategy(nullptr)
+{
+}
+
+const Name&
+Entry::getStrategyName() const
+{
+  return m_strategy->getName();
+}
+
+} // namespace strategy_choice
+} // namespace nfd
diff --git a/NFD/daemon/table/strategy-choice-entry.hpp b/NFD/daemon/table/strategy-choice-entry.hpp
new file mode 100644
index 0000000..857a313
--- /dev/null
+++ b/NFD/daemon/table/strategy-choice-entry.hpp
@@ -0,0 +1,94 @@
+/* -*- 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_DAEMON_TABLE_STRATEGY_CHOICE_ENTRY_HPP
+#define NFD_DAEMON_TABLE_STRATEGY_CHOICE_ENTRY_HPP
+
+#include "common.hpp"
+
+namespace nfd {
+
+class NameTree;
+namespace name_tree {
+class Entry;
+}
+namespace fw {
+class Strategy;
+}
+
+namespace strategy_choice {
+
+/** \brief represents a Strategy Choice entry
+ */
+class Entry : noncopyable
+{
+public:
+  Entry(const Name& prefix);
+
+  const Name&
+  getPrefix() const;
+
+  const Name&
+  getStrategyName() const;
+
+  fw::Strategy&
+  getStrategy() const;
+
+  void
+  setStrategy(fw::Strategy& strategy);
+
+private:
+  Name m_prefix;
+  fw::Strategy* m_strategy;
+
+  shared_ptr<name_tree::Entry> m_nameTreeEntry;
+  friend class nfd::NameTree;
+  friend class nfd::name_tree::Entry;
+};
+
+
+inline const Name&
+Entry::getPrefix() const
+{
+  return m_prefix;
+}
+
+inline fw::Strategy&
+Entry::getStrategy() const
+{
+  BOOST_ASSERT(m_strategy != nullptr);
+  return *m_strategy;
+}
+
+inline void
+Entry::setStrategy(fw::Strategy& strategy)
+{
+  m_strategy = &strategy;
+}
+
+} // namespace strategy_choice
+} // namespace nfd
+
+#endif // NFD_DAEMON_TABLE_STRATEGY_CHOICE_ENTRY_HPP
diff --git a/NFD/daemon/table/strategy-choice.cpp b/NFD/daemon/table/strategy-choice.cpp
new file mode 100644
index 0000000..85aade6
--- /dev/null
+++ b/NFD/daemon/table/strategy-choice.cpp
@@ -0,0 +1,287 @@
+/* -*- 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/>.
+ */
+
+#include "strategy-choice.hpp"
+#include "core/logger.hpp"
+#include "fw/strategy.hpp"
+#include "pit-entry.hpp"
+#include "measurements-entry.hpp"
+
+namespace nfd {
+
+using strategy_choice::Entry;
+using fw::Strategy;
+
+NFD_LOG_INIT("StrategyChoice");
+
+StrategyChoice::StrategyChoice(NameTree& nameTree, shared_ptr<Strategy> defaultStrategy)
+  : m_nameTree(nameTree)
+  , m_nItems(0)
+{
+  this->setDefaultStrategy(defaultStrategy);
+}
+
+bool
+StrategyChoice::hasStrategy(const Name& strategyName, bool isExact) const
+{
+  if (isExact) {
+    return m_strategyInstances.count(strategyName) > 0;
+  }
+  else {
+    return static_cast<bool>(this->getStrategy(strategyName));
+  }
+}
+
+bool
+StrategyChoice::install(shared_ptr<Strategy> strategy)
+{
+  BOOST_ASSERT(static_cast<bool>(strategy));
+  const Name& strategyName = strategy->getName();
+
+  if (this->hasStrategy(strategyName, true)) {
+    NFD_LOG_ERROR("install(" << strategyName << ") duplicate strategyName");
+    return false;
+  }
+
+  m_strategyInstances[strategyName] = strategy;
+  return true;
+}
+
+fw::Strategy*
+StrategyChoice::getStrategy(const Name& strategyName) const
+{
+  fw::Strategy* candidate = nullptr;
+  for (auto it = m_strategyInstances.lower_bound(strategyName);
+       it != m_strategyInstances.end() && strategyName.isPrefixOf(it->first); ++it) {
+    switch (it->first.size() - strategyName.size()) {
+    case 0: // exact match
+      return it->second.get();
+    case 1: // unversioned strategyName matches versioned strategy
+      candidate = it->second.get();
+      break;
+    }
+  }
+  return candidate;
+}
+
+bool
+StrategyChoice::insert(const Name& prefix, const Name& strategyName)
+{
+  Strategy* strategy = this->getStrategy(strategyName);
+  if (strategy == nullptr) {
+    NFD_LOG_ERROR("insert(" << prefix << "," << strategyName << ") strategy not installed");
+    return false;
+  }
+
+  shared_ptr<name_tree::Entry> nte = m_nameTree.lookup(prefix);
+  shared_ptr<Entry> entry = nte->getStrategyChoiceEntry();
+  Strategy* oldStrategy = nullptr;
+  if (static_cast<bool>(entry)) {
+    if (entry->getStrategy().getName() == strategy->getName()) {
+      NFD_LOG_TRACE("insert(" << prefix << ") not changing " << strategy->getName());
+      return true;
+    }
+    oldStrategy = &entry->getStrategy();
+    NFD_LOG_TRACE("insert(" << prefix << ") changing from " << oldStrategy->getName() <<
+                  " to " << strategy->getName());
+  }
+
+  if (!static_cast<bool>(entry)) {
+    oldStrategy = &this->findEffectiveStrategy(prefix);
+    entry = make_shared<Entry>(prefix);
+    nte->setStrategyChoiceEntry(entry);
+    ++m_nItems;
+    NFD_LOG_TRACE("insert(" << prefix << ") new entry " << strategy->getName());
+  }
+
+  this->changeStrategy(*entry, *oldStrategy, *strategy);
+  entry->setStrategy(*strategy);
+  return true;
+}
+
+void
+StrategyChoice::erase(const Name& prefix)
+{
+  BOOST_ASSERT(prefix.size() > 0);
+
+  shared_ptr<name_tree::Entry> nte = m_nameTree.findExactMatch(prefix);
+  if (!static_cast<bool>(nte)) {
+    return;
+  }
+
+  shared_ptr<Entry> entry = nte->getStrategyChoiceEntry();
+  if (!static_cast<bool>(entry)) {
+    return;
+  }
+
+  Strategy& oldStrategy = entry->getStrategy();
+
+  Strategy& parentStrategy = this->findEffectiveStrategy(prefix.getPrefix(-1));
+  this->changeStrategy(*entry, oldStrategy, parentStrategy);
+
+  nte->setStrategyChoiceEntry(shared_ptr<Entry>());
+  m_nameTree.eraseEntryIfEmpty(nte);
+  --m_nItems;
+}
+
+std::pair<bool, Name>
+StrategyChoice::get(const Name& prefix) const
+{
+  shared_ptr<name_tree::Entry> nte = m_nameTree.findExactMatch(prefix);
+  if (!static_cast<bool>(nte)) {
+    return { false, Name() };
+  }
+
+  shared_ptr<Entry> entry = nte->getStrategyChoiceEntry();
+  if (!static_cast<bool>(entry)) {
+    return { false, Name() };
+  }
+
+  return { true, entry->getStrategy().getName() };
+}
+
+Strategy&
+StrategyChoice::findEffectiveStrategy(const Name& prefix) const
+{
+  shared_ptr<name_tree::Entry> nte = m_nameTree.findLongestPrefixMatch(prefix,
+    [] (const name_tree::Entry& entry) {
+      return static_cast<bool>(entry.getStrategyChoiceEntry());
+    });
+
+  BOOST_ASSERT(static_cast<bool>(nte));
+  return nte->getStrategyChoiceEntry()->getStrategy();
+}
+
+Strategy&
+StrategyChoice::findEffectiveStrategy(shared_ptr<name_tree::Entry> nte) const
+{
+  shared_ptr<strategy_choice::Entry> entry = nte->getStrategyChoiceEntry();
+  if (static_cast<bool>(entry))
+    return entry->getStrategy();
+
+  nte = m_nameTree.findLongestPrefixMatch(nte,
+    [] (const name_tree::Entry& entry) {
+      return static_cast<bool>(entry.getStrategyChoiceEntry());
+    });
+
+  BOOST_ASSERT(static_cast<bool>(nte));
+  return nte->getStrategyChoiceEntry()->getStrategy();
+}
+
+Strategy&
+StrategyChoice::findEffectiveStrategy(const pit::Entry& pitEntry) const
+{
+  shared_ptr<name_tree::Entry> nte = m_nameTree.get(pitEntry);
+
+  BOOST_ASSERT(static_cast<bool>(nte));
+  return this->findEffectiveStrategy(nte);
+}
+
+Strategy&
+StrategyChoice::findEffectiveStrategy(const measurements::Entry& measurementsEntry) const
+{
+  shared_ptr<name_tree::Entry> nte = m_nameTree.get(measurementsEntry);
+
+  BOOST_ASSERT(static_cast<bool>(nte));
+  return this->findEffectiveStrategy(nte);
+}
+
+void
+StrategyChoice::setDefaultStrategy(shared_ptr<Strategy> strategy)
+{
+  this->install(strategy);
+
+  // don't use .insert here, because it will invoke findEffectiveStrategy
+  // which expects an existing root entry
+  shared_ptr<name_tree::Entry> nte = m_nameTree.lookup(Name());
+  shared_ptr<Entry> entry = make_shared<Entry>(Name());
+  nte->setStrategyChoiceEntry(entry);
+  ++m_nItems;
+  NFD_LOG_INFO("setDefaultStrategy " << strategy->getName());
+
+  entry->setStrategy(*strategy);
+}
+
+static inline void
+clearStrategyInfo(const name_tree::Entry& nte)
+{
+  NFD_LOG_TRACE("clearStrategyInfo " << nte.getPrefix());
+
+  for (const shared_ptr<pit::Entry>& pitEntry : nte.getPitEntries()) {
+    pitEntry->clearStrategyInfo();
+    for (const pit::InRecord& inRecord : pitEntry->getInRecords()) {
+      const_cast<pit::InRecord&>(inRecord).clearStrategyInfo();
+    }
+    for (const pit::OutRecord& outRecord : pitEntry->getOutRecords()) {
+      const_cast<pit::OutRecord&>(outRecord).clearStrategyInfo();
+    }
+  }
+  if (static_cast<bool>(nte.getMeasurementsEntry())) {
+    nte.getMeasurementsEntry()->clearStrategyInfo();
+  }
+}
+
+void
+StrategyChoice::changeStrategy(strategy_choice::Entry& entry,
+                               fw::Strategy& oldStrategy,
+                               fw::Strategy& newStrategy)
+{
+  if (&oldStrategy == &newStrategy) {
+    return;
+  }
+
+  NFD_LOG_INFO("changeStrategy(" << entry.getPrefix() << ")"
+               << " from " << oldStrategy.getName()
+               << " to " << newStrategy.getName());
+
+  // reset StrategyInfo on a portion of NameTree,
+  // where entry's effective strategy is covered by the changing StrategyChoice entry
+  const name_tree::Entry* rootNte = m_nameTree.get(entry).get();
+  auto&& ntChanged = m_nameTree.partialEnumerate(entry.getPrefix(),
+    [&rootNte] (const name_tree::Entry& nte) -> std::pair<bool, bool> {
+      if (&nte == rootNte) {
+        return {true, true};
+      }
+      if (static_cast<bool>(nte.getStrategyChoiceEntry())) {
+        return {false, false};
+      }
+      return {true, true};
+    });
+  for (const name_tree::Entry& nte : ntChanged) {
+    clearStrategyInfo(nte);
+  }
+}
+
+StrategyChoice::const_iterator
+StrategyChoice::begin() const
+{
+  auto&& enumerable = m_nameTree.fullEnumerate(
+    [] (const name_tree::Entry& entry) {
+      return static_cast<bool>(entry.getStrategyChoiceEntry());
+    });
+  return const_iterator(enumerable.begin());
+}
+
+} // namespace nfd
diff --git a/NFD/daemon/table/strategy-choice.hpp b/NFD/daemon/table/strategy-choice.hpp
new file mode 100644
index 0000000..fdff3ab
--- /dev/null
+++ b/NFD/daemon/table/strategy-choice.hpp
@@ -0,0 +1,237 @@
+/* -*- 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_DAEMON_TABLE_STRATEGY_CHOICE_HPP
+#define NFD_DAEMON_TABLE_STRATEGY_CHOICE_HPP
+
+#include "strategy-choice-entry.hpp"
+#include "name-tree.hpp"
+
+namespace nfd {
+
+/** \brief represents the Strategy Choice table
+ *
+ *  The Strategy Choice table maintains available Strategy types,
+ *  and associates Name prefixes with Strategy types.
+ *
+ *  Each strategy is identified by a strategyName.
+ *  It's recommended to include a version number as the last component of strategyName.
+ *
+ *  A Name prefix is owned by a strategy if a longest prefix match on the
+ *  Strategy Choice table returns that strategy.
+ */
+class StrategyChoice : noncopyable
+{
+public:
+  StrategyChoice(NameTree& nameTree, shared_ptr<fw::Strategy> defaultStrategy);
+
+public: // available Strategy types
+  /** \brief determines if a strategy is installed
+   *  \param isExact true to require exact match, false to permit unversioned strategyName
+   *  \return true if strategy is installed
+   */
+  bool
+  hasStrategy(const Name& strategyName, bool isExact = false) const;
+
+  /** \brief install a strategy
+   *  \return true if installed; false if not installed due to duplicate strategyName
+   *  \note shared_ptr is passed by value because StrategyChoice takes ownership of strategy
+   */
+  bool
+  install(shared_ptr<fw::Strategy> strategy);
+
+public: // Strategy Choice table
+  /** \brief set strategy of prefix to be strategyName
+   *  \param strategyName the strategy to be used
+   *  \return true on success
+   *
+   *  This method set a strategy onto a Name prefix.
+   *  The strategy must have been installed.
+   *  The strategyName can either be exact (contains version component),
+   *  or omit the version component to pick the latest version.
+   */
+  bool
+  insert(const Name& prefix, const Name& strategyName);
+
+  /** \brief make prefix to inherit strategy from its parent
+   *
+   *  not allowed for root prefix (ndn:/)
+   */
+  void
+  erase(const Name& prefix);
+
+  /** \brief get strategy Name of prefix
+   *  \return true and strategyName at exact match, or false
+   */
+  std::pair<bool, Name>
+  get(const Name& prefix) const;
+
+public: // effective strategy
+  /// get effective strategy for prefix
+  fw::Strategy&
+  findEffectiveStrategy(const Name& prefix) const;
+
+  /// get effective strategy for pitEntry
+  fw::Strategy&
+  findEffectiveStrategy(const pit::Entry& pitEntry) const;
+
+  /// get effective strategy for measurementsEntry
+  fw::Strategy&
+  findEffectiveStrategy(const measurements::Entry& measurementsEntry) const;
+
+public: // enumeration
+  class const_iterator
+    : public std::iterator<std::forward_iterator_tag, const strategy_choice::Entry>
+  {
+  public:
+    explicit
+    const_iterator(const NameTree::const_iterator& it);
+
+    ~const_iterator();
+
+    const strategy_choice::Entry&
+    operator*() const;
+
+    shared_ptr<strategy_choice::Entry>
+    operator->() const;
+
+    const_iterator&
+    operator++();
+
+    const_iterator
+    operator++(int);
+
+    bool
+    operator==(const const_iterator& other) const;
+
+    bool
+    operator!=(const const_iterator& other) const;
+
+  private:
+    NameTree::const_iterator m_nameTreeIterator;
+  };
+
+  /// number of entries stored
+  size_t
+  size() const;
+
+  const_iterator
+  begin() const;
+
+  const_iterator
+  end() const;
+
+private:
+  /** \brief get Strategy instance by strategyName
+   *  \param strategyName a versioned or unversioned strategyName
+   */
+  fw::Strategy*
+  getStrategy(const Name& strategyName) const;
+
+  void
+  setDefaultStrategy(shared_ptr<fw::Strategy> strategy);
+
+  void
+  changeStrategy(strategy_choice::Entry& entry,
+                 fw::Strategy& oldStrategy,
+                 fw::Strategy& newStrategy);
+
+  fw::Strategy&
+  findEffectiveStrategy(shared_ptr<name_tree::Entry> nte) const;
+
+private:
+  NameTree& m_nameTree;
+  size_t m_nItems;
+
+  typedef std::map<Name, shared_ptr<fw::Strategy> > StrategyInstanceTable;
+  StrategyInstanceTable m_strategyInstances;
+};
+
+inline size_t
+StrategyChoice::size() const
+{
+  return m_nItems;
+}
+
+inline StrategyChoice::const_iterator
+StrategyChoice::end() const
+{
+  return const_iterator(m_nameTree.end());
+}
+
+inline
+StrategyChoice::const_iterator::const_iterator(const NameTree::const_iterator& it)
+  : m_nameTreeIterator(it)
+{
+}
+
+inline
+StrategyChoice::const_iterator::~const_iterator()
+{
+}
+
+inline
+StrategyChoice::const_iterator
+StrategyChoice::const_iterator::operator++(int)
+{
+  StrategyChoice::const_iterator temp(*this);
+  ++(*this);
+  return temp;
+}
+
+inline StrategyChoice::const_iterator&
+StrategyChoice::const_iterator::operator++()
+{
+  ++m_nameTreeIterator;
+  return *this;
+}
+
+inline const strategy_choice::Entry&
+StrategyChoice::const_iterator::operator*() const
+{
+  return *(m_nameTreeIterator->getStrategyChoiceEntry());
+}
+
+inline shared_ptr<strategy_choice::Entry>
+StrategyChoice::const_iterator::operator->() const
+{
+  return m_nameTreeIterator->getStrategyChoiceEntry();
+}
+
+inline bool
+StrategyChoice::const_iterator::operator==(const StrategyChoice::const_iterator& other) const
+{
+  return m_nameTreeIterator == other.m_nameTreeIterator;
+}
+
+inline bool
+StrategyChoice::const_iterator::operator!=(const StrategyChoice::const_iterator& other) const
+{
+  return m_nameTreeIterator != other.m_nameTreeIterator;
+}
+
+} // namespace nfd
+
+#endif // NFD_DAEMON_TABLE_STRATEGY_CHOICE_HPP
diff --git a/NFD/daemon/table/strategy-info-host.cpp b/NFD/daemon/table/strategy-info-host.cpp
new file mode 100644
index 0000000..4961c68
--- /dev/null
+++ b/NFD/daemon/table/strategy-info-host.cpp
@@ -0,0 +1,36 @@
+/* -*- 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/>.
+ */
+
+#include "strategy-info-host.hpp"
+
+namespace nfd {
+
+void
+StrategyInfoHost::clearStrategyInfo()
+{
+  m_items.clear();
+}
+
+} // namespace nfd
diff --git a/NFD/daemon/table/strategy-info-host.hpp b/NFD/daemon/table/strategy-info-host.hpp
new file mode 100644
index 0000000..1f6d940
--- /dev/null
+++ b/NFD/daemon/table/strategy-info-host.hpp
@@ -0,0 +1,119 @@
+/* -*- 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_DAEMON_TABLE_STRATEGY_INFO_HOST_HPP
+#define NFD_DAEMON_TABLE_STRATEGY_INFO_HOST_HPP
+
+#include "fw/strategy-info.hpp"
+
+namespace nfd {
+
+/** \brief base class for an entity onto which StrategyInfo objects may be placed
+ */
+class StrategyInfoHost
+{
+public:
+  /** \brief get a StrategyInfo item
+   *  \tparam T type of StrategyInfo, must be a subclass of from nfd::fw::StrategyInfo
+   *  \retval nullptr if no StrategyInfo of type T is stored
+   */
+  template<typename T>
+  shared_ptr<T>
+  getStrategyInfo() const;
+
+  /** \brief set a StrategyInfo item
+   *  \tparam T type of StrategyInfo, must be a subclass of from nfd::fw::StrategyInfo
+   */
+  template<typename T>
+  void
+  setStrategyInfo(shared_ptr<T> strategyInfo);
+
+  /** \brief get or create a StrategyInfo item
+   *  \tparam T type of StrategyInfo, must be a subclass of from nfd::fw::StrategyInfo
+   *
+   *  If no StrategyInfo of type T is stored, it's created with \p{args};
+   *  otherwise, the existing item is returned.
+   */
+  template<typename T, typename ...A>
+  shared_ptr<T>
+  getOrCreateStrategyInfo(A&&... args);
+
+  /** \brief clear all StrategyInfo items
+   */
+  void
+  clearStrategyInfo();
+
+private:
+  std::map<int, shared_ptr<fw::StrategyInfo>> m_items;
+};
+
+
+template<typename T>
+shared_ptr<T>
+StrategyInfoHost::getStrategyInfo() const
+{
+  static_assert(std::is_base_of<fw::StrategyInfo, T>::value,
+                "T must inherit from StrategyInfo");
+
+  auto it = m_items.find(T::getTypeId());
+  if (it == m_items.end()) {
+    return nullptr;
+  }
+  return static_pointer_cast<T, fw::StrategyInfo>(it->second);
+}
+
+template<typename T>
+void
+StrategyInfoHost::setStrategyInfo(shared_ptr<T> item)
+{
+  static_assert(std::is_base_of<fw::StrategyInfo, T>::value,
+                "T must inherit from StrategyInfo");
+
+  if (item == nullptr) {
+    m_items.erase(T::getTypeId());
+  }
+  else {
+    m_items[T::getTypeId()] = item;
+  }
+}
+
+template<typename T, typename ...A>
+shared_ptr<T>
+StrategyInfoHost::getOrCreateStrategyInfo(A&&... args)
+{
+  static_assert(std::is_base_of<fw::StrategyInfo, T>::value,
+                "T must inherit from StrategyInfo");
+
+  shared_ptr<T> item = this->getStrategyInfo<T>();
+  if (!static_cast<bool>(item)) {
+    item = make_shared<T>(std::forward<A>(args)...);
+    this->setStrategyInfo(item);
+  }
+  return item;
+}
+
+} // namespace nfd
+
+#endif // NFD_DAEMON_TABLE_STRATEGY_INFO_HOST_HPP
diff --git a/NFD/docs/FAQ.rst b/NFD/docs/FAQ.rst
new file mode 100644
index 0000000..0709ef8
--- /dev/null
+++ b/NFD/docs/FAQ.rst
@@ -0,0 +1,221 @@
+FAQ
+===
+
+How to change default paths?
+----------------------------
+
+Paths to where NFD is installed can be configured during ``./waf
+configure``:
+
+- Installation prefix (default ``/usr/local``):
+
+    ::
+
+        ./waf configure --prefix=/usr
+
+- Location of NFD configuration file (default: ``${prefix}/etc``):
+
+    ::
+
+        ./waf configure --prefix=/usr --sysconfdir=/etc
+
+- Location of manpages (default: ``${prefix}/share/man``)
+
+    ::
+
+        ./waf configure --prefix=/usr --sysconfdir=/etc --mandir=/usr/share/man
+
+How to run NFD on Raspberry Pi?
+-------------------------------
+
+To run NFD on the Raspberry Pi, you need to either enable IPv6 support
+in Raspberry Pi or disable IPv6 support in NFD.
+
+To enable IPv6 in Raspberry Pi:
+
+::
+
+    sudo modprobe ipv6
+
+To disable IPv6 in NFD, replace ``enable_v6 yes`` with ``enable_v6 no``
+in ``tcp`` and ``udp`` sections of ``/usr/local/etc/ndn/nfd.conf``:
+
+::
+
+    ...
+    tcp
+    {
+      listen yes
+      port 6363
+      enable_v4 yes
+      enable_v6 no
+    }
+
+    udp
+    {
+      port 6363
+      enable_v4 yes
+      enable_v6 no
+      idle_timeout 600
+      keep_alive_interval 25
+
+      mcast yes
+      mcast_port 56363
+      mcast_group 224.0.23.170
+    }
+    ...
+
+
+How to run NFD as non-root user?
+--------------------------------
+
+How to configure automatic dropping of privileges?
+++++++++++++++++++++++++++++++++++++++++++++++++++
+
+NFD can be configured to drop privileges whenever possible.  You can specify a user and/or
+group for NFD to change its *effective* user/group ID to in the ``general`` section of the
+configuration file. For example:
+
+::
+
+    general
+    {
+      user nobody
+      group nogroup
+    }
+
+will configure NFD to drop its effective user and group IDs to ``nobody`` and ``nogroup``,
+respectively.
+
+.. note::
+
+    **IMPORTANT:** NFD may regain elevated permissions as needed during normal
+    execution. Dropping privileges in this manner should not be considered a security
+    mechanism (a compromised NFD that was started as root can trivially return to
+    root). However, reducing privileges may limit any damaged caused by well intentioned,
+    but buggy, code.
+
+
+How to enable Ethernet Face Support?
+++++++++++++++++++++++++++++++++++++
+
+The ``ether`` configuration file section contains settings for Ethernet faces and
+channels. These settings will **NOT** work without root or setting the appropriate
+permissions:
+
+::
+
+    sudo setcap cap_net_raw,cap_net_admin=eip /full/path/nfd
+
+You may need to install a package to use setcap:
+
+**Ubuntu:**
+
+::
+
+    sudo apt-get install libcap2-bin
+
+**Mac OS X:**
+
+::
+
+    curl https://bugs.wireshark.org/bugzilla/attachment.cgi?id=3373 -o ChmodBPF.tar.gz
+    tar zxvf ChmodBPF.tar.gz
+    open ChmodBPF/Install\ ChmodBPF.app
+
+or manually:
+
+::
+
+    sudo chgrp admin /dev/bpf*
+    sudo chmod g+rw /dev/bpf*
+
+How to enable UDP multicast support in multi-homed Linux machines
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+The UDP configuration file section contains settings for unicast and multicast UDP
+faces. If the Linux box is equipped with multiple network interfaces with multicast
+capabilities, the settings for multicast faces will **NOT** work without root
+or setting the appropriate permissions:
+
+::
+
+    sudo setcap cap_net_raw=eip /full/path/nfd
+
+.. _How to configure NFD security:
+
+How to configure NFD security?
+------------------------------
+
+.. note:: Sample configuration file of NFD allow any user to manage faces, FIB, RIB, and
+    StrategyChoice.  The following description can be used to restrict certain operations
+    to certain users.
+
+    More extensive documentation about NFD's security and options to configure trust model
+    for NFD is currently in preparation.
+
+Many NFD management protocols require signed commands to be processed
+(e.g. FIB modification, Face creation/destructions, etc.). You will need
+an NDN certificate to use any application that issues signed commands.
+
+If you do not already have NDN certificate, you can generate one with
+the following commands:
+
+**Generate and install a self-signed identity certificate**:
+
+::
+
+    ndnsec-keygen /`whoami` | ndnsec-install-cert -
+
+Note that the argument to ndnsec-key will be the identity name of the
+new key (in this case, ``/your-username``). Identity names are
+hierarchical NDN names and may have multiple components (e.g.
+``/ndn/ucla/edu/alice``). You may create additional keys and identities
+as you see fit.
+
+**Dump the NDN certificate to a file**:
+
+The following commands assume that you have not modified ``PREFIX`` or
+``SYSCONFDIR`` If you have, please substitute ``/usr/local/etc`` for the
+appropriate value (the overriden ``SYSCONFDIR`` or ``PREFIX/etc`` if you
+changed ``PREFIX``).
+
+::
+
+    sudo mkdir -p /usr/local/etc/ndn/keys
+    ndnsec-cert-dump -i /`whoami` > default.ndncert
+    sudo mv default.ndncert /usr/local/etc/ndn/keys/default.ndncert
+
+.. _How to start using NDN MacPorts repository on OSX:
+
+How to start using NDN MacPorts repository on OSX?
+--------------------------------------------------
+
+Please see :ref:`Install NFD Using the NDN MacPorts Repository on OS X`.
+
+.. _How to start using NDN PPA repository on Ubuntu Linux:
+
+How to start using NDN PPA repository on Ubuntu Linux?
+------------------------------------------------------
+
+Please see :ref:`Install NFD Using the NDN PPA Repository on Ubuntu Linux`.
+
+.. _How to start using NDN Overlay on Gentoo Linux:
+
+How to start using NDN Overlay on Gentoo Linux?
+-----------------------------------------------
+
+If you want to install NFD and other packages using Portage on Gentoo Linux, you can start
+using NDN Overlay.
+
+Simply checkout `NDN Overlay <https://github.com/Pesa/ndn-overlay>`_ repository to some
+location add the checkout path to ``PORTDIR_OVERLAY`` in ``/etc/portage/make.conf``:
+
+::
+
+    cd /some/path
+    git clone https://github.com/Pesa/ndn-overlay.git
+
+    # and then add /some/path/ndn-overlay to PORTDIR_OVERLAY in /etc/portage/make.conf
+
+Afterwards, you will be able to install ``nfd`` using standard mechanisms.
diff --git a/NFD/docs/INSTALL.rst b/NFD/docs/INSTALL.rst
new file mode 100644
index 0000000..e04ee48
--- /dev/null
+++ b/NFD/docs/INSTALL.rst
@@ -0,0 +1,387 @@
+Getting Started with NFD
+========================
+
+Installing NFD from Binaries
+----------------------------
+
+We provide NFD binaries for the supported platforms, which are the preferred installation
+method. In addition to simplifying installation, the binary release also includes
+automatic initial configuration and platform-specific tools to automatically start NFD and
+related daemons.  In particular, on OS X NFD is controlled using `launchd
+<https://github.com/named-data/NFD/tree/master/contrib/osx-launchd>`__ and on Ubuntu using
+`upstart <https://github.com/named-data/NFD/tree/master/contrib/upstart>`__ mechanisms.
+In both cases, `nfd-start` and `nfd-stop` scripts are convenience wrappers for launchd and
+upstart.
+
+On OS X, NFD can be installed with MacPorts.  Refer to :ref:`Install NFD Using the NDN
+MacPorts Repository on OS X` for more details.
+
+On Ubuntu 12.04, 14.04, or 14.10 NFD can be installed from NDN PPA repository.  Refer to
+:ref:`Install NFD Using the NDN PPA Repository on Ubuntu Linux`.
+
+Future releases could include support for other platforms.  Please send us feedback on the
+platforms you're using, so we can prioritize our goals.  We would also appreciate help
+packaging the current NFD release for other platforms.
+
+
+.. _Install NFD Using the NDN MacPorts Repository on OS X:
+
+Install NFD Using the NDN MacPorts Repository on OS X
+-----------------------------------------------------
+
+OS X users have the opportunity to seamlessly install and run NFD as well as other related
+applications via `MacPorts <https://www.macports.org/>`_.  If you are not using MacPorts
+yet, go to the `MacPorts website <https://www.macports.org/install.php>`_ and install the
+MacPorts package.
+
+NFD and related ports are not part of the official MacPorts repository. In order to use
+these ports, you will need to add the NDN MacPorts repository to your local configuration.
+In particular, you will need to modify the list of source URLs for MacPorts.  For example,
+if your MacPorts are installed in ``/opt/local``, add the following line to
+`/opt/local/etc/macports/sources.conf` before or after the default port repository:
+
+::
+
+    rsync://macports.named-data.net/macports/
+
+After this step, you can use ``sudo port selfupdate`` to fetch updated port definitions.
+
+The following command will install NFD using MacPorts:
+
+::
+
+    sudo port install nfd
+
+.. note::
+
+    You have to have XCode installed on your machine. This can be installed from the
+    AppStore (free) on OS X 10.7 or later. Older editions of OS X can download an
+    appropriate version of XCode from http://developer.apple.com.
+
+
+One advantage of using MacPorts is that you can easily upgrade NFD and other packages to
+the latest version.  The following commands will do this job:
+
+::
+
+    sudo port selfupdate
+    sudo port upgrade nfd
+
+.. _Install NFD Using the NDN PPA Repository on Ubuntu Linux:
+
+Install NFD Using the NDN PPA Repository on Ubuntu Linux
+--------------------------------------------------------
+
+NFD binaries and related tools for Ubuntu 12.04, 14.04, or 14.10 can be installed using PPA
+packages from named-data repository.  First, you will need to add ``named-data/ppa``
+repository to binary package sources and update list of available packages.
+
+Preliminary steps if you haven't used PPA packages before
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+To simplify adding new PPA repositories, Ubuntu provides ``add-apt-repository`` tool,
+which is not installed by default on some platforms.
+
+On Ubuntu **12.04**:
+
+::
+
+    sudo apt-get install python-software-properties
+
+On Ubuntu **14.04** or **14.10**:
+
+::
+
+    sudo apt-get install software-properties-common
+
+
+Adding NDN PPA
+~~~~~~~~~~~~~~
+
+After installing ``add-apt-repository``, run the following command to add `NDN PPA
+repository`_.
+
+::
+
+    sudo add-apt-repository ppa:named-data/ppa
+    sudo apt-get update
+
+Installing NFD and other NDN packages
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+After you have added `NDN PPA repository`_, NFD and other NDN packages can be easily
+installed in a standard way, i.e., either using ``apt-get`` as shown below or using any
+other package manager, such as Synaptic Package Manager:
+
+::
+
+    sudo apt-get install nfd
+
+For the list of available packages, refer to `NDN PPA repository`_ homepage.
+
+.. _NDN PPA repository: https://launchpad.net/~named-data/+archive/ppa
+
+Building from Source
+--------------------
+
+Downloading from Git
+~~~~~~~~~~~~~~~~~~~~
+
+The first step is to obtain the source code for ``NFD`` and, its main dependency, the
+``ndn-cxx`` library.  If you are not planning to work with the bleeding edge code, make
+sure you checkout the correct release tag (e.g., ``*-0.2.0``) for both repositories:
+
+::
+
+    # Download ndn-cxx
+    git clone https://github.com/named-data/ndn-cxx
+
+    # Download NFD
+    git clone --recursive https://github.com/named-data/NFD
+
+Prerequisites
+~~~~~~~~~~~~~
+
+-  Install the `ndn-cxx library <http://named-data.net/doc/ndn-cxx/current/INSTALL.html>`_
+   and its requirements
+
+-  ``pkg-config``
+
+   On OS X 10.8, 10.9, and 10.10 with MacPorts:
+
+   ::
+
+       sudo port install pkgconfig
+
+   On Ubuntu >= 12.04:
+
+   ::
+
+       sudo apt-get install pkg-config
+
+-  ``libpcap``
+
+   Comes with the base system on OS X 10.8, 10.9, and 10.10.
+
+   On Ubuntu >= 12.04:
+
+   ::
+
+       sudo apt-get install libpcap-dev
+
+To build manpages and API documentation:
+
+-  ``doxygen``
+-  ``graphviz``
+-  ``python-sphinx``
+
+   On OS X 10.8, 10.9, and 10.10 with MacPorts:
+
+   ::
+
+       sudo port install doxygen graphviz py27-sphinx sphinx_select
+       sudo port select sphinx py27-sphinx
+
+   On Ubuntu >= 12.04:
+
+   ::
+
+       sudo apt-get install doxygen graphviz python-sphinx
+
+
+Besides officially supported platforms, NFD is known to work on: Fedora 20, CentOS 6/7, Gentoo Linux,
+Raspberry Pi, OpenWRT, FreeBSD 10.0, and several other platforms.  We are soliciting help
+with documenting common problems / pitfalls in installing/using NFD on different platforms
+on `NFD Wiki
+<http://redmine.named-data.net/projects/nfd/wiki/Wiki#Installation-experiences-for-selected-platforms>`__.
+
+
+Build
+~~~~~
+
+The following basic commands should be used to build NFD on Ubuntu:
+
+::
+
+    ./waf configure
+    ./waf
+    sudo ./waf install
+
+If you have installed `ndn-cxx` library and/or other dependencies into a non-standard paths, you
+may need to modify ``PKG_CONFIG_PATH`` environment variable before running ``./waf configure``.
+For example,
+
+::
+
+    export PKG_CONFIG_PATH=/custom/lib/pkgconfig:$PKG_CONFIG_PATH
+    ./waf configure
+    ./waf
+    sudo ./waf install
+
+
+Refer to ``./waf --help`` for more options that can be used during ``configure`` stage and
+how to properly configure and run NFD.
+
+.. note::
+   If you are working on a source repository that has been compiled before, and you have
+   upgraded one of the dependencies, please execute ``./waf distclean`` to clear object files
+   and start over.
+
+Debug symbols
+~~~~~~~~~~~~~
+
+The default compiler flags enable debug symbols to be included in binaries.  This
+potentially allows more meaningful debugging if NFD or other tools happen to crash.
+
+If it is undesirable, default flags can be easily overridden.  The following example shows
+how to completely disable debug symbols and configure NFD to be installed into ``/usr``
+with configuration in ``/etc`` folder.
+
+::
+
+    CXXFLAGS="-O2" ./waf configure --prefix=/usr --sysconfdir=/etc
+    ./waf
+    sudo ./waf install
+
+Building documentation
+~~~~~~~~~~~~~~~~~~~~~~
+
+NFD tutorials and API documentation can be built using the following commands:
+
+::
+
+    # Full set of documentation (tutorials + API) in build/docs
+    ./waf docs
+
+    # Only tutorials in `build/docs`
+    ./waf sphinx
+
+    # Only API docs in `build/docs/doxygen`
+    ./waf doxgyen
+
+
+Manpages are automatically created and installed during the normal build process (e.g.,
+during ``./waf`` and ``./waf install``), if ``python-sphinx`` module is detected during
+``./waf configure`` stage.  By default, manpages are installed into
+``${PREFIX}/share/man`` (where default value for ``PREFIX`` is ``/usr/local``). This
+location can be changed during ``./waf configure`` stage using ``--prefix``,
+``--datarootdir``, or ``--mandir`` options.
+
+For more details, refer to ``./waf --help``.
+
+
+Initial configuration
+---------------------
+
+.. note::
+    If you have installed NFD from binary packages, the package manager has already
+    installed initial configuration and you can safely skip this section.
+
+General
+~~~~~~~
+
+After installing NFD from source, you need to create a proper config file.  If default
+location for ``./waf configure`` was used, this can be accomplished by simply copying the
+sample configuration file:
+
+::
+
+    sudo cp /usr/local/etc/ndn/nfd.conf.sample /usr/local/etc/ndn/nfd.conf
+
+NFD Security
+~~~~~~~~~~~~
+
+NFD provides mechanisms to enable strict authorization for all management commands. In
+particular, one can authorize only specific public keys to create new Faces or change the
+forwarding strategy for specific namespaces. For more information about how to generate
+private/public key pair, generate self-signed certificate, and use this self-signed
+certificate to authorize NFD management commands refer to :ref:`How to configure NFD
+security` FAQ question.
+
+In the sample configuration file, all authorizations are disabled, effectively allowing
+anybody on the local machine to issue NFD management commands. **The sample file is
+intended only for demo purposes and MUST NOT be used in a production environment.**
+
+Running
+-------
+
+**You should not run ndnd or ndnd-tlv, otherwise NFD will not work correctly**
+
+Starting
+~~~~~~~~
+
+In order to use NFD, you need to start two separate daemons: ``nfd`` (the forwarder
+itself) and ``nrd`` (RIB manager that will manage all prefix registrations).  The
+recommended way is to use `nfd-start` script:
+
+::
+
+    nfd-start
+
+On OS X it may ask for your keychain password or ask ``nfd/nrd wants to sign using key in
+your keychain.`` Enter your keychain password and click Always Allow.
+
+Later, you can stop NFD with ``nfd-stop`` or by simply killing the ``nfd`` process.
+
+
+Connecting to remote NFDs
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+To create a UDP or TCP tunnel to remote NFD and create route toward it, use the following
+command in terminal:
+
+::
+
+    nfdc register /ndn udp://<other host>
+
+where ``<other host>`` is the name or IP address of the other host (e.g.,
+``udp://spurs.cs.ucla.edu``). This outputs:
+
+::
+
+    Successful in name registration: ControlParameters(Name: /ndn, FaceId: 260, Origin: 255, Cost: 0, Flags: 1, )
+
+The ``/ndn`` means that NFD will forward all Interests that start with ``/ndn`` through
+the face to the other host.  If you only want to forward Interests with a certain prefix,
+use it instead of ``/ndn``.  This only forwards Interests to the other host, but there is
+no "back route" for the other host to forward Interests to you.  For that, you must go to
+the other host and use ``nfdc`` to add the route.
+
+The "back route" can also be automatically configured with ``nfd-autoreg``. For more
+information refer to :doc:`manpages/nfd-autoreg`.
+
+Playing with NFD
+----------------
+
+After you haved installed, configured, and started NFD, you can try to install and play
+with the following:
+
+Sample applications:
+
+-  `Simple examples in ndn-cxx
+   library <http://named-data.net/doc/ndn-cxx/current/examples.html>`__.
+   If you have installed ndn-cxx from source, you already have compiled
+   these:
+
+   +  examples/producer
+   +  examples/consumer
+   +  examples/consumer-with-timer
+
+   +  tools/ndncatchunks3
+   +  tools/ndnputchunks3
+
+-  `Introductory examples of
+   NDN-CCL <http://redmine.named-data.net/projects/nfd/wiki/Getting_Started_-_Common_Client_Libraries#Install-the-Common-Client-Library>`__
+
+Real applications and libraries:
+
+   + `ndn-tlv-ping - Reachability Testing Tool for NDN
+      <https://github.com/named-data/ndn-tlv-ping>`__
+   +  `ndn-traffic-generator - Traffic Generator For
+      NDN <https://github.com/named-data/ndn-traffic-generator>`__
+   +  `repo-ng - Next generation of NDN
+      repository <https://github.com/named-data/repo-ng>`__
+   +  `ChronoChat - Multi-user NDN chat
+      application <https://github.com/named-data/ChronoChat>`__
+   +  `ChronoSync - Sync library for multiuser realtime applications for
+      NDN <https://github.com/named-data/ChronoSync>`__
diff --git a/NFD/docs/RELEASE_NOTES.rst b/NFD/docs/RELEASE_NOTES.rst
new file mode 100644
index 0000000..bfb47b9
--- /dev/null
+++ b/NFD/docs/RELEASE_NOTES.rst
@@ -0,0 +1,263 @@
+.. _NFD Release Notes:
+
+NFD Release Notes
+=================
+
+NFD version 0.2.0 (changes since version 0.1.0)
+-----------------------------------------------
+
+Release date: August 25, 2014
+
+- **Documentation**
+
+  + `"NFD Developer's Guide" by NFD authors
+    <http://named-data.net/wp-content/uploads/2014/07/NFD-developer-guide.pdf>`_ that
+    explains NFD's internals including the overall design, major modules, their
+    implementation, and their interactions
+
+  + New detailed instructions on how to enable auto-start of NFD using OSX ``launchd``
+    and Ubuntu's ``upstart`` (see `contrib/ folder
+    <https://github.com/named-data/NFD/tree/master/contrib>`_)
+
+- **Core**
+
+  + Add support for temporary privilege drop and elevation (`Issue #1370
+    <http://redmine.named-data.net/issues/1370>`_)
+
+  + Add support to reinitialize multicast Faces and (partially) reload config file
+    (`Issue #1584 <http://redmine.named-data.net/issues/1584>`_)
+
+  + Randomization routines are now uniform across all NFD modules
+    (`Issue #1369 <http://redmine.named-data.net/issues/1369>`_)
+
+  + Enable use of new NDN naming conventions
+    (`Issue #1837 <http://redmine.named-data.net/issues/1837>`_ and
+    `#1838 <http://redmine.named-data.net/issues/1838>`_)
+
+- **Faces**
+
+  + `WebSocket <http://tools.ietf.org/html/rfc6455>`_ Face support
+    (`Issue #1468 <http://redmine.named-data.net/issues/1468>`_)
+
+  + Fix Ethernet Face support on Linux with ``libpcap`` version >=1.5.0
+    (`Issue #1511 <http://redmine.named-data.net/issues/1511>`_)
+
+  + Fix to recognize IPv4-mapped IPv6 addresses in ``FaceUri``
+    (`Issue #1635 <http://redmine.named-data.net/issues/1635>`_)
+
+  + Fix to avoid multiple onFail events
+    (`Issue #1497 <http://redmine.named-data.net/issues/1497>`_)
+
+  + Fix broken support of multicast UDP Faces on OSX
+    (`Issue #1668 <http://redmine.named-data.net/issues/1668>`_)
+
+  + On Linux, path MTU discovery on unicast UDPv4 faces is now disabled
+    (`Issue #1651 <http://redmine.named-data.net/issues/1651>`_)
+
+  + Added link layer byte counts in FaceCounters
+    (`Issue #1729 <http://redmine.named-data.net/issues/1729>`_)
+
+  + Face IDs 0-255 are now reserved for internal NFD use
+    (`Issue #1620 <http://redmine.named-data.net/issues/1620>`_)
+
+  + Serialized StreamFace::send(Interest|Data) operations using queue
+    (`Issue #1777 <http://redmine.named-data.net/issues/1777>`_)
+
+- **Forwarding**
+
+  + Outgoing Interest pipeline now allows strategies to request a fresh ``Nonce``
+    (e.g., when the strategy needs to re-express the Interest)
+    (`Issue #1596 <http://redmine.named-data.net/issues/1596>`_)
+
+  + Fix in the incoming Data pipeline to avoid sending packets to the incoming Face
+    (`Issue #1556 <http://redmine.named-data.net/issues/1556>`_)
+
+  + New ``RttEstimator`` class that implements the Mean-Deviation RTT estimator to be used
+    in forwarding strategies
+
+  + Fix memory leak caused by not removing PIT entry when Interest matches CS
+    (`Issue #1882 <http://redmine.named-data.net/issues/1882>`_)
+
+  + Fix spurious assertion in NCC strategy
+    (`Issue #1853 <http://redmine.named-data.net/issues/1853>`_)
+
+- **Tables**
+
+  + Fix in ContentStore to properly adjust internal structure when ``Cs::setLimit`` is called
+    (`Issue #1646 <http://redmine.named-data.net/issues/1646>`_)
+
+  + New option in configuration file to set an upper bound on ContentStore size
+    (`Issue #1623 <http://redmine.named-data.net/issues/1623>`_)
+
+  + Fix to prevent infinite lifetime of Measurement entries
+    (`Issue #1665 <http://redmine.named-data.net/issues/1665>`_)
+
+  + Introducing capacity limit in PIT NonceList
+    (`Issue #1770 <http://redmine.named-data.net/issues/1770>`_)
+
+  + Fix memory leak in NameTree
+    (`Issue #1803 <http://redmine.named-data.net/issues/1803>`_)
+
+  + Fix segfault during Fib::removeNextHopFromAllEntries
+    (`Issue #1816 <http://redmine.named-data.net/issues/1816>`_)
+
+- **Management**
+
+  + RibManager now fully support ``CHILD_INHERIT`` and ``CAPTURE`` flags
+    (`Issue #1325 <http://redmine.named-data.net/issues/1325>`_)
+
+  + Fix in ``FaceManager`` to respond with canonical form of Face URI for Face creation
+    command (`Issue #1619 <http://redmine.named-data.net/issues/1619>`_)
+
+  + Fix to prevent creation of duplicate TCP/UDP Faces due to async calls
+    (`Issue #1680 <http://redmine.named-data.net/issues/1680>`_)
+
+  + Fix to properly handle optional ExpirationPeriod in RibRegister command
+    (`Issue #1772 <http://redmine.named-data.net/issues/1772>`_)
+
+  + Added functionality of publishing RIB status (RIB dataset) by RibManager
+    (`Issue #1662 <http://redmine.named-data.net/issues/1662>`_)
+
+  + Fix issue of not properly canceling route expiration during processing of
+    ``unregister`` command
+    (`Issue #1902 <http://redmine.named-data.net/issues/1902>`_)
+
+  + Enable periodic clean up of route entries that refer to non-existing faces
+    (`Issue #1875 <http://redmine.named-data.net/issues/1875>`_)
+
+- **Tools**
+
+  + Extended functionality of ``nfd-status``
+
+     * ``-x`` to output in XML format, see :ref:`nfd-status xml schema`
+     * ``-c`` to retrieve channel status information (enabled by default)
+     * ``-s`` to retrieve configured strategy choice for NDN namespaces (enabled by default)
+     * Face status now includes reporting of Face flags (``local`` and ``on-demand``)
+     * On-demand UDP Faces now report remaining lifetime (``expirationPeriod``)
+     * ``-r`` to retrieve RIB information
+
+  + Improved ``nfd-status-http-server``
+
+     * HTTP server now presents status as XSL-formatted XML page
+     * XML dataset and formatted page now include certificate name of the corresponding NFD
+       (`Issue #1807 <http://redmine.named-data.net/issues/1807>`_)
+
+  + Several fixes in ``ndn-autoconfig`` tool
+    (`Issue #1595 <http://redmine.named-data.net/issues/1595>`_)
+
+  + Extended options in ``nfdc``:
+
+    * ``-e`` to set expiration time for registered routes
+    * ``-o`` to specify origin for registration and unregistration commands
+
+  + Enable ``all-faces-prefix'' option in ``nfd-autoreg`` to register prefix for all face
+    (on-demand and non-on-demand)
+    (`Issue #1861 <http://redmine.named-data.net/issues/1861>`_)
+
+  + Enable processing auto-registration in ``nfd-autoreg`` for faces that existed
+    prior to start of the tool
+    (`Issue #1863 <http://redmine.named-data.net/issues/1863>`_)
+
+- **Build**
+
+  + Enable support of precompiled headers for clang and gcc to speed up compilation
+
+- `Other small fixes and extensions
+  <https://github.com/named-data/NFD/compare/NFD-0.1.0...master>`_
+
+NFD version 0.1.0
+-----------------
+
+Release date: May 7, 2014
+
+This is an incomplete list of features that are implemented in NFD version 0.1.0.
+
+- **Packet Format**
+
+  + `NDN-TLV <http://named-data.net/doc/ndn-tlv/>`_
+  + LocalControlHeader, to allow apps to set outgoing face and learn incoming face.
+
+- **Faces**
+
+  + Unix stream socket
+  + UDP unicast
+  + UDP multicast
+  + TCP
+  + Ethernet, currently without fragmentation.
+
+    .. note::
+         Ethernet support will not work properly on Linux kernels with TPACKET_V3 flexible
+         buffer implementation (>= 3.2.0) and libpcap >= 1.5.0 (e.g., Ubuntu Linux 14.04).
+         Refer to `Issue 1551 <http://redmine.named-data.net/issues/1511>`_ for more
+         detail and implementation progress.
+
+- **Management**
+
+  + Use of signed Interests as commands, with authentication and authorization.
+  + Face management
+  + FIB management
+  + Per-namespace strategy selection
+  + NFD status publishing
+  + Notification to authorized apps of internal events, including Face creation and destruction.
+
+- **Tables and forwarding pipelines** support most Interest/Data processing, including
+  selectors.
+
+- **RIB Management** that runs as a separate process, ``nrd``.  It supports basic prefix
+  registration by applications, but no flags yet.
+
+- **Strategies**
+
+  + ``broadcast``
+  + ``best-route``
+  + ``ncc``: based on ccnx 0.7 for experimentation
+  + ``client-control``: authorized application can directly control Interest forwarding
+
+- **Name-based scoping**
+
+  + ``/localhost``: communication only within localhost using "local" Faces
+    (UnixStreamFace, LocalTcpFace).  NFD will strictly enforce this scope for Interests
+    and Data packets
+  + ``/localhop``: one-hop communication (e.g., if at least one incoming or outgoing Face
+    in PIT entry is non-local, the Interest cannot be forwarded to any non-local Face)
+
+- **Support configuration file**, which is in the Boost INFO format.
+
+- **Applications**
+
+  + Tools to discover hubs on NDN testbed.
+  + peek/poke and traffic generators for testing and debugging.
+  + ``nfdc``, a command-line tool to configure NFD.
+  + ``nfd-status``, a command-line tool to query NFD status.
+  + ``nfd-status-http-server``, which reads the NFD status and publishes over HTTP.
+
+
+Planned Functions and Features for Next Releases
+------------------------------------------------
+
+- NACK
+    A packet sent back by a producer or a router to signal the unavailability of a requested
+    Data packet. The protocol specification for NACK is in progress.
+
+- New strategies
+    Additional strategies, including self-learning that populates the FIB by observing
+    Interest and Data exchange.
+
+- Hop-by-hop Interest limit mechanism
+    For congestion control
+
+- Face enhancements
+    Add fragmentation support for Ethernet face, may add support for new types such as
+    WiFi direct and WebSockets.
+
+- Tables
+    Experiment and evaluate different data structures and algorithms.
+
+- RIB management
+    Move to more scalable data structures and support all flags in prefix registrations.
+
+- Tunnel management
+    For hub nodes to authenticate incoming tunnel requests and maintain the tunnels.
+
+- Extensible name-based scoping
+    Configurable organization-based scoping
diff --git a/NFD/docs/_static/.gitignore b/NFD/docs/_static/.gitignore
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/NFD/docs/_static/.gitignore
diff --git a/NFD/docs/_static/nfd-status.xsd b/NFD/docs/_static/nfd-status.xsd
new file mode 100644
index 0000000..a34b2f2
--- /dev/null
+++ b/NFD/docs/_static/nfd-status.xsd
@@ -0,0 +1,171 @@
+<?xml version="1.0"?>
+
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
+targetNamespace="ndn:/localhost/nfd/status/1" xmlns:nfd="ndn:/localhost/nfd/status/1"
+elementFormDefault="qualified">
+
+<xs:complexType name="unidirectionalPacketCountersType">
+  <xs:sequence>
+    <xs:element type="xs:nonNegativeInteger" name="nInterests"/>
+    <xs:element type="xs:nonNegativeInteger" name="nDatas"/>
+  </xs:sequence>
+</xs:complexType>
+
+<xs:complexType name="bidirectionalPacketCountersType">
+  <xs:sequence>
+    <xs:element type="nfd:unidirectionalPacketCountersType" name="incomingPackets"/>
+    <xs:element type="nfd:unidirectionalPacketCountersType" name="outgoingPackets"/>
+  </xs:sequence>
+</xs:complexType>
+
+<xs:complexType name="bidirectionalByteCountersType">
+  <xs:sequence>
+    <xs:element type="xs:nonNegativeInteger" name="incomingBytes"/>
+    <xs:element type="xs:nonNegativeInteger" name="outgoingBytes"/>
+  </xs:sequence>
+</xs:complexType>
+
+<xs:complexType name="generalStatusType">
+  <xs:sequence>
+    <xs:element type="xs:anyURI" name="nfdId"/>
+    <xs:element type="xs:string" name="version"/>
+    <xs:element type="xs:dateTime" name="startTime"/>
+    <xs:element type="xs:dateTime" name="currentTime"/>
+    <xs:element type="xs:duration" name="uptime"/>
+    <xs:element type="xs:nonNegativeInteger" name="nNameTreeEntries"/>
+    <xs:element type="xs:nonNegativeInteger" name="nFibEntries"/>
+    <xs:element type="xs:nonNegativeInteger" name="nPitEntries"/>
+    <xs:element type="xs:nonNegativeInteger" name="nMeasurementsEntries"/>
+    <xs:element type="xs:nonNegativeInteger" name="nCsEntries"/>
+    <xs:element type="nfd:bidirectionalPacketCountersType" name="packetCounters"/>
+  </xs:sequence>
+</xs:complexType>
+
+<xs:complexType name="channelType">
+  <xs:sequence>
+    <xs:element type="xs:anyURI" name="localUri"/>
+  </xs:sequence>
+</xs:complexType>
+
+<xs:complexType name="channelsType">
+  <xs:sequence>
+    <xs:element type="nfd:channelType" name="channel" maxOccurs="unbounded" minOccurs="0"/>
+  </xs:sequence>
+</xs:complexType>
+
+<xs:complexType name="faceType">
+  <xs:sequence>
+    <xs:element type="xs:nonNegativeInteger" name="faceId"/>
+    <xs:element type="xs:anyURI" name="remoteUri"/>
+    <xs:element type="xs:anyURI" name="localUri"/>
+    <xs:element type="xs:duration" name="expirationPeriod" minOccurs="0"/>
+    <xs:element type="xs:string" name="faceScope"/>
+    <xs:element type="xs:string" name="facePersistency"/>
+    <xs:element type="xs:string" name="linkType"/>
+    <xs:element type="nfd:bidirectionalPacketCountersType" name="packetCounters"/>
+    <xs:element type="nfd:bidirectionalByteCountersType" name="byteCounters"/>
+  </xs:sequence>
+</xs:complexType>
+
+<xs:complexType name="facesType">
+  <xs:sequence>
+    <xs:element type="nfd:faceType" name="face" maxOccurs="unbounded" minOccurs="0"/>
+  </xs:sequence>
+</xs:complexType>
+
+<xs:complexType name="nextHopType">
+  <xs:sequence>
+    <xs:element type="xs:nonNegativeInteger" name="faceId"/>
+    <xs:element type="xs:nonNegativeInteger" name="cost"/>
+  </xs:sequence>
+</xs:complexType>
+
+<xs:complexType name="fibEntryType">
+  <xs:sequence>
+    <xs:element type="xs:anyURI" name="prefix"/>
+    <xs:element name="nextHops">
+      <xs:complexType>
+        <xs:sequence>
+          <xs:element type="nfd:nextHopType" name="nextHop" maxOccurs="unbounded"/>
+        </xs:sequence>
+      </xs:complexType>
+    </xs:element>
+  </xs:sequence>
+</xs:complexType>
+
+<xs:complexType name="fibType">
+  <xs:sequence>
+    <xs:element type="nfd:fibEntryType" name="fibEntry" maxOccurs="unbounded" minOccurs="0"/>
+  </xs:sequence>
+</xs:complexType>
+
+<xs:complexType name="ribFlagsType">
+  <xs:sequence>
+    <xs:element type="xs:string" name="childInherit" minOccurs="0"/>
+    <xs:element type="xs:string" name="ribCapture" minOccurs="0"/>
+  </xs:sequence>
+</xs:complexType>
+
+<xs:complexType name="routeType">
+  <xs:sequence>
+    <xs:element type="xs:nonNegativeInteger" name="faceId"/>
+    <xs:element type="xs:nonNegativeInteger" name="origin"/>
+    <xs:element type="xs:nonNegativeInteger" name="cost"/>
+    <xs:element type="nfd:ribFlagsType" name="flags"/>
+    <xs:element type="xs:duration" name="expirationPeriod" minOccurs="0"/>
+  </xs:sequence>
+</xs:complexType>
+
+<xs:complexType name="ribEntryType">
+  <xs:sequence>
+    <xs:element type="xs:anyURI" name="prefix"/>
+    <xs:element name="routes">
+      <xs:complexType>
+        <xs:sequence>
+          <xs:element type="nfd:routeType" name="route" maxOccurs="unbounded"/>
+        </xs:sequence>
+      </xs:complexType>
+    </xs:element>
+  </xs:sequence>
+</xs:complexType>
+
+<xs:complexType name="ribType">
+  <xs:sequence>
+    <xs:element type="nfd:ribEntryType" name="ribEntry" maxOccurs="unbounded" minOccurs="0"/>
+  </xs:sequence>
+</xs:complexType>
+
+<xs:complexType name="strategyType">
+  <xs:sequence>
+    <xs:element type="xs:anyURI" name="name"/>
+  </xs:sequence>
+</xs:complexType>
+
+<xs:complexType name="strategyChoiceType">
+  <xs:sequence>
+    <xs:element type="xs:anyURI" name="namespace"/>
+    <xs:element type="nfd:strategyType" name="strategy"/>
+  </xs:sequence>
+</xs:complexType>
+
+<xs:complexType name="strategyChoicesType">
+  <xs:sequence>
+    <xs:element type="nfd:strategyChoiceType" name="strategyChoice"
+                maxOccurs="unbounded" minOccurs="0"/>
+  </xs:sequence>
+</xs:complexType>
+
+<xs:element name="nfdStatus">
+  <xs:complexType>
+    <xs:sequence>
+      <xs:element type="nfd:generalStatusType" name="generalStatus"/>
+      <xs:element type="nfd:channelsType" name="channels"/>
+      <xs:element type="nfd:facesType" name="faces"/>
+      <xs:element type="nfd:fibType" name="fib"/>
+      <xs:element type="nfd:ribType" name="rib"/>
+      <xs:element type="nfd:strategyChoicesType" name="strategyChoices"/>
+    </xs:sequence>
+  </xs:complexType>
+</xs:element>
+
+</xs:schema>
diff --git a/NFD/docs/conf.py b/NFD/docs/conf.py
new file mode 100644
index 0000000..c261a1d
--- /dev/null
+++ b/NFD/docs/conf.py
@@ -0,0 +1,268 @@
+# -*- coding: utf-8 -*-
+#
+# NFD - Named Data Networking Forwarding Daemon documentation build configuration file, created by
+# sphinx-quickstart on Sun Apr  6 19:58:22 2014.
+#
+# This file is execfile()d with the current directory set to its
+# containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+import sys
+import os
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#sys.path.insert(0, os.path.abspath('.'))
+
+# -- General configuration ------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = [
+    'sphinx.ext.todo',
+]
+
+def addExtensionIfExists(extension):
+    try:
+        __import__(extension)
+        extensions.append(extension)
+    except ImportError:
+        sys.stderr.write("Extension '%s' in not available. "
+                         "Some documentation may not build correctly.\n" % extension)
+        sys.stderr.write("To install, use \n"
+                         "  sudo pip install %s\n" % extension.replace('.', '-'))
+
+addExtensionIfExists('sphinxcontrib.doxylink')
+
+if os.getenv('GOOGLE_ANALYTICS', None):
+    addExtensionIfExists('sphinxcontrib.googleanalytics')
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+# The encoding of source files.
+#source_encoding = 'utf-8-sig'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = u'NFD - Named Data Networking Forwarding Daemon'
+copyright = u'2014, Named Data Networking Project'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+#today_fmt = '%B %d, %Y'
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+exclude_patterns = []
+
+# The reST default role (used for this markup: `text`) to use for all
+# documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# A list of ignored prefixes for module index sorting.
+#modindex_common_prefix = []
+
+# If true, keep warnings as "system message" paragraphs in the built documents.
+#keep_warnings = False
+
+
+# -- Options for HTML output ----------------------------------------------
+
+# The theme to use for HTML and HTML Help pages.  See the documentation for
+# a list of builtin themes.
+# html_theme = 'default'
+html_theme = 'named_data_theme'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further.  For a list of options available for each theme, see the
+# documentation.
+#html_theme_options = {}
+
+# Add any paths that contain custom themes here, relative to this directory.
+html_theme_path = ['./']
+
+# The name for this set of Sphinx documents.  If None, it defaults to
+# "<project> v<release> documentation".
+#html_title = None
+
+# A shorter title for the navigation bar.  Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+#html_logo = None
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# Add any extra paths that contain custom files (such as robots.txt or
+# .htaccess) here, relative to this directory. These files are copied
+# directly to the root of the documentation.
+#html_extra_path = []
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+#html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+
+# If false, no module index is generated.
+#html_domain_indices = True
+
+# If false, no index is generated.
+#html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+#html_show_sourcelink = True
+
+# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
+#html_show_sphinx = True
+
+# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
+#html_show_copyright = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it.  The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# This is the file name suffix for HTML files (e.g. ".xhtml").
+html_file_suffix = ".html"
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'nfd-docs'
+
+
+# -- Options for LaTeX output ---------------------------------------------
+
+latex_elements = {
+# The paper size ('letterpaper' or 'a4paper').
+#'papersize': 'letterpaper',
+
+# The font size ('10pt', '11pt' or '12pt').
+#'pointsize': '10pt',
+
+# Additional stuff for the LaTeX preamble.
+#'preamble': '',
+}
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title,
+#  author, documentclass [howto, manual, or own class]).
+latex_documents = [
+  ('index', 'nfd-docs.tex', u'NFD - Named Data Networking Forwarding Daemon Documentation',
+   u'Named Data Networking Project', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = False
+
+# If true, show page references after internal links.
+#latex_show_pagerefs = False
+
+# If true, show URL addresses after external links.
+#latex_show_urls = False
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+#latex_domain_indices = True
+
+
+# -- Options for manual page output ---------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+    ('manpages/nfd', 'nfd', u'Named Data Networking Forwarding Daemon', None, 1),
+    ('manpages/nrd', 'nrd', u'NFD RIB Daemon', None, 1),
+    ('manpages/ndn-autoconfig-server', 'ndn-autoconfig-server',
+        u'NFD Auto-configuration Server', None, 1),
+    ('manpages/ndn-autoconfig', 'ndn-autoconfig',
+        u'NFD Auto-configuration Client', None, 1),
+    ('manpages/nfdc', 'nfdc',
+        u'NFD utility to manipulate the forwarding table (FIB)', None, 1),
+    ('manpages/ndn-tlv-peek', 'ndn-tlv-peek', u'NFD consumer', None, 1),
+    ('manpages/ndn-tlv-poke', 'ndn-tlv-poke', u'NFD producer', None, 1),
+    ('manpages/nfd-autoreg', 'nfd-autoreg', u'NFD Auto-registration Server', None, 1),
+    ('manpages/nfd-status-http-server', 'nfd-status-http-server',
+        u'NFD status HTTP server', None, 1),
+    ('manpages/nfd-status', 'nfd-status', u'Command-line utility to show NFD status', None, 1),
+]
+
+
+# If true, show URL addresses after external links.
+#man_show_urls = False
+
+doxylink = {
+  'NFD' : ('NFD.tag', 'doxygen/'),
+}
+
+if os.getenv('GOOGLE_ANALYTICS', None):
+    googleanalytics_id = os.environ['GOOGLE_ANALYTICS']
+    googleanalytics_enabled = True
diff --git a/NFD/docs/doxygen.conf.in b/NFD/docs/doxygen.conf.in
new file mode 100644
index 0000000..cc58aa6
--- /dev/null
+++ b/NFD/docs/doxygen.conf.in
@@ -0,0 +1,2286 @@
+# Doxyfile 1.8.5
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a double hash (##) is considered a comment and is placed in
+# front of the TAG it is preceding.
+#
+# All text after a single hash (#) is considered a comment and will be ignored.
+# The format is:
+# TAG = value [value, ...]
+# For lists, items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (\" \").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all text
+# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv
+# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv
+# for the list of possible encodings.
+# The default value is: UTF-8.
+
+DOXYFILE_ENCODING      = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
+# double-quotes, unless you are using Doxywizard) that should identify the
+# project for which the documentation is generated. This name is used in the
+# title of most generated pages and in a few other places.
+# The default value is: My Project.
+
+PROJECT_NAME           = "NFD - Named Data Networking Forwarding Daemon"
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
+# could be handy for archiving the generated documentation or if some version
+# control system is used.
+
+PROJECT_NUMBER         = @VERSION@
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer a
+# quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF          =
+
+# With the PROJECT_LOGO tag one can specify an logo or icon that is included in
+# the documentation. The maximum height of the logo should not exceed 55 pixels
+# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo
+# to the output directory.
+
+PROJECT_LOGO           =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
+# into which the generated documentation will be written. If a relative path is
+# entered, it will be relative to the location where doxygen was started. If
+# left blank the current directory will be used.
+
+OUTPUT_DIRECTORY       = docs/doxygen
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub-
+# directories (in 2 levels) under the output directory of each output format and
+# will distribute the generated files over these directories. Enabling this
+# option can be useful when feeding doxygen a huge amount of source files, where
+# putting all generated files in the same directory would otherwise causes
+# performance problems for the file system.
+# The default value is: NO.
+
+CREATE_SUBDIRS         = YES
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# Possible values are: Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-
+# Traditional, Croatian, Czech, Danish, Dutch, English, Esperanto, Farsi,
+# Finnish, French, German, Greek, Hungarian, Italian, Japanese, Japanese-en,
+# Korean, Korean-en, Latvian, Norwegian, Macedonian, Persian, Polish,
+# Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish,
+# Turkish, Ukrainian and Vietnamese.
+# The default value is: English.
+
+OUTPUT_LANGUAGE        = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member
+# descriptions after the members that are listed in the file and class
+# documentation (similar to Javadoc). Set to NO to disable this.
+# The default value is: YES.
+
+BRIEF_MEMBER_DESC      = YES
+
+# If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief
+# description of a member or function before the detailed description
+#
+# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+# The default value is: YES.
+
+REPEAT_BRIEF           = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator that is
+# used to form the text in various listings. Each string in this list, if found
+# as the leading text of the brief description, will be stripped from the text
+# and the result, after processing the whole list, is used as the annotated
+# text. Otherwise, the brief description is used as-is. If left blank, the
+# following values are used ($name is automatically replaced with the name of
+# the entity):The $name class, The $name widget, The $name file, is, provides,
+# specifies, contains, represents, a, an and the.
+
+ABBREVIATE_BRIEF       =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# doxygen will generate a detailed section even if there is only a brief
+# description.
+# The default value is: NO.
+
+ALWAYS_DETAILED_SEC    = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+# The default value is: NO.
+
+INLINE_INHERITED_MEMB  = YES
+
+# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path
+# before files name in the file list and in the header files. If set to NO the
+# shortest path that makes the file name unique will be used
+# The default value is: YES.
+
+FULL_PATH_NAMES        = NO
+
+# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
+# Stripping is only done if one of the specified strings matches the left-hand
+# part of the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the path to
+# strip.
+#
+# Note that you can specify absolute paths here, but also relative paths, which
+# will be relative from the directory where doxygen is started.
+# This tag requires that the tag FULL_PATH_NAMES is set to YES.
+
+STRIP_FROM_PATH        =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
+# path mentioned in the documentation of a class, which tells the reader which
+# header file to include in order to use a class. If left blank only the name of
+# the header file containing the class definition is used. Otherwise one should
+# specify the list of include paths that are normally passed to the compiler
+# using the -I flag.
+
+STRIP_FROM_INC_PATH    =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
+# less readable) file names. This can be useful is your file systems doesn't
+# support long names like on DOS, Mac, or CD-ROM.
+# The default value is: NO.
+
+SHORT_NAMES            = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the
+# first line (until the first dot) of a Javadoc-style comment as the brief
+# description. If set to NO, the Javadoc-style will behave just like regular Qt-
+# style comments (thus requiring an explicit @brief command for a brief
+# description.)
+# The default value is: NO.
+
+JAVADOC_AUTOBRIEF      = YES
+
+# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
+# line (until the first dot) of a Qt-style comment as the brief description. If
+# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
+# requiring an explicit \brief command for a brief description.)
+# The default value is: NO.
+
+QT_AUTOBRIEF           = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
+# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
+# a brief description. This used to be the default behavior. The new default is
+# to treat a multi-line C++ comment block as a detailed description. Set this
+# tag to YES if you prefer the old behavior instead.
+#
+# Note that setting this tag to YES also means that rational rose comments are
+# not recognized any more.
+# The default value is: NO.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
+# documentation from any documented member that it re-implements.
+# The default value is: YES.
+
+INHERIT_DOCS           = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a
+# new page for each member. If set to NO, the documentation of a member will be
+# part of the file/class/namespace that contains it.
+# The default value is: NO.
+
+SEPARATE_MEMBER_PAGES  = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
+# uses this value to replace tabs by spaces in code fragments.
+# Minimum value: 1, maximum value: 16, default value: 4.
+
+TAB_SIZE               = 4
+
+# This tag can be used to specify a number of aliases that act as commands in
+# the documentation. An alias has the form:
+# name=value
+# For example adding
+# "sideeffect=@par Side Effects:\n"
+# will allow you to put the command \sideeffect (or @sideeffect) in the
+# documentation, which will result in a user-defined paragraph with heading
+# "Side Effects:". You can put \n's in the value part of an alias to insert
+# newlines.
+
+ALIASES                =
+
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding "class=itcl::class"
+# will allow you to use the command class in the itcl::class meaning.
+
+TCL_SUBST              =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C. For
+# instance, some of the names that are used will be different. The list of all
+# members will be omitted, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_FOR_C  = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
+# Python sources only. Doxygen will then generate output that is more tailored
+# for that language. For instance, namespaces will be presented as packages,
+# qualified scopes will look different, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_JAVA   = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources. Doxygen will then generate output that is tailored for Fortran.
+# The default value is: NO.
+
+OPTIMIZE_FOR_FORTRAN   = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for VHDL.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_VHDL   = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given
+# extension. Doxygen has a built-in mapping, but you can override or extend it
+# using this tag. The format is ext=language, where ext is a file extension, and
+# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
+# C#, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL. For instance to make
+# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
+# (default is Fortran), use: inc=Fortran f=C.
+#
+# Note For files without extension you can use no_extension as a placeholder.
+#
+# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
+# the files are not read by doxygen.
+
+EXTENSION_MAPPING      =
+
+# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
+# according to the Markdown format, which allows for more readable
+# documentation. See http://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you can
+# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
+# case of backward compatibilities issues.
+# The default value is: YES.
+
+MARKDOWN_SUPPORT       = YES
+
+# When enabled doxygen tries to link words that correspond to documented
+# classes, or namespaces to their corresponding documentation. Such a link can
+# be prevented in individual cases by by putting a % sign in front of the word
+# or globally by setting AUTOLINK_SUPPORT to NO.
+# The default value is: YES.
+
+AUTOLINK_SUPPORT       = YES
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should set this
+# tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string);
+# versus func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+# The default value is: NO.
+
+BUILTIN_STL_SUPPORT    = YES
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+# The default value is: NO.
+
+CPP_CLI_SUPPORT        = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
+# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen
+# will parse them like normal C++ but will assume all classes use public instead
+# of private inheritance when no explicit protection keyword is present.
+# The default value is: NO.
+
+SIP_SUPPORT            = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate
+# getter and setter methods for a property. Setting this option to YES will make
+# doxygen to replace the get and set methods by a property in the documentation.
+# This will only work if the methods are indeed getting or setting a simple
+# type. If this is not the case, or you want to show the methods anyway, you
+# should set this option to NO.
+# The default value is: YES.
+
+IDL_PROPERTY_SUPPORT   = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+# The default value is: NO.
+
+DISTRIBUTE_GROUP_DOC   = NO
+
+# Set the SUBGROUPING tag to YES to allow class member groups of the same type
+# (for instance a group of public functions) to be put as a subgroup of that
+# type (e.g. under the Public Functions section). Set it to NO to prevent
+# subgrouping. Alternatively, this can be done per class using the
+# \nosubgrouping command.
+# The default value is: YES.
+
+SUBGROUPING            = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
+# are shown inside the group in which they are included (e.g. using \ingroup)
+# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
+# and RTF).
+#
+# Note that this feature does not work in combination with
+# SEPARATE_MEMBER_PAGES.
+# The default value is: NO.
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
+# with only public data fields or simple typedef fields will be shown inline in
+# the documentation of the scope in which they are defined (i.e. file,
+# namespace, or group documentation), provided this scope is documented. If set
+# to NO, structs, classes, and unions are shown on a separate page (for HTML and
+# Man pages) or section (for LaTeX and RTF).
+# The default value is: NO.
+
+INLINE_SIMPLE_STRUCTS  = NO
+
+# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
+# enum is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically be
+# useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+# The default value is: NO.
+
+TYPEDEF_HIDES_STRUCT   = NO
+
+# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
+# cache is used to resolve symbols given their name and scope. Since this can be
+# an expensive process and often the same symbol appears multiple times in the
+# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
+# doxygen will become slower. If the cache is too large, memory is wasted. The
+# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
+# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
+# symbols. At the end of a run doxygen will report the cache usage and suggest
+# the optimal cache size from a speed point of view.
+# Minimum value: 0, maximum value: 9, default value: 0.
+
+LOOKUP_CACHE_SIZE      = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available. Private
+# class members and static file members will be hidden unless the
+# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
+# Note: This will also disable the warnings about undocumented members that are
+# normally produced when WARNINGS is set to YES.
+# The default value is: NO.
+
+EXTRACT_ALL            = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will
+# be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PRIVATE        = NO
+
+# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal
+# scope will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PACKAGE        = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file will be
+# included in the documentation.
+# The default value is: NO.
+
+EXTRACT_STATIC         = YES
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined
+# locally in source files will be included in the documentation. If set to NO
+# only classes defined in header files are included. Does not have any effect
+# for Java sources.
+# The default value is: YES.
+
+EXTRACT_LOCAL_CLASSES  = YES
+
+# This flag is only useful for Objective-C code. When set to YES local methods,
+# which are defined in the implementation section but not in the interface are
+# included in the documentation. If set to NO only methods in the interface are
+# included.
+# The default value is: NO.
+
+EXTRACT_LOCAL_METHODS  = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base name of
+# the file that contains the anonymous namespace. By default anonymous namespace
+# are hidden.
+# The default value is: NO.
+
+EXTRACT_ANON_NSPACES   = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
+# undocumented members inside documented classes or files. If set to NO these
+# members will be included in the various overviews, but no documentation
+# section is generated. This option has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_MEMBERS     = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy. If set
+# to NO these classes will be included in the various overviews. This option has
+# no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_CLASSES     = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
+# (class|struct|union) declarations. If set to NO these declarations will be
+# included in the documentation.
+# The default value is: NO.
+
+HIDE_FRIEND_COMPOUNDS  = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
+# documentation blocks found inside the body of a function. If set to NO these
+# blocks will be appended to the function's detailed documentation block.
+# The default value is: NO.
+
+HIDE_IN_BODY_DOCS      = NO
+
+# The INTERNAL_DOCS tag determines if documentation that is typed after a
+# \internal command is included. If the tag is set to NO then the documentation
+# will be excluded. Set it to YES to include the internal documentation.
+# The default value is: NO.
+
+INTERNAL_DOCS          = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
+# names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+# The default value is: system dependent.
+
+CASE_SENSE_NAMES       = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
+# their full class and namespace scopes in the documentation. If set to YES the
+# scope will be hidden.
+# The default value is: NO.
+
+HIDE_SCOPE_NAMES       = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
+# the files that are included by a file in the documentation of that file.
+# The default value is: YES.
+
+SHOW_INCLUDE_FILES     = YES
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
+# files with double quotes in the documentation rather than with sharp brackets.
+# The default value is: NO.
+
+FORCE_LOCAL_INCLUDES   = NO
+
+# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
+# documentation for inline members.
+# The default value is: YES.
+
+INLINE_INFO            = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
+# (detailed) documentation of file and class members alphabetically by member
+# name. If set to NO the members will appear in declaration order.
+# The default value is: YES.
+
+SORT_MEMBER_DOCS       = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
+# descriptions of file, namespace and class members alphabetically by member
+# name. If set to NO the members will appear in declaration order.
+# The default value is: NO.
+
+SORT_BRIEF_DOCS        = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
+# (brief and detailed) documentation of class members so that constructors and
+# destructors are listed first. If set to NO the constructors will appear in the
+# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
+# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
+# member documentation.
+# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
+# detailed member documentation.
+# The default value is: NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
+# of group names into alphabetical order. If set to NO the group names will
+# appear in their defined order.
+# The default value is: NO.
+
+SORT_GROUP_NAMES       = YES
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
+# fully-qualified names, including namespaces. If set to NO, the class list will
+# be sorted only by class name, not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the alphabetical
+# list.
+# The default value is: NO.
+
+SORT_BY_SCOPE_NAME     = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
+# type resolution of all parameters of a function it will reject a match between
+# the prototype and the implementation of a member function even if there is
+# only one candidate or it is obvious which candidate to choose by doing a
+# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
+# accept a match between prototype and implementation in such cases.
+# The default value is: NO.
+
+STRICT_PROTO_MATCHING  = NO
+
+# The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the
+# todo list. This list is created by putting \todo commands in the
+# documentation.
+# The default value is: YES.
+
+GENERATE_TODOLIST      = YES
+
+# The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the
+# test list. This list is created by putting \test commands in the
+# documentation.
+# The default value is: YES.
+
+GENERATE_TESTLIST      = YES
+
+# The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug
+# list. This list is created by putting \bug commands in the documentation.
+# The default value is: YES.
+
+GENERATE_BUGLIST       = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO)
+# the deprecated list. This list is created by putting \deprecated commands in
+# the documentation.
+# The default value is: YES.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional documentation
+# sections, marked by \if <section_label> ... \endif and \cond <section_label>
+# ... \endcond blocks.
+
+ENABLED_SECTIONS       =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
+# initial value of a variable or macro / define can have for it to appear in the
+# documentation. If the initializer consists of more lines than specified here
+# it will be hidden. Use a value of 0 to hide initializers completely. The
+# appearance of the value of individual variables and macros / defines can be
+# controlled using \showinitializer or \hideinitializer command in the
+# documentation regardless of this setting.
+# Minimum value: 0, maximum value: 10000, default value: 30.
+
+MAX_INITIALIZER_LINES  = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
+# the bottom of the documentation of classes and structs. If set to YES the list
+# will mention the files that were used to generate the documentation.
+# The default value is: YES.
+
+SHOW_USED_FILES        = NO
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
+# will remove the Files entry from the Quick Index and from the Folder Tree View
+# (if specified).
+# The default value is: YES.
+
+SHOW_FILES             = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
+# page. This will remove the Namespaces entry from the Quick Index and from the
+# Folder Tree View (if specified).
+# The default value is: YES.
+
+SHOW_NAMESPACES        = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command command input-file, where command is the value of the
+# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
+# by doxygen. Whatever the program writes to standard output is used as the file
+# version. For an example see the documentation.
+
+FILE_VERSION_FILTER    =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. To create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option. You can
+# optionally specify a file name after the option, if omitted DoxygenLayout.xml
+# will be used as the name of the layout file.
+#
+# Note that if you run doxygen from a directory containing a file called
+# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
+# tag is left empty.
+
+LAYOUT_FILE            =
+
+# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
+# the reference definitions. This must be a list of .bib files. The .bib
+# extension is automatically appended if omitted. This requires the bibtex tool
+# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info.
+# For LaTeX the style of the bibliography can be controlled using
+# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
+# search path. Do not use file names with spaces, bibtex cannot handle them. See
+# also \cite for info how to create references.
+
+CITE_BIB_FILES         =
+
+#---------------------------------------------------------------------------
+# Configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated to
+# standard output by doxygen. If QUIET is set to YES this implies that the
+# messages are off.
+# The default value is: NO.
+
+QUIET                  = YES
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES
+# this implies that the warnings are on.
+#
+# Tip: Turn warnings on while writing the documentation.
+# The default value is: YES.
+
+WARNINGS               = YES
+
+# If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate
+# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
+# will automatically be disabled.
+# The default value is: YES.
+
+WARN_IF_UNDOCUMENTED   = YES
+
+# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some parameters
+# in a documented function, or documenting parameters that don't exist or using
+# markup commands wrongly.
+# The default value is: YES.
+
+WARN_IF_DOC_ERROR      = YES
+
+# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
+# are documented, but have no documentation for their parameters or return
+# value. If set to NO doxygen will only warn about wrong or incomplete parameter
+# documentation, but not about the absence of documentation.
+# The default value is: NO.
+
+WARN_NO_PARAMDOC       = YES
+
+# The WARN_FORMAT tag determines the format of the warning messages that doxygen
+# can produce. The string should contain the $file, $line, and $text tags, which
+# will be replaced by the file and line number from which the warning originated
+# and the warning text. Optionally the format may contain $version, which will
+# be replaced by the version of the file (if it could be obtained via
+# FILE_VERSION_FILTER)
+# The default value is: $file:$line: $text.
+
+WARN_FORMAT            = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning and error
+# messages should be written. If left blank the output is written to standard
+# error (stderr).
+
+WARN_LOGFILE           =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag is used to specify the files and/or directories that contain
+# documented source files. You may enter file names like myfile.cpp or
+# directories like /usr/src/myproject. Separate the files or directories with
+# spaces.
+# Note: If this tag is empty the current directory is searched.
+
+INPUT                  = core/ daemon/
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
+# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
+# documentation (see: http://www.gnu.org/software/libiconv) for the list of
+# possible encodings.
+# The default value is: UTF-8.
+
+INPUT_ENCODING         = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank the
+# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii,
+# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp,
+# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown,
+# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf,
+# *.qsf, *.as and *.js.
+
+FILE_PATTERNS          =
+
+# The RECURSIVE tag can be used to specify whether or not subdirectories should
+# be searched for input files as well.
+# The default value is: NO.
+
+RECURSIVE              = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+#
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
+
+EXCLUDE                =
+
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+# The default value is: NO.
+
+EXCLUDE_SYMLINKS       = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories.
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories for example use the pattern */test/*
+
+EXCLUDE_PATTERNS       =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories use the pattern */test/*
+
+EXCLUDE_SYMBOLS        =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or directories
+# that contain example code fragments that are included (see the \include
+# command).
+
+EXAMPLE_PATH           =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank all
+# files are included.
+
+EXAMPLE_PATTERNS       =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude commands
+# irrespective of the value of the RECURSIVE tag.
+# The default value is: NO.
+
+EXAMPLE_RECURSIVE      = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or directories
+# that contain images that are to be included in the documentation (see the
+# \image command).
+
+IMAGE_PATH             =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command:
+#
+# <filter> <input-file>
+#
+# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the
+# name of an input file. Doxygen will then use the output that the filter
+# program writes to standard output. If FILTER_PATTERNS is specified, this tag
+# will be ignored.
+#
+# Note that the filter must not add or remove lines; it is applied before the
+# code is scanned, but not when the output code is generated. If lines are added
+# or removed, the anchors will not be placed correctly.
+
+INPUT_FILTER           =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form: pattern=filter
+# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
+# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
+# patterns match the file name, INPUT_FILTER is applied.
+
+FILTER_PATTERNS        =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER ) will also be used to filter the input files that are used for
+# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
+# The default value is: NO.
+
+FILTER_SOURCE_FILES    = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
+# it is also possible to disable source filtering for a specific pattern using
+# *.ext= (so without naming a filter).
+# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
+
+FILTER_SOURCE_PATTERNS =
+
+# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
+# is part of the input, its contents will be placed on the main page
+# (index.html). This can be useful if you have a project on for instance GitHub
+# and want to reuse the introduction page also for the doxygen output.
+
+USE_MDFILE_AS_MAINPAGE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will be
+# generated. Documented entities will be cross-referenced with these sources.
+#
+# Note: To get rid of all source code in the generated output, make sure that
+# also VERBATIM_HEADERS is set to NO.
+# The default value is: NO.
+
+SOURCE_BROWSER         = YES
+
+# Setting the INLINE_SOURCES tag to YES will include the body of functions,
+# classes and enums directly into the documentation.
+# The default value is: NO.
+
+INLINE_SOURCES         = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
+# special comment blocks from generated source code fragments. Normal C, C++ and
+# Fortran comments will always remain visible.
+# The default value is: YES.
+
+STRIP_CODE_COMMENTS    = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
+# function all documented functions referencing it will be listed.
+# The default value is: NO.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES then for each documented function
+# all documented entities called/used by that function will be listed.
+# The default value is: NO.
+
+REFERENCES_RELATION    = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
+# to YES, then the hyperlinks from functions in REFERENCES_RELATION and
+# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
+# link to the documentation.
+# The default value is: YES.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
+# source code will show a tooltip with additional information such as prototype,
+# brief description and links to the definition and documentation. Since this
+# will make the HTML file larger and loading of large files a bit slower, you
+# can opt to disable this feature.
+# The default value is: YES.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+SOURCE_TOOLTIPS        = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code will
+# point to the HTML generated by the htags(1) tool instead of doxygen built-in
+# source browser. The htags tool is part of GNU's global source tagging system
+# (see http://www.gnu.org/software/global/global.html). You will need version
+# 4.8.6 or higher.
+#
+# To use it do the following:
+# - Install the latest version of global
+# - Enable SOURCE_BROWSER and USE_HTAGS in the config file
+# - Make sure the INPUT points to the root of the source tree
+# - Run doxygen as normal
+#
+# Doxygen will invoke htags (and that will in turn invoke gtags), so these
+# tools must be available from the command line (i.e. in the search path).
+#
+# The result: instead of the source browser generated by doxygen, the links to
+# source code will now point to the output of htags.
+# The default value is: NO.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+USE_HTAGS              = NO
+
+# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
+# verbatim copy of the header file for each class for which an include is
+# specified. Set to NO to disable this.
+# See also: Section \class.
+# The default value is: YES.
+
+VERBATIM_HEADERS       = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
+# compounds will be generated. Enable this if the project contains a lot of
+# classes, structs, unions or interfaces.
+# The default value is: YES.
+
+ALPHABETICAL_INDEX     = YES
+
+# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
+# which the alphabetical index list will be split.
+# Minimum value: 1, maximum value: 20, default value: 5.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+COLS_IN_ALPHA_INDEX    = 5
+
+# In case all classes in a project start with a common prefix, all classes will
+# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
+# can be used to specify a prefix (or a list of prefixes) that should be ignored
+# while generating the index headers.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+IGNORE_PREFIX          =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES doxygen will generate HTML output
+# The default value is: YES.
+
+GENERATE_HTML          = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_OUTPUT            = ./
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
+# generated HTML page (for example: .htm, .php, .asp).
+# The default value is: .html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FILE_EXTENSION    = .html
+
+# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
+# each generated HTML page. If the tag is left blank doxygen will generate a
+# standard header.
+#
+# To get valid HTML the header file that includes any scripts and style sheets
+# that doxygen needs, which is dependent on the configuration options used (e.g.
+# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
+# default header using
+# doxygen -w html new_header.html new_footer.html new_stylesheet.css
+# YourConfigFile
+# and then modify the file new_header.html. See also section "Doxygen usage"
+# for information on how to generate the default header that doxygen normally
+# uses.
+# Note: The header is subject to change so you typically have to regenerate the
+# default header when upgrading to a newer version of doxygen. For a description
+# of the possible markers and block names see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_HEADER            = ../docs/named_data_theme/named_data_header.html
+
+# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
+# generated HTML page. If the tag is left blank doxygen will generate a standard
+# footer. See HTML_HEADER for more information on how to generate a default
+# footer and what special commands can be used inside the footer. See also
+# section "Doxygen usage" for information on how to generate the default footer
+# that doxygen normally uses.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FOOTER            = @HTML_FOOTER@
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
+# sheet that is used by each HTML page. It can be used to fine-tune the look of
+# the HTML output. If left blank doxygen will generate a default style sheet.
+# See also section "Doxygen usage" for information on how to generate the style
+# sheet that doxygen normally uses.
+# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
+# it is more robust and this tag (HTML_STYLESHEET) will in the future become
+# obsolete.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+# HTML_STYLESHEET        = ../docs/named_data_theme/static/named_data_doxygen.css
+
+# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user-
+# defined cascading style sheet that is included after the standard style sheets
+# created by doxygen. Using this option one can overrule certain style aspects.
+# This is preferred over using HTML_STYLESHEET since it does not replace the
+# standard style sheet and is therefor more robust against future updates.
+# Doxygen will copy the style sheet file to the output directory. For an example
+# see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_STYLESHEET  =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that the
+# files will be copied as-is; there are no commands or markers available.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_FILES       = ../docs/named_data_theme/static/doxygen.css \
+                         ../docs/named_data_theme/static/base.css \
+                         ../docs/named_data_theme/static/foundation.css \
+                         ../docs/named_data_theme/static/bar-top.png
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
+# will adjust the colors in the stylesheet and background images according to
+# this color. Hue is specified as an angle on a colorwheel, see
+# http://en.wikipedia.org/wiki/Hue for more information. For instance the value
+# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
+# purple, and 360 is red again.
+# Minimum value: 0, maximum value: 359, default value: 220.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_HUE    = 0
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
+# in the HTML output. For a value of 0 the output will use grayscales only. A
+# value of 255 will produce the most vivid colors.
+# Minimum value: 0, maximum value: 255, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_SAT    = 0
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
+# luminance component of the colors in the HTML output. Values below 100
+# gradually make the output lighter, whereas values above 100 make the output
+# darker. The value divided by 100 is the actual gamma applied, so 80 represents
+# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
+# change the gamma.
+# Minimum value: 40, maximum value: 240, default value: 80.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_GAMMA  = 91
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting this
+# to NO can help when comparing the output of multiple runs.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_TIMESTAMP         = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_DYNAMIC_SECTIONS  = NO
+
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
+# shown in the various tree structured indices initially; the user can expand
+# and collapse entries dynamically later on. Doxygen will expand the tree to
+# such a level that at most the specified number of entries are visible (unless
+# a fully collapsed tree already exceeds this amount). So setting the number of
+# entries 1 will produce a full collapsed tree by default. 0 is a special value
+# representing an infinite number of entries and will result in a full expanded
+# tree by default.
+# Minimum value: 0, maximum value: 9999, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files will be
+# generated that can be used as input for Apple's Xcode 3 integrated development
+# environment (see: http://developer.apple.com/tools/xcode/), introduced with
+# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a
+# Makefile in the HTML output directory. Running make will produce the docset in
+# that directory and running make install will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
+# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_DOCSET        = NO
+
+# This tag determines the name of the docset feed. A documentation feed provides
+# an umbrella under which multiple documentation sets from a single provider
+# (such as a company or product suite) can be grouped.
+# The default value is: Doxygen generated docs.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_FEEDNAME        = "Doxygen generated docs"
+
+# This tag specifies a string that should uniquely identify the documentation
+# set bundle. This should be a reverse domain-name style string, e.g.
+# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_BUNDLE_ID       = org.doxygen.Project
+
+# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+# The default value is: org.doxygen.Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_ID    = org.doxygen.Publisher
+
+# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
+# The default value is: Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_NAME  = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
+# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
+# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
+# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on
+# Windows.
+#
+# The HTML Help Workshop contains a compiler that can convert all HTML output
+# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
+# files are now used as the Windows 98 help format, and will replace the old
+# Windows help format (.hlp) on all Windows platforms in the future. Compressed
+# HTML files also contain an index, a table of contents, and you can search for
+# words in the documentation. The HTML workshop also contains a viewer for
+# compressed HTML files.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_HTMLHELP      = NO
+
+# The CHM_FILE tag can be used to specify the file name of the resulting .chm
+# file. You can add a path in front of the file if the result should not be
+# written to the html output directory.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_FILE               =
+
+# The HHC_LOCATION tag can be used to specify the location (absolute path
+# including file name) of the HTML help compiler ( hhc.exe). If non-empty
+# doxygen will try to run the HTML help compiler on the generated index.hhp.
+# The file has to be specified with full path.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+HHC_LOCATION           =
+
+# The GENERATE_CHI flag controls if a separate .chi index file is generated (
+# YES) or that it should be included in the master .chm file ( NO).
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+GENERATE_CHI           = NO
+
+# The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc)
+# and project file content.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_INDEX_ENCODING     =
+
+# The BINARY_TOC flag controls whether a binary table of contents is generated (
+# YES) or a normal table of contents ( NO) in the .chm file.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+BINARY_TOC             = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members to
+# the table of contents of the HTML help documentation and to the tree view.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+TOC_EXPAND             = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
+# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
+# (.qch) of the generated HTML documentation.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_QHP           = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
+# the file name of the resulting .qch file. The path specified is relative to
+# the HTML output folder.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QCH_FILE               =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
+# Project output. For more information please see Qt Help Project / Namespace
+# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace).
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_NAMESPACE          = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
+# Help Project output. For more information please see Qt Help Project / Virtual
+# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual-
+# folders).
+# The default value is: doc.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_VIRTUAL_FOLDER     = doc
+
+# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
+# filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_NAME   =
+
+# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_ATTRS  =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's filter section matches. Qt Help Project / Filter Attributes (see:
+# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_SECT_FILTER_ATTRS  =
+
+# The QHG_LOCATION tag can be used to specify the location of Qt's
+# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
+# generated .qhp file.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHG_LOCATION           =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
+# generated, together with the HTML files, they form an Eclipse help plugin. To
+# install this plugin and make it available under the help contents menu in
+# Eclipse, the contents of the directory containing the HTML and XML files needs
+# to be copied into the plugins directory of eclipse. The name of the directory
+# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
+# After copying Eclipse needs to be restarted before the help appears.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_ECLIPSEHELP   = NO
+
+# A unique identifier for the Eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have this
+# name. Each documentation set should have its own identifier.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
+
+ECLIPSE_DOC_ID         = org.doxygen.Project
+
+# If you want full control over the layout of the generated HTML pages it might
+# be necessary to disable the index and replace it with your own. The
+# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
+# of each HTML page. A value of NO enables the index and the value YES disables
+# it. Since the tabs in the index contain the same information as the navigation
+# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+DISABLE_INDEX          = NO
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information. If the tag
+# value is set to YES, a side panel will be generated containing a tree-like
+# index structure (just like the one that is generated for HTML Help). For this
+# to work a browser that supports JavaScript, DHTML, CSS and frames is required
+# (i.e. any modern browser). Windows users are probably better off using the
+# HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can
+# further fine-tune the look of the index. As an example, the default style
+# sheet generated by doxygen has an example that shows how to put an image at
+# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
+# the same information as the tab index, you could consider setting
+# DISABLE_INDEX to YES when enabling this option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_TREEVIEW      = YES
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
+# doxygen will group on one line in the generated HTML documentation.
+#
+# Note that a value of 0 will completely suppress the enum values from appearing
+# in the overview section.
+# Minimum value: 0, maximum value: 20, default value: 4.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+ENUM_VALUES_PER_LINE   = 4
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
+# to set the initial width (in pixels) of the frame in which the tree is shown.
+# Minimum value: 0, maximum value: 1500, default value: 250.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+TREEVIEW_WIDTH         = 250
+
+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to
+# external symbols imported via tag files in a separate window.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+EXT_LINKS_IN_WINDOW    = NO
+
+# Use this tag to change the font size of LaTeX formulas included as images in
+# the HTML documentation. When you change the font size after a successful
+# doxygen run you need to manually remove any form_*.png images from the HTML
+# output directory to force them to be regenerated.
+# Minimum value: 8, maximum value: 50, default value: 10.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_FONTSIZE       = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are not
+# supported properly for IE 6.0, but are supported on all modern browsers.
+#
+# Note that when changing this option you need to delete any form_*.png files in
+# the HTML output directory before the changes have effect.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_TRANSPARENT    = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
+# http://www.mathjax.org) which uses client side Javascript for the rendering
+# instead of using prerendered bitmaps. Use this if you do not have LaTeX
+# installed or if you want to formulas look prettier in the HTML output. When
+# enabled you may also need to install MathJax separately and configure the path
+# to it using the MATHJAX_RELPATH option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+USE_MATHJAX            = NO
+
+# When MathJax is enabled you can set the default output format to be used for
+# the MathJax output. See the MathJax site (see:
+# http://docs.mathjax.org/en/latest/output.html) for more details.
+# Possible values are: HTML-CSS (which is slower, but has the best
+# compatibility), NativeMML (i.e. MathML) and SVG.
+# The default value is: HTML-CSS.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_FORMAT         = HTML-CSS
+
+# When MathJax is enabled you need to specify the location relative to the HTML
+# output directory using the MATHJAX_RELPATH option. The destination directory
+# should contain the MathJax.js script. For instance, if the mathjax directory
+# is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
+# Content Delivery Network so you can quickly see the result without installing
+# MathJax. However, it is strongly recommended to install a local copy of
+# MathJax from http://www.mathjax.org before deployment.
+# The default value is: http://cdn.mathjax.org/mathjax/latest.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_RELPATH        = http://cdn.mathjax.org/mathjax/latest
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
+# extension names that should be enabled during MathJax rendering. For example
+# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_EXTENSIONS     =
+
+# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
+# of code that will be used on startup of the MathJax code. See the MathJax site
+# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
+# example see the documentation.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_CODEFILE       =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
+# the HTML output. The underlying search engine uses javascript and DHTML and
+# should work on any modern browser. Note that when using HTML help
+# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
+# there is already a search function so this one should typically be disabled.
+# For large projects the javascript based search engine can be slow, then
+# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
+# search using the keyboard; to jump to the search box use <access key> + S
+# (what the <access key> is depends on the OS and browser, but it is typically
+# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down
+# key> to jump into the search results window, the results can be navigated
+# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel
+# the search. The filter options can be selected when the cursor is inside the
+# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>
+# to select a filter and <Enter> or <escape> to activate or cancel the filter
+# option.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+SEARCHENGINE           = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a web server instead of a web client using Javascript. There
+# are two flavours of web server based searching depending on the
+# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for
+# searching and an index file used by the script. When EXTERNAL_SEARCH is
+# enabled the indexing and searching needs to be provided by external tools. See
+# the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SERVER_BASED_SEARCH    = NO
+
+# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
+# script for searching. Instead the search results are written to an XML file
+# which needs to be processed by an external indexer. Doxygen will invoke an
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain the
+# search results.
+#
+# Doxygen ships with an example indexer ( doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/).
+#
+# See the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH        = NO
+
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server
+# which will return the search results when EXTERNAL_SEARCH is enabled.
+#
+# Doxygen ships with an example indexer ( doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/). See the section "External Indexing and
+# Searching" for details.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHENGINE_URL       =
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
+# search data is written to a file for indexing by an external tool. With the
+# SEARCHDATA_FILE tag the name of this file can be specified.
+# The default file is: searchdata.xml.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHDATA_FILE        = searchdata.xml
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
+# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
+# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
+# projects and redirect the results back to the right project.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH_ID     =
+
+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
+# projects other than the one defined by this configuration file, but that are
+# all added to the same external search index. Each project needs to have a
+# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
+# to a relative location where the documentation can be found. The format is:
+# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTRA_SEARCH_MAPPINGS  =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES doxygen will generate LaTeX output.
+# The default value is: YES.
+
+GENERATE_LATEX         = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_OUTPUT           = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked.
+#
+# Note that when enabling USE_PDFLATEX this option is only used for generating
+# bitmaps for formulas in the HTML output, but not in the Makefile that is
+# written to the output directory.
+# The default file is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_CMD_NAME         = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
+# index for LaTeX.
+# The default file is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+MAKEINDEX_CMD_NAME     = makeindex
+
+# If the COMPACT_LATEX tag is set to YES doxygen generates more compact LaTeX
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+COMPACT_LATEX          = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used by the
+# printer.
+# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
+# 14 inches) and executive (7.25 x 10.5 inches).
+# The default value is: a4.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PAPER_TYPE             = a4
+
+# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
+# that should be included in the LaTeX output. To get the times font for
+# instance you can specify
+# EXTRA_PACKAGES=times
+# If left blank no extra packages will be included.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+EXTRA_PACKAGES         =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
+# generated LaTeX document. The header should contain everything until the first
+# chapter. If it is left blank doxygen will generate a standard header. See
+# section "Doxygen usage" for information on how to let doxygen write the
+# default header to a separate file.
+#
+# Note: Only use a user-defined header if you know what you are doing! The
+# following commands have a special meaning inside the header: $title,
+# $datetime, $date, $doxygenversion, $projectname, $projectnumber. Doxygen will
+# replace them by respectively the title of the page, the current date and time,
+# only the current date, the version number of doxygen, the project name (see
+# PROJECT_NAME), or the project number (see PROJECT_NUMBER).
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HEADER           =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
+# generated LaTeX document. The footer should contain everything after the last
+# chapter. If it is left blank doxygen will generate a standard footer.
+#
+# Note: Only use a user-defined footer if you know what you are doing!
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_FOOTER           =
+
+# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the LATEX_OUTPUT output
+# directory. Note that the files will be copied as-is; there are no commands or
+# markers available.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_FILES      =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
+# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
+# contain links (just like the HTML output) instead of page references. This
+# makes the output suitable for online browsing using a PDF viewer.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PDF_HYPERLINKS         = YES
+
+# If the LATEX_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
+# the PDF file directly from the LaTeX files. Set this option to YES to get a
+# higher quality PDF documentation.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+USE_PDFLATEX           = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
+# command to the generated LaTeX files. This will instruct LaTeX to keep running
+# if errors occur, instead of asking the user for help. This option is also used
+# when generating formulas in HTML.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BATCHMODE        = NO
+
+# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
+# index chapters (such as File Index, Compound Index, etc.) in the output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HIDE_INDICES     = NO
+
+# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
+# code with syntax highlighting in the LaTeX output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_SOURCE_CODE      = NO
+
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. See
+# http://en.wikipedia.org/wiki/BibTeX and \cite for more info.
+# The default value is: plain.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BIB_STYLE        = plain
+
+#---------------------------------------------------------------------------
+# Configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES doxygen will generate RTF output. The
+# RTF output is optimized for Word 97 and may not look too pretty with other RTF
+# readers/editors.
+# The default value is: NO.
+
+GENERATE_RTF           = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: rtf.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_OUTPUT             = rtf
+
+# If the COMPACT_RTF tag is set to YES doxygen generates more compact RTF
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+COMPACT_RTF            = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
+# contain hyperlink fields. The RTF file will contain links (just like the HTML
+# output) instead of page references. This makes the output suitable for online
+# browsing using Word or some other Word compatible readers that support those
+# fields.
+#
+# Note: WordPad (write) and others do not support links.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_HYPERLINKS         = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's config
+# file, i.e. a series of assignments. You only have to provide replacements,
+# missing definitions are set to their default value.
+#
+# See also section "Doxygen usage" for information on how to generate the
+# default style sheet that doxygen normally uses.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_STYLESHEET_FILE    =
+
+# Set optional variables used in the generation of an RTF document. Syntax is
+# similar to doxygen's config file. A template extensions file can be generated
+# using doxygen -e rtf extensionFile.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_EXTENSIONS_FILE    =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES doxygen will generate man pages for
+# classes and files.
+# The default value is: NO.
+
+GENERATE_MAN           = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it. A directory man3 will be created inside the directory specified by
+# MAN_OUTPUT.
+# The default directory is: man.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_OUTPUT             = man
+
+# The MAN_EXTENSION tag determines the extension that is added to the generated
+# man pages. In case the manual section does not start with a number, the number
+# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
+# optional.
+# The default value is: .3.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_EXTENSION          = .3
+
+# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
+# will generate one additional man file for each entity documented in the real
+# man page(s). These additional files only source the real man page, but without
+# them the man command would be unable to find the correct page.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_LINKS              = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES doxygen will generate an XML file that
+# captures the structure of the code including all documentation.
+# The default value is: NO.
+
+GENERATE_XML           = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: xml.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_OUTPUT             = xml
+
+# The XML_SCHEMA tag can be used to specify a XML schema, which can be used by a
+# validating XML parser to check the syntax of the XML files.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_SCHEMA             =
+
+# The XML_DTD tag can be used to specify a XML DTD, which can be used by a
+# validating XML parser to check the syntax of the XML files.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_DTD                =
+
+# If the XML_PROGRAMLISTING tag is set to YES doxygen will dump the program
+# listings (including syntax highlighting and cross-referencing information) to
+# the XML output. Note that enabling this will significantly increase the size
+# of the XML output.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_PROGRAMLISTING     = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the DOCBOOK output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_DOCBOOK tag is set to YES doxygen will generate Docbook files
+# that can be used to generate PDF.
+# The default value is: NO.
+
+GENERATE_DOCBOOK       = NO
+
+# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
+# front of it.
+# The default directory is: docbook.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_OUTPUT         = docbook
+
+#---------------------------------------------------------------------------
+# Configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES doxygen will generate an AutoGen
+# Definitions (see http://autogen.sf.net) file that captures the structure of
+# the code including all documentation. Note that this feature is still
+# experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_AUTOGEN_DEF   = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES doxygen will generate a Perl module
+# file that captures the structure of the code including all documentation.
+#
+# Note that this feature is still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_PERLMOD       = NO
+
+# If the PERLMOD_LATEX tag is set to YES doxygen will generate the necessary
+# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI
+# output from the Perl module output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_LATEX          = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be nicely
+# formatted so it can be parsed by a human reader. This is useful if you want to
+# understand what is going on. On the other hand, if this tag is set to NO the
+# size of the Perl module output will be much smaller and Perl will parse it
+# just the same.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_PRETTY         = YES
+
+# The names of the make variables in the generated doxyrules.make file are
+# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful
+# so different doxyrules.make files included by the same Makefile don't
+# overwrite each other's variables.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES doxygen will evaluate all
+# C-preprocessor directives found in the sources and include files.
+# The default value is: YES.
+
+ENABLE_PREPROCESSING   = YES
+
+# If the MACRO_EXPANSION tag is set to YES doxygen will expand all macro names
+# in the source code. If set to NO only conditional compilation will be
+# performed. Macro expansion can be done in a controlled way by setting
+# EXPAND_ONLY_PREDEF to YES.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+MACRO_EXPANSION        = YES
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
+# the macro expansion is limited to the macros specified with the PREDEFINED and
+# EXPAND_AS_DEFINED tags.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_ONLY_PREDEF     = NO
+
+# If the SEARCH_INCLUDES tag is set to YES the includes files in the
+# INCLUDE_PATH will be searched if a #include is found.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SEARCH_INCLUDES        = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by the
+# preprocessor.
+# This tag requires that the tag SEARCH_INCLUDES is set to YES.
+
+INCLUDE_PATH           =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will be
+# used.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+INCLUDE_FILE_PATTERNS  =
+
+# The PREDEFINED tag can be used to specify one or more macro names that are
+# defined before the preprocessor is started (similar to the -D option of e.g.
+# gcc). The argument of the tag is a list of macros of the form: name or
+# name=definition (no spaces). If the definition and the "=" are omitted, "=1"
+# is assumed. To prevent a macro definition from being undefined via #undef or
+# recursively expanded use the := operator instead of the = operator.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+PREDEFINED             = NFD_LOG_INIT(x)= \
+                         NFD_LOG_INCLASS_TEMPLATE_SPECIALIZATION_DEFINE(x,y)= \
+                         NFD_LOG_INCLASS_2TEMPLATE_SPECIALIZATION_DEFINE(x,y,z)= \
+                         BOOST_STATIC_ASSERT(x)=
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
+# tag can be used to specify a list of macro names that should be expanded. The
+# macro definition that is found in the sources will be used. Use the PREDEFINED
+# tag if you want to use a different macro definition that overrules the
+# definition found in the source code.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_AS_DEFINED      =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
+# remove all refrences to function-like macros that are alone on a line, have an
+# all uppercase name, and do not end with a semicolon. Such function macros are
+# typically used for boiler-plate code, and will confuse the parser if not
+# removed.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SKIP_FUNCTION_MACROS   = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES tag can be used to specify one or more tag files. For each tag
+# file the location of the external documentation should be added. The format of
+# a tag file without this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where loc1 and loc2 can be relative or absolute paths or URLs. See the
+# section "Linking to external documentation" for more information about the use
+# of tag files.
+# Note: Each tag file must have an unique name (where the name does NOT include
+# the path). If a tag file is not located in the directory in which doxygen is
+# run, you must also specify the path to the tagfile here.
+
+TAGFILES               =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
+# tag file that is based on the input files it reads. See section "Linking to
+# external documentation" for more information about the usage of tag files.
+
+GENERATE_TAGFILE       = NFD.tag
+
+# If the ALLEXTERNALS tag is set to YES all external class will be listed in the
+# class index. If set to NO only the inherited external classes will be listed.
+# The default value is: NO.
+
+ALLEXTERNALS           = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed in
+# the modules index. If set to NO, only the current project's groups will be
+# listed.
+# The default value is: YES.
+
+EXTERNAL_GROUPS        = YES
+
+# If the EXTERNAL_PAGES tag is set to YES all external pages will be listed in
+# the related pages index. If set to NO, only the current project's pages will
+# be listed.
+# The default value is: YES.
+
+EXTERNAL_PAGES         = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of 'which perl').
+# The default file (with absolute path) is: /usr/bin/perl.
+
+PERL_PATH              = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES doxygen will generate a class diagram
+# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
+# NO turns the diagrams off. Note that this option also works with HAVE_DOT
+# disabled, but it is recommended to install and use dot, since it yields more
+# powerful graphs.
+# The default value is: YES.
+
+CLASS_DIAGRAMS         = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see:
+# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH            =
+
+# If set to YES, the inheritance and collaboration graphs will hide inheritance
+# and usage relations if the target is undocumented or is not a class.
+# The default value is: YES.
+
+HIDE_UNDOC_RELATIONS   = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz (see:
+# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
+# Bell Labs. The other options in this section have no effect if this option is
+# set to NO
+# The default value is: NO.
+
+HAVE_DOT               = YES
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
+# to run in parallel. When set to 0 doxygen will base this on the number of
+# processors available in the system. You can set it explicitly to a value
+# larger than 0 to get control over the balance between CPU load and processing
+# speed.
+# Minimum value: 0, maximum value: 32, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_NUM_THREADS        = 0
+
+# When you want a differently looking font n the dot files that doxygen
+# generates you can specify the font name using DOT_FONTNAME. You need to make
+# sure dot is able to find the font, which can be done by putting it in a
+# standard location or by setting the DOTFONTPATH environment variable or by
+# setting DOT_FONTPATH to the directory containing the font.
+# The default value is: Helvetica.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTNAME           = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
+# dot graphs.
+# Minimum value: 4, maximum value: 24, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTSIZE           = 10
+
+# By default doxygen will tell dot to use the default font as specified with
+# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
+# the path where dot can find it using this tag.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTPATH           =
+
+# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
+# each documented class showing the direct and indirect inheritance relations.
+# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CLASS_GRAPH            = YES
+
+# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
+# graph for each documented class showing the direct and indirect implementation
+# dependencies (inheritance, containment, and class references variables) of the
+# class with other documented classes.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+COLLABORATION_GRAPH    = YES
+
+# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
+# groups, showing the direct groups dependencies.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GROUP_GRAPHS           = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LOOK               = YES
+
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
+# class node. If there are many fields or methods and many nodes the graph may
+# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
+# number of items for each type to make the size more manageable. Set this to 0
+# for no limit. Note that the threshold may be exceeded by 50% before the limit
+# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
+# but if the number exceeds 15, the total amount of fields shown is limited to
+# 10.
+# Minimum value: 0, maximum value: 100, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LIMIT_NUM_FIELDS   = 10
+
+# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
+# collaboration graphs will show the relations between templates and their
+# instances.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+TEMPLATE_RELATIONS     = YES
+
+# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
+# YES then doxygen will generate a graph for each documented file showing the
+# direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDE_GRAPH          = YES
+
+# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
+# set to YES then doxygen will generate a graph for each documented file showing
+# the direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDED_BY_GRAPH      = YES
+
+# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALL_GRAPH             = NO
+
+# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable caller graphs for selected
+# functions only using the \callergraph command.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALLER_GRAPH           = NO
+
+# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
+# hierarchy of all classes instead of a textual one.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GRAPHICAL_HIERARCHY    = YES
+
+# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
+# dependencies a directory has on other directories in a graphical way. The
+# dependency relations are determined by the #include relations between the
+# files in the directories.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DIRECTORY_GRAPH        = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot.
+# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
+# to make the SVG files visible in IE 9+ (other browsers do not have this
+# requirement).
+# Possible values are: png, jpg, gif and svg.
+# The default value is: png.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_IMAGE_FORMAT       = svg
+
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+#
+# Note that this requires a modern browser other than Internet Explorer. Tested
+# and working are Firefox, Chrome, Safari, and Opera.
+# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make
+# the SVG files visible. Older versions of IE do not have SVG support.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INTERACTIVE_SVG        = NO
+
+# The DOT_PATH tag can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_PATH               =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the \dotfile
+# command).
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOTFILE_DIRS           =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the \mscfile
+# command).
+
+MSCFILE_DIRS           =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
+# that will be shown in the graph. If the number of nodes in a graph becomes
+# larger than this value, doxygen will truncate the graph, which is visualized
+# by representing a node as a red box. Note that doxygen if the number of direct
+# children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that
+# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+# Minimum value: 0, maximum value: 10000, default value: 50.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_GRAPH_MAX_NODES    = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
+# generated by dot. A depth value of 3 means that only nodes reachable from the
+# root by following a path via at most 3 edges will be shown. Nodes that lay
+# further from the root node will be omitted. Note that setting this option to 1
+# or 2 may greatly reduce the computation time needed for large code bases. Also
+# note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+# Minimum value: 0, maximum value: 1000, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+MAX_DOT_GRAPH_DEPTH    = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not seem
+# to support this out of the box.
+#
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_TRANSPARENT        = YES
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10) support
+# this, this feature is disabled by default.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_MULTI_TARGETS      = NO
+
+# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
+# explaining the meaning of the various boxes and arrows in the dot generated
+# graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GENERATE_LEGEND        = YES
+
+# If the DOT_CLEANUP tag is set to YES doxygen will remove the intermediate dot
+# files that are used to generate the various graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_CLEANUP            = YES
diff --git a/NFD/docs/index.rst b/NFD/docs/index.rst
new file mode 100644
index 0000000..74f568d
--- /dev/null
+++ b/NFD/docs/index.rst
@@ -0,0 +1,60 @@
+NFD - Named Data Networking Forwarding Daemon
+=============================================
+
+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
+release, NFD will become a core component of the `NDN Platform
+<http://named-data.net/codebase/platform/>`__ and will follow the same release cycle.
+
+NFD Documentation
+-----------------
+
+.. toctree::
+   :hidden:
+   :maxdepth: 3
+
+   overview
+   INSTALL
+   FAQ
+   manpages
+
+* :doc:`overview`
+
+  A brief overview of NFD and its major modules.
+
+* :doc:`INSTALL`
+
+  Instructions for obtaining, installing, and running NFD.
+
+* :doc:`FAQ`
+
+  Suggestions for configuring and running non-standard NFD setups.
+
+* :doc:`manpages`
+
+**Additional documentation**
+
+* `NFD Developer's Guide <http://named-data.net/wp-content/uploads/2014/07/NFD-developer-guide.pdf>`_
+
+  A comprehensive guide to the design and implementation of NFD. The developer's guide also contains
+  suggestions and hints for anyone wanting to modify or extend NFD.
+
+* `NFD Wiki <http://redmine.named-data.net/projects/nfd/wiki>`_
+
+  + `NFD Management protocol <http://redmine.named-data.net/projects/nfd/wiki/Management>`_
+  + `NFD Configuration file format <http://redmine.named-data.net/projects/nfd/wiki/ConfigFileFormat>`_
+
+  The NFD Wiki contains detailed protocol specifications and
+  information for building on unsupported platforms.
+
+* `API Documentation (doxygen) <doxygen/annotated.html>`_
+
+* `Release Notes <RELEASE_NOTES.html>`_
+
+License
+-------
+
+NFD is an open and free software package licensed under GPL 3.0 license and is the
+centerpiece of our committement to making NDN's core technology open and free to all
+Internet users and developers. For more information about the licensing details and
+limitation, refer to `COPYING.md <https://github.com/named-data/NFD/blob/master/COPYING.md>`_.
diff --git a/NFD/docs/manpages.rst b/NFD/docs/manpages.rst
new file mode 100644
index 0000000..6a52897
--- /dev/null
+++ b/NFD/docs/manpages.rst
@@ -0,0 +1,18 @@
+.. _Manpages:
+
+Manpages
+========
+
+.. toctree::
+   manpages/nfd
+   manpages/nrd
+   manpages/nfdc
+   manpages/nfd-status
+   schema
+   manpages/nfd-status-http-server
+   manpages/ndn-autoconfig
+   manpages/ndn-autoconfig-server
+   manpages/nfd-autoreg
+   manpages/ndn-tlv-peek
+   manpages/ndn-tlv-poke
+   :maxdepth: 1
diff --git a/NFD/docs/manpages/ndn-autoconfig-server.rst b/NFD/docs/manpages/ndn-autoconfig-server.rst
new file mode 100644
index 0000000..c38b6c1
--- /dev/null
+++ b/NFD/docs/manpages/ndn-autoconfig-server.rst
@@ -0,0 +1,50 @@
+.. _ndn-autoconfig-server:
+
+ndn-autoconfig-server
+=====================
+
+Usage
+-----
+
+::
+
+    ndn-autoconfig-server [-h] [-p prefix] [-p prefix] ... FaceUri
+
+
+Description
+-----------
+
+``ndn-autoconfig-server`` is a daemon that implements server part for the stage 1 of
+:ref:`NDN hub discovery procedure`.
+
+This daemon essentially waits for Interests for ``/localhop/ndn-autoconf/hub`` and
+satisfies them with a Data packet that contains TLV-encoded FaceUri block.  The value of
+this block is the ``Uri`` for the HUB, preferrably a UDP tunnel.
+
+``-h``
+  print usage and exit.
+
+``FaceUri``
+  FaceUri for this NDN hub.
+
+``-p prefix``
+  A local prefix for which the local hub allow end applications to register prefix
+  (See more details in :ref:`local-prefix-discovery`).  One can supply more than one
+  prefixes.  All supplied prefixes will be put into the local prefix discovery data
+  as described in :ref:`local-prefix-discovery`.  If no prefix is specified,
+  auto-config-server will not serve any local prefix discovery data.
+
+Examples
+--------
+
+::
+
+    ndn-autoconfig-server tcp://spurs.cs.ucla.edu
+
+    ndn-autoconfig-server -p /ndn/edu/ucla tcp://spurs.cs.ucla.edu
+
+
+See also
+--------
+
+:ref:`ndn-autoconfig`
diff --git a/NFD/docs/manpages/ndn-autoconfig.rst b/NFD/docs/manpages/ndn-autoconfig.rst
new file mode 100644
index 0000000..8e56435
--- /dev/null
+++ b/NFD/docs/manpages/ndn-autoconfig.rst
@@ -0,0 +1,129 @@
+.. _ndn-autoconfig:
+
+ndn-autoconfig
+==============
+
+Usage
+-----
+
+::
+
+    ndn-autoconfig
+
+Description
+-----------
+
+Client tool to run :ref:`NDN hub discovery procedure`.
+
+.. _NDN hub discovery procedure:
+
+NDN hub discovery procedure
+---------------------------
+
+When an end host starts up, or detects a change in its network environment, it MAY use
+this procedure to discover a local or home NDN router, in order to gain connectivity to
+`the NDN research testbed <http://named-data.net/ndn-testbed/>`_.
+
+Overview
+^^^^^^^^
+
+This procedure contains three methods to discover a NDN router:
+
+1.  Look for a local NDN router by multicast.
+    This is useful in a home or small office network.
+
+2.  Look for a local NDN router by DNS query with default suffix.
+    This allows network administrator to configure a NDN router in a large enterprise network.
+
+3.  Connect to the home NDN router according to user certificate.
+    This ensures connectivity from anywhere.
+
+Stage 1: multicast discovery
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Request
++++++++
+
+The end host sends an Interest over a multicast face.
+
+Interest Name is ``/localhop/ndn-autoconf/hub``.
+
+Response
+++++++++
+
+A producer app on the HUB answer this Interest with a Data packet that contains a
+TLV-encoded `Uri` block.  The value of this block is the URI for the HUB, preferrably a
+UDP tunnel.
+
+Stage 2: DNS query with default suffix
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Request
++++++++
+
+The end host sends a DNS query that is equivalent to this command::
+
+    dig +search +short +cmd +tries=2 +ndots=10 _ndn._udp srv
+
+Response
+++++++++
+
+The DNS server should answer with an SRV record that contains the hostname and UDP port
+number of the NDN router.
+
+Stage 3: find home router
+^^^^^^^^^^^^^^^^^^^^^^^^^
+
+This stage assumes that user has configured default certificate using
+`<http://ndncert.named-data.net/>`_ as described in `Certification Architecture
+<http://redmine.named-data.net/attachments/download/23/CertificationArchitecture.pptx>`_.
+
+Request
++++++++
+
+The end host loads the default user identity (eg. ``/ndn/edu/ucla/cs/afanasev``), and
+converts it to DNS format.
+
+The end host sends a DNS query for an SRV record of name ``_ndn._udp.`` + user identity in
+DNS format + ``_homehub._autoconf.named-data.net``. For example::
+
+    _ndn._udp.afanasev.cs.ucla.edu.ndn._homehub._autoconf.named-data.net
+
+Response
+++++++++
+
+The DNS server should answer with an SRV record that contains the hostname and UDP port
+number of the home NDN router of this user's site.
+
+Client procedure
+----------------
+
+Stage 1
+^^^^^^^
+
+Send a multicast discovery Interest.
+
+If this Interest is answered, connect to the HUB and terminate auto-discovery.
+
+Stage 2
+^^^^^^^
+
+Send a DNS query with default suffix.
+
+If this query is answered, connect to the HUB and terminate auto-discovery.
+
+Stage 3
+^^^^^^^
+
+* Load default user identity, and convert it to DNS format; if either fails, the
+  auto-discovery fails.
+
+* Send a DNS query to find home HUB.
+  If this query is answered, connect to the home HUB and terminate auto-discovery.
+  Otherwise, the auto-discovery fails.
+
+
+See also
+--------
+
+:ref:`ndn-autoconfig-server`
diff --git a/NFD/docs/manpages/ndn-tlv-peek.rst b/NFD/docs/manpages/ndn-tlv-peek.rst
new file mode 100644
index 0000000..9b2a7e4
--- /dev/null
+++ b/NFD/docs/manpages/ndn-tlv-peek.rst
@@ -0,0 +1,56 @@
+ndn-tlv-peek
+============
+
+Usage
+-----
+
+::
+
+    ndn-tlv-peek [-h] [-f] [-r] [-m min] [-M max] [-l lifetime] [-p] [-w timeout] name
+
+Description
+-----------
+
+``ndn-tlv-peek`` is a simple consumer program that sends one Interest and expects one Data
+packet in response.  The full Data packet (in TLV format) is written to stdout.  The
+program terminates with return code 0 if Data arrives, and with return code 1 when timeout
+occurs.
+
+``name`` is interpreted as the Interest name.
+
+Options
+-------
+
+``-h``
+  Print help and exit
+
+``-f``
+  If specified, set ``MustBeFresh`` selector in the Interest packet.
+
+``-r``
+  Set ``ChildSelector=1`` (the rightmost child) selector.
+
+``-m``
+  Set ``min`` as the ``MinSuffixComponents`` selector.
+
+``-M``
+  Set ``max`` as the ``MaxSuffixComponents`` selector.
+
+``-l``
+  Set ``lifetime`` (in milliseconds) as the ``InterestLifetime``.
+
+``-p``
+  If specified, print the received payload only, not the full packet.
+
+``-w``
+  Timeout after ``timeout`` milliseconds.
+
+
+Examples
+--------
+
+Send Interest for ``ndn:/app1/video`` and print the received payload only
+
+::
+
+    ndn-tlv-peek -p ndn:/app1/video
diff --git a/NFD/docs/manpages/ndn-tlv-poke.rst b/NFD/docs/manpages/ndn-tlv-poke.rst
new file mode 100644
index 0000000..73acb82
--- /dev/null
+++ b/NFD/docs/manpages/ndn-tlv-poke.rst
@@ -0,0 +1,54 @@
+ndn-tlv-poke
+============
+
+Usage
+-----
+
+::
+
+    ndn-tlv-poke [-h] [-f] [-D] [-i identity] [-F] [-x freshness] [-w timeout] name
+
+Description
+-----------
+
+``ndn-tlv-poke`` is a simple producer program that reads payload from stdin and publishes it
+as a single NDN Data packet.  The Data packet is published either as a response to the
+incoming Interest for the given ``name``, or forcefully pushed to the local NDN
+forwarder's cache if ``-f`` flag is specified.
+
+The program terminates with return code 0 if Data is sent and with return code 1 when
+timeout occurs.
+
+Options
+-------
+
+``-h``
+  Print usage and exit.
+
+``-f``
+  If specified, send Data without waiting for Interest.
+
+``-D``
+  If specified, use ``DigestSha256`` signature instead of default ``SignatureSha256WithRsa``.
+
+``-i``
+  Use ``identity`` to sign the created Data packet.
+
+``-F``
+  Set ``FinalBlockId`` to the last component of specified name.
+
+``-x``
+  Set ``FreshnessPeriod`` in milliseconds.
+
+``-w``
+  Wait at most ``timeout`` milliseconds for the incoming Interest.  If no Interest arrives
+  within the ``timeout``, the Data packet will not be published.
+
+
+Examples
+--------
+
+Create Data packet with content ``hello`` with the name ``ndn:/app/video`` and wait at
+most 3 seconds for the incoming Interest for it::
+
+    echo "Hello" | build/bin/ndn-tlv-poke -w 3000 ndn:/app/video
diff --git a/NFD/docs/manpages/nfd-autoreg.rst b/NFD/docs/manpages/nfd-autoreg.rst
new file mode 100644
index 0000000..168528d
--- /dev/null
+++ b/NFD/docs/manpages/nfd-autoreg.rst
@@ -0,0 +1,57 @@
+ndn-autoreg
+===========
+
+Usage
+-----
+
+::
+
+    nfd-autoreg --prefix=</autoreg/prefix> [--prefix=</another/prefix>] ...
+
+Description
+-----------
+
+``autoreg-server`` is a deamon application that automatically registers the specified
+prefix(es) when new on-demand Face is created (i.e., when a new UDP face is created
+as a result of an incoming packet or TCP face is created as a result of an incoming
+connection).
+
+Options
+-------
+
+``-h`` or ``--help``
+  Produce help message.
+
+``-i`` or ``--prefix``
+  Prefix that should be automatically registered when a new remote face is created.
+  Can be repeated multiple to specify additional prefixes.
+
+``-c`` or ``--cost``
+  RIB cost to be assigned to auto-registered prefixes.   if not specified, default cost
+  is set to 255.
+
+``-w`` or ``--whitelist``
+  Whitelisted network, e.g., 192.168.2.0/24 or ::1/128.   Can be repeated multiple times
+  to specify multiple whitelisted networks.
+
+  Prefix(es) will be auto-registered only when remote IP address is within the specified
+  range(s), except blacklist ranges.
+
+  Default: 0.0.0.0/0 and ::/0
+
+``-b`` or ``--blacklist``
+  Blacklisted network, e.g., 192.168.2.32/30 or ::1/128.  Can be repeated multiple times
+  to specify multiple blacklisted networks.
+
+  Prefix(es) will be auto-registered only when remote IP address in **NOT** within the
+  specified range(s), but is within the range define by the whitelist(s).
+
+  Default: none
+
+Examples
+--------
+
+Auto-register two prefixes for any newly created on-demand Face, except those that has
+source IP address in ``10.0.0.0/8`` network::
+
+    nfd-autoreg --prefix=/app1/video --prefix=/app2/pictures -b 10.0.0.0/8
diff --git a/NFD/docs/manpages/nfd-status-http-server.rst b/NFD/docs/manpages/nfd-status-http-server.rst
new file mode 100644
index 0000000..a6aa0eb
--- /dev/null
+++ b/NFD/docs/manpages/nfd-status-http-server.rst
@@ -0,0 +1,43 @@
+nfd-status-http-server
+======================
+
+Usage
+-----
+
+::
+
+    nfd-status-http-server [-h] [-p port number] [-a IP address] [-r] [-v]
+
+Description
+-----------
+
+``nfd-status-http-server`` is a daemon that enables retrieval of NFD status via HTTP protocol.
+
+Options
+-------
+
+``-h``
+  Show this help message and exit.
+
+``-p``
+  HTTP server port number (default is 8080).
+
+``-a``
+  HTTP server IP address (default is 127.0.0.1).
+
+``-r``
+  Enable HTTP robots to crawl (disabled by default).
+
+``-v``
+  Verbose mode.
+
+Examples
+--------
+
+Enable NFD HTTP status server on all IPv4 interfaces::
+
+    nfd-status-http-server -p 80 -a 0.0.0.0
+
+Enable NFD HTTP status server on all IPv6 interfaces::
+
+    nfd-status-http-server -p 80 -a ::
diff --git a/NFD/docs/manpages/nfd-status.rst b/NFD/docs/manpages/nfd-status.rst
new file mode 100644
index 0000000..4fabe2e
--- /dev/null
+++ b/NFD/docs/manpages/nfd-status.rst
@@ -0,0 +1,99 @@
+nfd-status
+==========
+
+Usage
+-----
+
+::
+
+    nfd-status [options]
+
+Description
+-----------
+
+``nfd-status`` is a tool to retrieve and print out NFD version and status information.
+
+Options:
+--------
+
+``-h``
+  Print usage information.
+
+``-v``
+  Retrieve version information.
+
+``-c``
+  Retrieve channel status information.
+
+``-f``
+  Retrieve face status information.
+
+``-b``
+  Retrieve FIB information.
+
+``-r``
+  Retrieve RIB information.
+
+``-s``
+  Retrieve configured strategy choice for NDN namespaces.
+
+``-x``
+  Output NFD status information in XML format.
+
+``-V``
+  Show version information of nfd-status and exit.
+
+If no options are provided, all information is retrieved.
+
+If -x is provided, other options(-v, -c, etc.) are ignored, and all information is printed in XML format.
+
+Examples
+--------
+
+Get all status information from NFD::
+
+    $ nfd-status
+
+    General NFD status:
+                     nfdId=/chengyu/KEY/ksk-1405136377018/ID-CERT
+                   version=2000
+                 startTime=20140725T232341.374000
+               currentTime=20140725T233240
+                    uptime=538 seconds
+          nNameTreeEntries=10
+               nFibEntries=3
+               nPitEntries=2
+      nMeasurementsEntries=0
+                nCsEntries=56
+              nInInterests=55
+             nOutInterests=54
+                  nInDatas=56
+                 nOutDatas=47
+    Channels:
+      ws://[::]:9696
+      unix:///private/var/run/nfd.sock
+      udp6://[::]:6363
+      udp4://0.0.0.0:6363
+      tcp6://[::]:6363
+      tcp4://0.0.0.0:6363
+    Faces:
+      faceid=1 remote=internal:// local=internal:// counters={in={0i 52d 0B} out={51i 0d 0B}} local
+      faceid=254 remote=contentstore:// local=contentstore:// counters={in={0i 0d 0B} out={0i 0d 0B}} local
+      faceid=255 remote=null:// local=null:// counters={in={0i 0d 0B} out={0i 0d 0B}} local
+      faceid=256 remote=udp4://224.0.23.170:56363 local=udp4://129.82.138.211:56363 counters={in={0i 0d 0B} out={0i 0d 0B}}
+      faceid=257 remote=udp4://224.0.23.170:56363 local=udp4://127.0.0.1:56363 counters={in={0i 0d 0B} out={0i 0d 0B}}
+      faceid=258 remote=ether://[01:00:5e:00:17:aa] local=dev://bridge0 counters={in={0i 0d 0B} out={0i 0d 0B}}
+      faceid=259 remote=ether://[01:00:5e:00:17:aa] local=dev://en0 counters={in={0i 0d 0B} out={0i 0d 0B}}
+      faceid=260 remote=ether://[01:00:5e:00:17:aa] local=dev://en1 counters={in={0i 0d 0B} out={0i 0d 0B}}
+      faceid=261 remote=ether://[01:00:5e:00:17:aa] local=dev://en2 counters={in={0i 0d 0B} out={0i 0d 0B}}
+      faceid=262 remote=fd://30 local=unix:///private/var/run/nfd.sock counters={in={24i 6d 4880B} out={6i 16d 8417B}} local on-demand
+      faceid=268 remote=fd://31 local=unix:///private/var/run/nfd.sock counters={in={1i 0d 410B} out={0i 1d 764B}} local on-demand
+      faceid=269 remote=fd://32 local=unix:///private/var/run/nfd.sock counters={in={3i 0d 137B} out={0i 2d 925B}} local on-demand
+    FIB:
+      /localhost/nfd nexthops={faceid=1 (cost=0)}
+      /example/testApp nexthops={faceid=268 (cost=0)}
+      /localhost/nfd/rib nexthops={faceid=262 (cost=0)}
+    RIB:
+      /example/testApp route={faceid=268 (origin=0 cost=0 flags=1)}
+    Strategy choices:
+      / strategy=/localhost/nfd/strategy/best-route
diff --git a/NFD/docs/manpages/nfd.rst b/NFD/docs/manpages/nfd.rst
new file mode 100644
index 0000000..0fa0ef8
--- /dev/null
+++ b/NFD/docs/manpages/nfd.rst
@@ -0,0 +1,38 @@
+nfd
+===
+
+Usage
+-----
+
+::
+
+    nfd [options]
+
+
+Description
+-----------
+
+NFD forwarding daemon.
+
+
+Options:
+--------
+
+``--help``
+  Print this help message.
+
+``--modules``
+  List available logging modules
+
+``--config <path/to/nfd.conf>``
+  Specify the path to nfd configuration file (default: ``${SYSCONFDIR}/ndn/nfd.conf``).
+
+Examples
+--------
+
+Start NFD forwarding daemon as super user and use a configuration file from the current
+working directory.
+
+::
+
+    sudo nfd --config nfd.conf
diff --git a/NFD/docs/manpages/nfdc.rst b/NFD/docs/manpages/nfdc.rst
new file mode 100644
index 0000000..67d691c
--- /dev/null
+++ b/NFD/docs/manpages/nfdc.rst
@@ -0,0 +1,190 @@
+nfdc
+====
+
+Usage
+-----
+
+::
+
+    nfdc [-h] COMMAND [<command options>]
+
+
+Description
+-----------
+
+``nfdc`` is a tool to manipulate routing information base (RIB), forwarding information
+base (FIB), and StrategyChoices table (i.e., which strategy should be used by which
+namespaces).
+
+Options
+-------
+
+``-h``
+  Print usage information.
+
+``COMMAND``
+
+  ``register``
+    Register a new or update existing routing entry in Routing Information Base (RIB).
+
+    ``register [-I] [-C] [-c <cost>] [-e expiration time] [-o origin] <prefix> <faceId | faceUri>``
+
+      ``-I``
+        Unset CHILD_INHERIT flag from the routing entry.
+
+      ``-C``
+        Set CAPTURE flag in the routing entry.
+
+      ``-c <cost>``
+        Cost for the RIB entry (default is 0).
+
+      ``-e <expiration time>``
+        Expiration time of the RIB entry in milliseconds. If not specified, the entry remains in FIB
+        for the lifetime of the associated face.
+
+       ``-o <origin>``
+        Origin of the registration request (default is 255).
+        0 for Local producer applications, 128 for NLSR, 255 for static routes.
+
+      ``prefix``
+        A prefix of an existing or to be created RIB entry, for which routing entry is
+        requested to be added or updated.
+
+      ``faceId``
+        An existing NFD Face ID number, which can be obtained, for example, using
+        ``nfd-status`` command.
+
+      ``faceUri``
+        URI of the existing or to be created Face.
+
+  ``unregister``
+    Unregister an existing routing entry from Routing Information Base (RIB).
+
+    ``unregister [-o origin] <prefix> <faceId>``
+
+       ``-o <origin>``
+        Origin of the unregistration request (default is 255).
+
+       ``prefix``
+        A prefix of an existing RIB entry, from which routing entry is requested to be
+        removed.
+
+      ``faceId``
+        An existing NFD Face ID number, which can be obtained, for example, using
+        ``nfd-status`` command.
+
+  ``create``
+    Create a UDP unicast or TCP Face
+
+    ``create <faceUri>``
+
+      ``faceUri``
+        UDP unicast or TCP Face URI::
+
+            UDP unicast:    udp[4|6]://<remote-IP-or-host>[:<remote-port>]
+            TCP:            tcp[4|6]://<remote-IP-or-host>[:<remote-port>]
+
+  ``destroy``
+    Create an existing UDP unicast or TCP Face.
+
+    ``destroy <faceId | faceUri>``
+
+      ``faceId``
+        An existing NFD Face ID number, which can be obtained, for example, using
+        ``nfd-status`` command.
+
+      ``faceUri``
+        UDP unicast or TCP Face URI::
+
+            UDP unicast:    udp[4|6]://<remote-IP-or-host>[:<remote-port>]
+            TCP:            tcp[4|6]://<remote-IP-or-host>[:<remote-port>]
+
+  ``set-strategy``
+    Select strategy to be used for the specified namespace
+
+    ``set-strategy <namespace> <strategy-name>``
+
+      ``namespace``
+        Namespace that will use the specified strategy.
+
+        Note that more specific namespace(s) can use different strategy or strategies.
+        For example, if namespace ``/A/B/C`` was using strategy
+        ``ndn:/localhost/nfd/strategy/best-route`` before running ``set-strategy`` on
+        ``/A`` namespace, it will continue using the same strategy no matter which
+        namespace was specified for ``/A``.
+
+      ``strategy-name``
+        Name of one of the available strategies.
+
+        Currently, NFD supports the following strategies::
+
+            ndn:/localhost/nfd/strategy/best-route
+            ndn:/localhost/nfd/strategy/broadcast
+            ndn:/localhost/nfd/strategy/client-control
+            ndn:/localhost/nfd/strategy/ncc
+
+  ``unset-strategy``
+    Unset the strategy for a given ``namespace``.
+
+    Effectively, this command select parent's namespace strategy to be used for the
+    specified ``namespace``.
+
+    ``unset-strategy <namespace>``
+
+      ``namespace``
+        Namespace from which namespace customization should be removed.
+
+  ``add-nexthop``
+    Directly add nexthop entry info NFD's Forwarding Information Base (FIB).  This command
+    is intended only for debugging purposes.  Normally, prefix-nexhop association should
+    be registered in Routing Information Base using ``register`` command.
+
+    ``add-nexthop [-c <cost>] <prefix> <faceId | faceUri>``
+
+      ``-c <cost>``
+        Cost for the nexthop entry to be inserted (default is 0).
+
+      ``prefix``
+        A prefix of an existing or to be created FIB entry, to which nexthop
+        entry is requested to be added.
+
+      ``faceId``
+        An existing NFD Face ID number, which can be obtained, for example, using
+        ``nfd-status`` command
+
+      ``faceUri``
+        URI of the existing or to be created Face.
+
+  ``remove-nexthop``
+    Directly remove nexthop entry from NFD'S FIB.  This command
+    is intended only for debugging purposes.  Normally, prefix-nexhop association should
+    be unregistered from Routing Information Base using ``unregister`` command.
+
+    ``remove-nexthop <prefix> <faceId>``
+
+      ``prefix``
+        A prefix of an existing FIB entry, from which nexthop entry is requested to be removed.
+
+      ``faceId``
+        An existing NFD Face ID number, which can be obtained, for example, using
+        ``nfd-status`` command.
+
+        Note that when ``faceId`` is the last Face associated with ``prefix`` FIB entry,
+        the whole FIB entry will be removed.
+
+
+
+Examples
+--------
+
+Add a namespace to a face uri:
+
+::
+
+    nfdc register ndn:/app1/video udp://192.168.1.2
+
+Set strategy to a name:
+
+::
+
+    nfdc set-strategy ndn:/app1/video ndn:/localhost/nfd/strategy/broadcast
diff --git a/NFD/docs/manpages/nrd.rst b/NFD/docs/manpages/nrd.rst
new file mode 100644
index 0000000..31fb384
--- /dev/null
+++ b/NFD/docs/manpages/nrd.rst
@@ -0,0 +1,34 @@
+NRD (NFD RIB Daemon)
+====================
+
+Usage
+-----
+
+::
+
+    nrd [options]
+
+
+Description
+-----------
+
+NRD is a complementary daemon designed to work in parallel with NFD and to provide
+services for Routing Information Base management and constructing NFD's FIB:
+
+* prefix registration from applications;
+* manual prefix registration;
+* prefix registrations from multiple dynamic routing protocols.
+
+NRD's runtime settings may be modified via `rib_*` sections in NFD's configuration file.
+
+Options:
+--------
+
+``--help``
+  Print this help message.
+
+``--modules``
+  List available logging modules
+
+``--config <path/to/nfd.conf>``
+  Specify the path to nfd configuration file (default: ``${SYSCONFDIR}/ndn/nfd.conf``).
diff --git a/NFD/docs/misc/local-prefix-discovery.rst b/NFD/docs/misc/local-prefix-discovery.rst
new file mode 100644
index 0000000..f1bca84
--- /dev/null
+++ b/NFD/docs/misc/local-prefix-discovery.rst
@@ -0,0 +1,32 @@
+.. _local-prefix-discovery:
+
+Discover local hub prefix
+=========================
+
+Some applications need to discover prefix(es) under which they can publish data
+/ which Interests local hub will be able to forward down to the application.
+In order to discover that, applications need to send an interest for
+``/localhop/ndn-autoconf/routable-prefixes`` prefix. Response data to the
+interest contains a list of prefixes and should be encoded as:
+
+::
+
+    Response ::= DATA-TYPE TLV-LENGTH
+                 Name (= /localhop/ndn-autoconf/routable-prefixes/[version])
+                 MetaInfo (= ResponseMetaInfo)
+                 Content (= ResponseContent)
+                 Signature
+
+    ResponseMetaInfo ::= META-INFO-TYPE TLV-LENGTH
+                         ContentType (= DATA)
+                         FreshnessPeriod (= 5000)
+
+    ResponseContent ::= Name+
+
+.. note::
+ResponseContent should contain at least one Name, which should be routable
+towards the face from which the request has been received.  The requester may
+process list of the returned names and pick whichever it wants to use.
+
+For now, the ``/localhop/ndn-autoconf/routable-prefixes`` data is served by
+:ref:`ndn-autoconfig-server`.
diff --git a/NFD/docs/named_data_theme/layout.html b/NFD/docs/named_data_theme/layout.html
new file mode 100644
index 0000000..16ae50f
--- /dev/null
+++ b/NFD/docs/named_data_theme/layout.html
@@ -0,0 +1,85 @@
+{#
+    named_data_theme/layout.html
+    ~~~~~~~~~~~~~~~~~
+#}
+{% extends "basic/layout.html" %}
+
+{% block header %}
+    <!--headercontainer-->
+    <div id="header_container">
+
+        <!--header-->
+        <div class="row">
+             <div class="three columns">
+                  <div id="logo">
+                        <a href="http://named-data.net" title="A Future Internet Architecture"><img src="http://named-data.net/wp-content/uploads/cropped-20130722_Logo2.png" alt="" /></a>
+                  </div><!--logo end-->
+             </div>
+
+             <!--top menu-->
+             <div class="nine columns" id="menu_container" >
+               <h1><a href="{{ pathto(master_doc) }}">{{ shorttitle|e }}</a></h1>
+             </div>
+        </div>
+    </div><!--header container end-->
+
+{% endblock %}
+
+{% block content %}
+    <div class="content-wrapper">
+      <div class="content">
+        <div class="document">
+          {%- block document %}
+            {{ super() }}
+          {%- endblock %}
+        </div>
+        <div class="sidebar">
+          {%- block sidebartoc %}
+          <h3>{{ _('Table Of Contents') }}</h3>
+          {{ toctree(includehidden=True) }}
+
+          <h3>{{ _('Additional documenation') }}</h3>
+          <ul>
+            <li class="toctree-l1"><a class="reference external" href="http://redmine.named-data.net/projects/nfd/wiki">NFD Wiki</a></li>
+            <li class="toctree-l1"><a class="reference internal" href="doxygen/annotated.html">API documentation (doxygen)</a></li>
+          </ul>
+          {%- endblock %}
+          {%- block sidebarsearch %}
+          <h3 style="margin-top: 1.5em;">{{ _('Search') }}</h3>
+          <form class="search" action="{{ pathto('search') }}" method="get">
+            <input type="text" name="q" />
+            <input type="submit" value="{{ _('Go') }}" />
+            <input type="hidden" name="check_keywords" value="yes" />
+            <input type="hidden" name="area" value="default" />
+          </form>
+          <p class="searchtip" style="font-size: 90%">
+            {{ _('Enter search terms or a module, class or function name.') }}
+          </p>
+          {%- endblock %}
+        </div>
+        <div class="clearer"></div>
+      </div>
+    </div>
+{% endblock %}
+
+{% block footer %}
+    <div id="footer-container">
+        <!--footer container-->
+        <div class="row">
+        </div><!-- footer container-->
+    </div>
+
+    <div id="footer-info">
+        <!--footer container-->
+        <div class="row">
+            <div class="twelve columns">
+
+                <div id="copyright">This research is partially supported by NSF (Award <a href="http://www.nsf.gov/awardsearch/showAward?AWD_ID=1040868" target="_blank>">CNS-1040868</a>)<br/><br/><a rel="license" href="http://creativecommons.org/licenses/by/3.0/deed.en_US" target="_blank">Creative Commons Attribution 3.0 Unported License</a> except where noted.</div>
+
+            </div>
+        </div>
+    </div><!--footer info end-->
+{% endblock %}
+
+{% block relbar1 %}{% endblock %}
+{% block relbar2 %}{% endblock %}
diff --git a/NFD/docs/named_data_theme/named_data_footer-with-analytics.html.in b/NFD/docs/named_data_theme/named_data_footer-with-analytics.html.in
new file mode 100644
index 0000000..68a4a9d
--- /dev/null
+++ b/NFD/docs/named_data_theme/named_data_footer-with-analytics.html.in
@@ -0,0 +1,32 @@
+<!-- start footer part -->
+<!--BEGIN GENERATE_TREEVIEW-->
+<div id="nav-path" class="navpath"><!-- id is needed for treeview function! -->
+  <ul>
+    $navpath
+    <li class="footer">$generatedby
+    <a href="http://www.doxygen.org/index.html">
+    <img class="footer" src="$relpath$doxygen.png" alt="doxygen"/></a> $doxygenversion </li>
+  </ul>
+</div>
+<!--END GENERATE_TREEVIEW-->
+<!--BEGIN !GENERATE_TREEVIEW-->
+<hr class="footer"/>
+<address class="footer"><small>
+$generatedby &#160;<a href="http://www.doxygen.org/index.html">
+<img class="footer" src="$relpath$doxygen.png" alt="doxygen"/>
+</a> $doxygenversion
+</small></address>
+<!--END !GENERATE_TREEVIEW-->
+
+<script>
+  (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
+  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
+  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
+  })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
+
+  ga('create', '@GOOGLE_ANALYTICS@', 'auto');
+  ga('send', 'pageview');
+</script>
+
+</body>
+</html>
diff --git a/NFD/docs/named_data_theme/named_data_footer.html b/NFD/docs/named_data_theme/named_data_footer.html
new file mode 100644
index 0000000..77fc327
--- /dev/null
+++ b/NFD/docs/named_data_theme/named_data_footer.html
@@ -0,0 +1,24 @@
+<!-- start footer part -->
+<!--BEGIN GENERATE_TREEVIEW-->
+<div id="nav-path" class="navpath"><!-- id is needed for treeview function! -->
+  <ul>
+    $navpath
+    <li class="footer">$generatedby
+    <a href="http://www.doxygen.org/index.html">
+    <img class="footer" src="$relpath$doxygen.png" alt="doxygen"/></a> $doxygenversion </li>
+  </ul>
+</div>
+<!--END GENERATE_TREEVIEW-->
+<!--BEGIN !GENERATE_TREEVIEW-->
+<hr class="footer"/>
+<address class="footer"><small>
+$generatedby &#160;<a href="http://www.doxygen.org/index.html">
+<img class="footer" src="$relpath$doxygen.png" alt="doxygen"/>
+</a> $doxygenversion
+</small></address>
+<!--END !GENERATE_TREEVIEW-->
+
+<script type="text/javascript">
+</script>
+</body>
+</html>
diff --git a/NFD/docs/named_data_theme/named_data_header.html b/NFD/docs/named_data_theme/named_data_header.html
new file mode 100644
index 0000000..97b2932
--- /dev/null
+++ b/NFD/docs/named_data_theme/named_data_header.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta http-equiv="X-UA-Compatible" content="IE=9"/>
+<!--BEGIN PROJECT_NAME--><title>$projectname: $title</title><!--END PROJECT_NAME-->
+<!--BEGIN !PROJECT_NAME--><title>$title</title><!--END !PROJECT_NAME-->
+<link href="$relpath$tabs.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="$relpath$jquery.js"></script>
+<script type="text/javascript" src="$relpath$dynsections.js"></script>
+$treeview
+$search
+$mathjax
+<link href="$relpath$doxygen.css" rel="stylesheet" type="text/css"/>
+<link href="$relpath$named_data_doxygen.css" rel="stylesheet" type="text/css" />
+<link href="$relpath$favicon.ico" rel="shortcut icon" type="image/ico" />
+</head>
+<body>
+<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
+
+<!--BEGIN TITLEAREA-->
+<!--headercontainer-->
+<div id="header_container">
+
+    <!--header-->
+    <div class="row">
+         <div class="three columns">
+              <div id="logo">
+                    <a href="http://named-data.net" title="A Future Internet Architecture"><img src="http://named-data.net/wp-content/uploads/cropped-20130722_Logo2.png" alt="" /></a>
+              </div><!--logo end-->
+         </div>
+
+         <!--top menu-->
+         <div class="nine columns" id="menu_container" >
+           <h1><a href="http://named-data.net/doc/NFD/$projectnumber/">$projectname $projectnumber documentation</a></h1>
+         </div>
+    </div>
+</div><!--header container end-->
+<!--END TITLEAREA-->
+
+<!-- end header part -->
diff --git a/NFD/docs/named_data_theme/static/bar-top.png b/NFD/docs/named_data_theme/static/bar-top.png
new file mode 100644
index 0000000..07cafb6
--- /dev/null
+++ b/NFD/docs/named_data_theme/static/bar-top.png
Binary files differ
diff --git a/NFD/docs/named_data_theme/static/base.css b/NFD/docs/named_data_theme/static/base.css
new file mode 100644
index 0000000..164d1c1
--- /dev/null
+++ b/NFD/docs/named_data_theme/static/base.css
@@ -0,0 +1,71 @@
+* {
+  margin: 0px;
+  padding: 0px;
+}
+
+html { font-size: 62.5%; }
+
+body {
+  font-family: "Verdana", Arial, sans-serif;
+  background-color: #eeeeec;
+  color: #777;
+  border-top: 4px solid #fd7800;
+}
+
+body { background: white; font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; font-size: 14px; line-height: 1; color: #222222; position: relative; -webkit-font-smoothing: antialiased; }
+
+.clearer {
+  clear: both;
+}
+
+.left {
+  float: left;
+}
+
+.right {
+  float: right;
+}
+
+.line-block {
+    display: block;
+    margin-top: 1em;
+    margin-bottom: 1em;
+}
+
+.line-block .line-block {
+    margin-top: 0;
+    margin-bottom: 0;
+    margin-left: 1.5em;
+}
+
+h1, h2, h3, h4 {
+  font-family: "Georgia", "Times New Roman", serif;
+  font-weight: normal;
+  color: #3465a4;
+  margin-bottom: .8em;
+}
+
+h1 {
+  color: #204a87;
+}
+
+h2 {
+  padding-bottom: .5em;
+  border-bottom: 1px solid #3465a4;
+}
+
+a.headerlink {
+  visibility: hidden;
+  color: #dddddd;
+  padding-left: .3em;
+}
+
+h1:hover > a.headerlink,
+h2:hover > a.headerlink,
+h3:hover > a.headerlink,
+h4:hover > a.headerlink,
+h5:hover > a.headerlink,
+h6:hover > a.headerlink,
+dt:hover > a.headerlink {
+  visibility: visible;
+}
diff --git a/NFD/docs/named_data_theme/static/base.css_t b/NFD/docs/named_data_theme/static/base.css_t
new file mode 100644
index 0000000..eed3973
--- /dev/null
+++ b/NFD/docs/named_data_theme/static/base.css_t
@@ -0,0 +1,459 @@
+* {
+  margin: 0px;
+  padding: 0px;
+}
+
+html { font-size: 62.5%; }
+
+body {
+  font-family: {{ theme_bodyfont }};
+  background-color: {{ theme_bgcolor }};
+  color: #777;
+  border-top: 4px solid #fd7800;
+}
+
+body { background: white; font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; font-size: 14px; line-height: 1; color: #222222; position: relative; -webkit-font-smoothing: antialiased; }
+
+/* Page layout */
+
+div.header, div.content, div.footer {
+  width: 90%;
+  margin-left: auto;
+  margin-right: auto;
+}
+
+div.header-wrapper {
+  background: {{ theme_headerbg }};
+  border-bottom: 3px solid #2e3436;
+}
+
+
+/* Default body styles */
+a {
+  color: {{ theme_linkcolor }};
+}
+
+div.bodywrapper a, div.footer a {
+  text-decoration: none;
+}
+
+.clearer {
+  clear: both;
+}
+
+.left {
+  float: left;
+}
+
+.right {
+  float: right;
+}
+
+.line-block {
+    display: block;
+    margin-top: 1em;
+    margin-bottom: 1em;
+}
+
+.line-block .line-block {
+    margin-top: 0;
+    margin-bottom: 0;
+    margin-left: 1.5em;
+}
+
+h1, h2, h3, h4 {
+  font-family: {{ theme_headerfont }};
+  font-weight: normal;
+  color: {{ theme_headercolor2 }};
+  margin-bottom: .8em;
+}
+
+h1 {
+  color: {{ theme_headercolor1 }};
+}
+
+h2 {
+  padding-bottom: .5em;
+  border-bottom: 1px solid {{ theme_headercolor2 }};
+}
+
+a.headerlink {
+  visibility: hidden;
+  color: #dddddd;
+  padding-left: .3em;
+}
+
+h1:hover > a.headerlink,
+h2:hover > a.headerlink,
+h3:hover > a.headerlink,
+h4:hover > a.headerlink,
+h5:hover > a.headerlink,
+h6:hover > a.headerlink,
+dt:hover > a.headerlink {
+  visibility: visible;
+}
+
+img {
+  border: 0;
+}
+
+div.admonition {
+  margin-top: 10px;
+  margin-bottom: 10px;
+  padding: 2px 7px 1px 7px;
+  border-left: 0.2em solid black;
+}
+
+p.admonition-title {
+  margin: 0px 10px 5px 0px;
+  font-weight: bold;
+}
+
+dt:target, .highlighted {
+  background-color: #fbe54e;
+}
+
+/* Header */
+
+div.header {
+  padding-top: 10px;
+  padding-bottom: 10px;
+}
+
+div.header .headertitle {
+  font-family: {{ theme_headerfont }};
+  font-weight: normal;
+  font-size: 180%;
+  letter-spacing: .08em;
+  margin-bottom: .8em;
+}
+
+div.header .headertitle a {
+  color: white;
+}
+
+div.header div.rel {
+  margin-top: 1em;
+}
+
+div.header div.rel a {
+  color: {{ theme_headerlinkcolor }};
+  letter-spacing: .1em;
+  text-transform: uppercase;
+}
+
+p.logo {
+    float: right;
+}
+
+img.logo {
+    border: 0;
+}
+
+
+/* Content */
+div.content-wrapper {
+  background-color: white;
+  padding-top: 20px;
+  padding-bottom: 20px;
+}
+
+div.document {
+  width: 70%;
+  float: left;
+}
+
+div.body {
+  padding-right: 2em;
+  text-align: left;
+}
+
+div.document h1 {
+  line-height: 120%;
+}
+
+div.document ul {
+  margin-left: 1.5em;
+  list-style-type: square;
+}
+
+div.document dd {
+  margin-left: 1.2em;
+  margin-top: .4em;
+  margin-bottom: 1em;
+}
+
+div.document .section {
+  margin-top: 1.7em;
+}
+div.document .section:first-child {
+  margin-top: 0px;
+}
+
+div.document div.highlight {
+  padding: 3px;
+  background-color: #eeeeec;
+  border-top: 2px solid #dddddd;
+  border-bottom: 2px solid #dddddd;
+  margin-bottom: .8em;
+}
+
+div.document h2 {
+  margin-top: .7em;
+}
+
+div.document p {
+  margin-bottom: .5em;
+}
+
+div.document li.toctree-l1 {
+  margin-bottom: 1em;
+}
+
+div.document .descname {
+  font-weight: bold;
+}
+
+div.document .docutils.literal {
+  background-color: #eeeeec;
+  padding: 1px;
+}
+
+div.document .docutils.xref.literal {
+  background-color: transparent;
+  padding: 0px;
+}
+
+div.document ol {
+  margin: 1.5em;
+}
+
+
+/* Sidebar */
+
+div.sidebar {
+  width: 20%;
+  float: right;
+  font-size: .9em;
+}
+
+div.sidebar a, div.header a {
+  text-decoration: none;
+}
+
+div.sidebar a:hover, div.header a:hover {
+  text-decoration: none;
+}
+
+div.sidebar h3 {
+  color: #2e3436;
+  text-transform: uppercase;
+  font-size: 130%;
+  letter-spacing: .1em;
+}
+
+div.sidebar ul {
+  list-style-type: none;
+}
+
+div.sidebar li.toctree-l1 a {
+  display: block;
+  padding: 1px;
+  border: 1px solid #dddddd;
+  background-color: #eeeeec;
+  margin-bottom: .4em;
+  padding-left: 3px;
+  color: #2e3436;
+}
+
+div.sidebar li.toctree-l2 a {
+  background-color: transparent;
+  border: none;
+  margin-left: 1em;
+  border-bottom: 1px solid #dddddd;
+}
+
+div.sidebar li.toctree-l3 a {
+  background-color: transparent;
+  border: none;
+  margin-left: 2em;
+  border-bottom: 1px solid #dddddd;
+}
+
+div.sidebar li.toctree-l2:last-child a {
+  border-bottom: none;
+}
+
+div.sidebar li.toctree-l1.current a {
+  border-right: 5px solid {{ theme_headerlinkcolor }};
+}
+
+div.sidebar li.toctree-l1.current li.toctree-l2 a {
+  border-right: none;
+}
+
+div.sidebar input[type="text"] {
+  width: 170px;
+}
+
+div.sidebar input[type="submit"] {
+  width: 30px;
+}
+
+
+/* Footer */
+
+div.footer-wrapper {
+  background: {{ theme_footerbg }};
+  border-top: 4px solid #babdb6;
+  padding-top: 10px;
+  padding-bottom: 10px;
+  min-height: 80px;
+}
+
+div.footer, div.footer a {
+  color: #888a85;
+}
+
+div.footer .right {
+  text-align: right;
+}
+
+div.footer .left {
+  text-transform: uppercase;
+}
+
+
+/* Styles copied from basic theme */
+
+img.align-left, .figure.align-left, object.align-left {
+    clear: left;
+    float: left;
+    margin-right: 1em;
+}
+
+img.align-right, .figure.align-right, object.align-right {
+    clear: right;
+    float: right;
+    margin-left: 1em;
+}
+
+img.align-center, .figure.align-center, object.align-center {
+  display: block;
+  margin-left: auto;
+  margin-right: auto;
+}
+
+.align-left {
+    text-align: left;
+}
+
+.align-center {
+    text-align: center;
+}
+
+.align-right {
+    text-align: right;
+}
+
+/* -- search page ----------------------------------------------------------- */
+
+ul.search {
+    margin: 10px 0 0 20px;
+    padding: 0;
+}
+
+ul.search li {
+    padding: 5px 0 5px 20px;
+    background-image: url(file.png);
+    background-repeat: no-repeat;
+    background-position: 0 7px;
+}
+
+ul.search li a {
+    font-weight: bold;
+}
+
+ul.search li div.context {
+    color: #888;
+    margin: 2px 0 0 30px;
+    text-align: left;
+}
+
+ul.keywordmatches li.goodmatch a {
+    font-weight: bold;
+}
+
+/* -- index page ------------------------------------------------------------ */
+
+table.contentstable {
+    width: 90%;
+}
+
+table.contentstable p.biglink {
+    line-height: 150%;
+}
+
+a.biglink {
+    font-size: 1.3em;
+}
+
+span.linkdescr {
+    font-style: italic;
+    padding-top: 5px;
+    font-size: 90%;
+}
+
+/* -- general index --------------------------------------------------------- */
+
+table.indextable td {
+    text-align: left;
+    vertical-align: top;
+}
+
+table.indextable dl, table.indextable dd {
+    margin-top: 0;
+    margin-bottom: 0;
+}
+
+table.indextable tr.pcap {
+    height: 10px;
+}
+
+table.indextable tr.cap {
+    margin-top: 10px;
+    background-color: #f2f2f2;
+}
+
+img.toggler {
+    margin-right: 3px;
+    margin-top: 3px;
+    cursor: pointer;
+}
+
+/* -- viewcode extension ---------------------------------------------------- */
+
+.viewcode-link {
+    float: right;
+}
+
+.viewcode-back {
+    float: right;
+    font-family:: {{ theme_bodyfont }};
+}
+
+div.viewcode-block:target {
+    margin: -1px -3px;
+    padding: 0 3px;
+    background-color: #f4debf;
+    border-top: 1px solid #ac9;
+    border-bottom: 1px solid #ac9;
+}
+
+td.linenos pre {
+    padding: 5px 0px;
+    border: 0;
+    background-color: transparent;
+    color: #aaa;
+    margin-top: -10pt;
+}
\ No newline at end of file
diff --git a/NFD/docs/named_data_theme/static/bc_s.png b/NFD/docs/named_data_theme/static/bc_s.png
new file mode 100644
index 0000000..eebf862
--- /dev/null
+++ b/NFD/docs/named_data_theme/static/bc_s.png
Binary files differ
diff --git a/NFD/docs/named_data_theme/static/default.css_t b/NFD/docs/named_data_theme/static/default.css_t
new file mode 100644
index 0000000..b582768
--- /dev/null
+++ b/NFD/docs/named_data_theme/static/default.css_t
@@ -0,0 +1,14 @@
+@import url("agogo.css");
+
+pre {
+    padding: 10px;
+    background-color: #fafafa;
+    color: #222;
+    line-height: 1.2em;
+    border: 2px solid #C6C9CB;
+    font-size: 1.1em;
+    /* margin: 1.5em 0 1.5em 0; */
+    margin: 0;
+    border-right-style: none;
+    border-left-style: none;
+}
diff --git a/NFD/docs/named_data_theme/static/doxygen.css b/NFD/docs/named_data_theme/static/doxygen.css
new file mode 100644
index 0000000..e5c796e
--- /dev/null
+++ b/NFD/docs/named_data_theme/static/doxygen.css
@@ -0,0 +1,1157 @@
+/* The standard CSS for doxygen */
+
+body, table, div, p, dl {
+	font-family: Lucida Grande, Verdana, Geneva, Arial, sans-serif;
+	font-size: 13px;
+	line-height: 1.3;
+}
+
+/* @group Heading Levels */
+
+h1 {
+	font-size: 150%;
+}
+
+.title {
+	font-size: 150%;
+	font-weight: bold;
+	margin: 10px 2px;
+}
+
+h2 {
+	font-size: 120%;
+}
+
+h3 {
+	font-size: 100%;
+}
+
+h1, h2, h3, h4, h5, h6 {
+	-webkit-transition: text-shadow 0.5s linear;
+	-moz-transition: text-shadow 0.5s linear;
+	-ms-transition: text-shadow 0.5s linear;
+	-o-transition: text-shadow 0.5s linear;
+	transition: text-shadow 0.5s linear;
+	margin-right: 15px;
+}
+
+h1.glow, h2.glow, h3.glow, h4.glow, h5.glow, h6.glow {
+	text-shadow: 0 0 15px cyan;
+}
+
+dt {
+	font-weight: bold;
+}
+
+div.multicol {
+	-moz-column-gap: 1em;
+	-webkit-column-gap: 1em;
+	-moz-column-count: 3;
+	-webkit-column-count: 3;
+}
+
+p.startli, p.startdd, p.starttd {
+	margin-top: 2px;
+}
+
+p.endli {
+	margin-bottom: 0px;
+}
+
+p.enddd {
+	margin-bottom: 4px;
+}
+
+p.endtd {
+	margin-bottom: 2px;
+}
+
+/* @end */
+
+caption {
+	font-weight: bold;
+}
+
+span.legend {
+        font-size: 70%;
+        text-align: center;
+}
+
+h3.version {
+        font-size: 90%;
+        text-align: center;
+}
+
+div.qindex, div.navtab{
+	background-color: #EFEFEF;
+	border: 1px solid #B5B5B5;
+	text-align: center;
+}
+
+div.qindex, div.navpath {
+	width: 100%;
+	line-height: 140%;
+}
+
+div.navtab {
+	margin-right: 15px;
+}
+
+/* @group Link Styling */
+
+a {
+	color: #585858;
+	font-weight: normal;
+	text-decoration: none;
+}
+
+/*.contents a:visited {
+	color: #686868;
+}*/
+
+a:hover {
+	text-decoration: underline;
+}
+
+a.qindex {
+	font-weight: bold;
+}
+
+a.qindexHL {
+	font-weight: bold;
+	background-color: #B0B0B0;
+	color: #ffffff;
+	border: 1px double #9F9F9F;
+}
+
+.contents a.qindexHL:visited {
+        color: #ffffff;
+}
+
+a.el {
+	font-weight: bold;
+}
+
+a.elRef {
+}
+
+a.code, a.code:visited {
+	color: #4665A2;
+}
+
+a.codeRef, a.codeRef:visited {
+	color: #4665A2;
+}
+
+/* @end */
+
+dl.el {
+	margin-left: -1cm;
+}
+
+pre.fragment {
+        border: 1px solid #C4CFE5;
+        background-color: #FBFCFD;
+        padding: 4px 6px;
+        margin: 4px 8px 4px 2px;
+        overflow: auto;
+        word-wrap: break-word;
+        font-size:  9pt;
+        line-height: 125%;
+        font-family: monospace, fixed;
+        font-size: 105%;
+}
+
+div.fragment {
+        padding: 4px;
+        margin: 4px;
+	background-color: #FCFCFC;
+	border: 1px solid #D0D0D0;
+}
+
+div.line {
+	font-family: monospace, fixed;
+        font-size: 13px;
+	min-height: 13px;
+	line-height: 1.0;
+	text-wrap: unrestricted;
+	white-space: -moz-pre-wrap; /* Moz */
+	white-space: -pre-wrap;     /* Opera 4-6 */
+	white-space: -o-pre-wrap;   /* Opera 7 */
+	white-space: pre-wrap;      /* CSS3  */
+	word-wrap: break-word;      /* IE 5.5+ */
+	text-indent: -53px;
+	padding-left: 53px;
+	padding-bottom: 0px;
+	margin: 0px;
+	-webkit-transition-property: background-color, box-shadow;
+	-webkit-transition-duration: 0.5s;
+	-moz-transition-property: background-color, box-shadow;
+	-moz-transition-duration: 0.5s;
+	-ms-transition-property: background-color, box-shadow;
+	-ms-transition-duration: 0.5s;
+	-o-transition-property: background-color, box-shadow;
+	-o-transition-duration: 0.5s;
+	transition-property: background-color, box-shadow;
+	transition-duration: 0.5s;
+}
+
+div.line.glow {
+	background-color: cyan;
+	box-shadow: 0 0 10px cyan;
+}
+
+
+span.lineno {
+	padding-right: 4px;
+	text-align: right;
+	border-right: 2px solid #0F0;
+	background-color: #E8E8E8;
+        white-space: pre;
+}
+span.lineno a {
+	background-color: #D8D8D8;
+}
+
+span.lineno a:hover {
+	background-color: #C8C8C8;
+}
+
+div.ah {
+	background-color: black;
+	font-weight: bold;
+	color: #ffffff;
+	margin-bottom: 3px;
+	margin-top: 3px;
+	padding: 0.2em;
+	border: solid thin #333;
+	border-radius: 0.5em;
+	-webkit-border-radius: .5em;
+	-moz-border-radius: .5em;
+	box-shadow: 2px 2px 3px #999;
+	-webkit-box-shadow: 2px 2px 3px #999;
+	-moz-box-shadow: rgba(0, 0, 0, 0.15) 2px 2px 2px;
+	background-image: -webkit-gradient(linear, left top, left bottom, from(#eee), to(#000),color-stop(0.3, #444));
+	background-image: -moz-linear-gradient(center top, #eee 0%, #444 40%, #000);
+}
+
+div.groupHeader {
+	margin-left: 16px;
+	margin-top: 12px;
+	font-weight: bold;
+}
+
+div.groupText {
+	margin-left: 16px;
+	font-style: italic;
+}
+
+body {
+	background-color: white;
+	color: black;
+        margin: 0;
+}
+
+div.contents {
+	margin-top: 10px;
+	margin-left: 12px;
+	margin-right: 8px;
+}
+
+td.indexkey {
+	background-color: #EFEFEF;
+	font-weight: bold;
+	border: 1px solid #D0D0D0;
+	margin: 2px 0px 2px 0;
+	padding: 2px 10px;
+        white-space: nowrap;
+        vertical-align: top;
+}
+
+td.indexvalue {
+	background-color: #EFEFEF;
+	border: 1px solid #D0D0D0;
+	padding: 2px 10px;
+	margin: 2px 0px;
+}
+
+tr.memlist {
+	background-color: #F1F1F1;
+}
+
+p.formulaDsp {
+	text-align: center;
+}
+
+img.formulaDsp {
+
+}
+
+img.formulaInl {
+	vertical-align: middle;
+}
+
+div.center {
+	text-align: center;
+        margin-top: 0px;
+        margin-bottom: 0px;
+        padding: 0px;
+}
+
+div.center img {
+	border: 0px;
+}
+
+address.footer {
+	text-align: right;
+	padding-right: 12px;
+}
+
+img.footer {
+	border: 0px;
+	vertical-align: middle;
+}
+
+/* @group Code Colorization */
+
+span.keyword {
+	color: #008000
+}
+
+span.keywordtype {
+	color: #604020
+}
+
+span.keywordflow {
+	color: #e08000
+}
+
+span.comment {
+	color: #800000
+}
+
+span.preprocessor {
+	color: #806020
+}
+
+span.stringliteral {
+	color: #002080
+}
+
+span.charliteral {
+	color: #008080
+}
+
+span.vhdldigit {
+	color: #ff00ff
+}
+
+span.vhdlchar {
+	color: #000000
+}
+
+span.vhdlkeyword {
+	color: #700070
+}
+
+span.vhdllogic {
+	color: #ff0000
+}
+
+blockquote {
+        background-color: #F8F8F8;
+        border-left: 2px solid #B0B0B0;
+        margin: 0 24px 0 4px;
+        padding: 0 12px 0 16px;
+}
+
+/* @end */
+
+/*
+.search {
+	color: #003399;
+	font-weight: bold;
+}
+
+form.search {
+	margin-bottom: 0px;
+	margin-top: 0px;
+}
+
+input.search {
+	font-size: 75%;
+	color: #000080;
+	font-weight: normal;
+	background-color: #e8eef2;
+}
+*/
+
+td.tiny {
+	font-size: 75%;
+}
+
+.dirtab {
+	padding: 4px;
+	border-collapse: collapse;
+	border: 1px solid #B5B5B5;
+}
+
+th.dirtab {
+	background: #EFEFEF;
+	font-weight: bold;
+}
+
+hr {
+	height: 0px;
+	border: none;
+	border-top: 1px solid #6E6E6E;
+}
+
+hr.footer {
+	height: 1px;
+}
+
+/* @group Member Descriptions */
+
+table.memberdecls {
+	border-spacing: 0px;
+	padding: 0px;
+}
+
+.memberdecls td {
+	-webkit-transition-property: background-color, box-shadow;
+	-webkit-transition-duration: 0.5s;
+	-moz-transition-property: background-color, box-shadow;
+	-moz-transition-duration: 0.5s;
+	-ms-transition-property: background-color, box-shadow;
+	-ms-transition-duration: 0.5s;
+	-o-transition-property: background-color, box-shadow;
+	-o-transition-duration: 0.5s;
+	transition-property: background-color, box-shadow;
+	transition-duration: 0.5s;
+}
+
+.memberdecls td.glow {
+	background-color: cyan;
+	box-shadow: 0 0 15px cyan;
+}
+
+.mdescLeft, .mdescRight,
+.memItemLeft, .memItemRight,
+.memTemplItemLeft, .memTemplItemRight, .memTemplParams {
+	background-color: #FAFAFA;
+	border: none;
+	margin: 4px;
+	padding: 1px 0 0 8px;
+}
+
+.mdescLeft, .mdescRight {
+	padding: 0px 8px 4px 8px;
+	color: #555;
+}
+
+.memItemLeft, .memItemRight, .memTemplParams {
+	border-top: 1px solid #D0D0D0;
+}
+
+.memItemLeft, .memTemplItemLeft {
+        white-space: nowrap;
+}
+
+.memItemRight {
+	width: 100%;
+}
+
+.memTemplParams {
+	color: #686868;
+        white-space: nowrap;
+}
+
+/* @end */
+
+/* @group Member Details */
+
+/* Styles for detailed member documentation */
+
+.memtemplate {
+	font-size: 80%;
+	color: #686868;
+	font-weight: normal;
+	margin-left: 9px;
+}
+
+.memnav {
+	background-color: #EFEFEF;
+	border: 1px solid #B5B5B5;
+	text-align: center;
+	margin: 2px;
+	margin-right: 15px;
+	padding: 2px;
+}
+
+.mempage {
+	width: 100%;
+}
+
+.memitem {
+	padding: 0;
+	margin-bottom: 10px;
+	margin-right: 5px;
+        -webkit-transition: box-shadow 0.5s linear;
+        -moz-transition: box-shadow 0.5s linear;
+        -ms-transition: box-shadow 0.5s linear;
+        -o-transition: box-shadow 0.5s linear;
+        transition: box-shadow 0.5s linear;
+        display: table !important;
+        width: 100%;
+}
+
+.memitem.glow {
+         box-shadow: 0 0 15px cyan;
+}
+
+.memname {
+        font-weight: bold;
+        margin-left: 6px;
+}
+
+.memname td {
+	vertical-align: bottom;
+}
+
+.memproto, dl.reflist dt {
+        border-top: 1px solid #B9B9B9;
+        border-left: 1px solid #B9B9B9;
+        border-right: 1px solid #B9B9B9;
+        padding: 6px 0px 6px 0px;
+        color: #323232;
+        font-weight: bold;
+        text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.9);
+        background-image:url('nav_f.png');
+        background-repeat:repeat-x;
+        background-color: #E8E8E8;
+        /* opera specific markup */
+        box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15);
+        border-top-right-radius: 4px;
+        border-top-left-radius: 4px;
+        /* firefox specific markup */
+        -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px;
+        -moz-border-radius-topright: 4px;
+        -moz-border-radius-topleft: 4px;
+        /* webkit specific markup */
+        -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15);
+        -webkit-border-top-right-radius: 4px;
+        -webkit-border-top-left-radius: 4px;
+
+}
+
+.memdoc, dl.reflist dd {
+        border-bottom: 1px solid #B9B9B9;
+        border-left: 1px solid #B9B9B9;
+        border-right: 1px solid #B9B9B9;
+        padding: 6px 10px 2px 10px;
+        background-color: #FCFCFC;
+        border-top-width: 0;
+        background-image:url('nav_g.png');
+        background-repeat:repeat-x;
+        background-color: #FFFFFF;
+        /* opera specific markup */
+        border-bottom-left-radius: 4px;
+        border-bottom-right-radius: 4px;
+        box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15);
+        /* firefox specific markup */
+        -moz-border-radius-bottomleft: 4px;
+        -moz-border-radius-bottomright: 4px;
+        -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px;
+        /* webkit specific markup */
+        -webkit-border-bottom-left-radius: 4px;
+        -webkit-border-bottom-right-radius: 4px;
+        -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15);
+}
+
+dl.reflist dt {
+        padding: 5px;
+}
+
+dl.reflist dd {
+        margin: 0px 0px 10px 0px;
+        padding: 5px;
+}
+
+.paramkey {
+	text-align: right;
+}
+
+.paramtype {
+	white-space: nowrap;
+}
+
+.paramname {
+	color: #602020;
+	white-space: nowrap;
+}
+.paramname em {
+	font-style: normal;
+}
+.paramname code {
+        line-height: 14px;
+}
+
+.params, .retval, .exception, .tparams {
+        margin-left: 0px;
+        padding-left: 0px;
+}
+
+.params .paramname, .retval .paramname {
+        font-weight: bold;
+        vertical-align: top;
+}
+
+.params .paramtype {
+        font-style: italic;
+        vertical-align: top;
+}
+
+.params .paramdir {
+        font-family: "courier new",courier,monospace;
+        vertical-align: top;
+}
+
+table.mlabels {
+	border-spacing: 0px;
+}
+
+td.mlabels-left {
+	width: 100%;
+	padding: 0px;
+}
+
+td.mlabels-right {
+	vertical-align: bottom;
+	padding: 0px;
+	white-space: nowrap;
+}
+
+span.mlabels {
+        margin-left: 8px;
+}
+
+span.mlabel {
+        background-color: #8F8F8F;
+        border-top:1px solid #787878;
+        border-left:1px solid #787878;
+        border-right:1px solid #D0D0D0;
+        border-bottom:1px solid #D0D0D0;
+	text-shadow: none;
+        color: white;
+        margin-right: 4px;
+        padding: 2px 3px;
+        border-radius: 3px;
+        font-size: 7pt;
+	white-space: nowrap;
+}
+
+
+
+/* @end */
+
+/* these are for tree view when not used as main index */
+
+div.directory {
+        margin: 10px 0px;
+        border-top: 1px solid #A8B8D9;
+        border-bottom: 1px solid #A8B8D9;
+        width: 100%;
+}
+
+.directory table {
+        border-collapse:collapse;
+        width: 100%;
+}
+
+.directory td {
+        margin: 0px;
+        padding: 0px;
+	vertical-align: top;
+}
+
+.directory td.entry {
+        width: 20%;
+        white-space: nowrap;
+        padding-right: 6px;
+}
+
+.directory td.entry a {
+        outline:none;
+}
+
+.directory td.entry a img {
+        border: none;
+}
+
+.directory td.desc {
+        width: 80%;
+        padding-left: 6px;
+	padding-right: 6px;
+	border-left: 1px solid rgba(0,0,0,0.05);
+}
+
+.directory tr.even {
+	padding-left: 6px;
+	background-color: #F8F8F8;
+}
+
+.directory img {
+	vertical-align: -30%;
+}
+
+.directory .levels {
+        white-space: nowrap;
+        width: 100%;
+        text-align: right;
+        font-size: 9pt;
+}
+
+.directory .levels span {
+        cursor: pointer;
+        padding-left: 2px;
+        padding-right: 2px;
+	color: #585858;
+}
+
+div.dynheader {
+        margin-top: 8px;
+	-webkit-touch-callout: none;
+	-webkit-user-select: none;
+	-khtml-user-select: none;
+	-moz-user-select: none;
+	-ms-user-select: none;
+	user-select: none;
+}
+
+address {
+	font-style: normal;
+	color: #3A3A3A;
+}
+
+table.doxtable {
+	border-collapse:collapse;
+        margin-top: 4px;
+        margin-bottom: 4px;
+}
+
+table.doxtable td, table.doxtable th {
+	border: 1px solid #3F3F3F;
+	padding: 3px 7px 2px;
+}
+
+table.doxtable th {
+	background-color: #4F4F4F;
+	color: #FFFFFF;
+	font-size: 110%;
+	padding-bottom: 4px;
+	padding-top: 5px;
+}
+
+table.fieldtable {
+        width: 100%;
+        margin-bottom: 10px;
+        border: 1px solid #B9B9B9;
+        border-spacing: 0px;
+        -moz-border-radius: 4px;
+        -webkit-border-radius: 4px;
+        border-radius: 4px;
+        -moz-box-shadow: rgba(0, 0, 0, 0.15) 2px 2px 2px;
+        -webkit-box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15);
+        box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15);
+}
+
+.fieldtable td, .fieldtable th {
+        padding: 3px 7px 2px;
+}
+
+.fieldtable td.fieldtype, .fieldtable td.fieldname {
+        white-space: nowrap;
+        border-right: 1px solid #B9B9B9;
+        border-bottom: 1px solid #B9B9B9;
+        vertical-align: top;
+}
+
+.fieldtable td.fielddoc {
+        border-bottom: 1px solid #B9B9B9;
+        width: 100%;
+}
+
+.fieldtable tr:last-child td {
+        border-bottom: none;
+}
+
+.fieldtable th {
+        background-image:url('nav_f.png');
+        background-repeat:repeat-x;
+        background-color: #E8E8E8;
+        font-size: 90%;
+        color: #323232;
+        padding-bottom: 4px;
+        padding-top: 5px;
+        text-align:left;
+        -moz-border-radius-topleft: 4px;
+        -moz-border-radius-topright: 4px;
+        -webkit-border-top-left-radius: 4px;
+        -webkit-border-top-right-radius: 4px;
+        border-top-left-radius: 4px;
+        border-top-right-radius: 4px;
+        border-bottom: 1px solid #B9B9B9;
+}
+
+
+.tabsearch {
+	top: 0px;
+	left: 10px;
+	height: 36px;
+	background-image: url('tab_b.png');
+	z-index: 101;
+	overflow: hidden;
+	font-size: 13px;
+}
+
+.navpath ul
+{
+	font-size: 11px;
+	background-image:url('tab_b.png');
+	background-repeat:repeat-x;
+	height:30px;
+	line-height:30px;
+	color:#A2A2A2;
+	border:solid 1px #CECECE;
+	overflow:hidden;
+	margin:0px;
+	padding:0px;
+}
+
+.navpath li
+{
+	list-style-type:none;
+	float:left;
+	padding-left:10px;
+	padding-right:15px;
+	background-image:url('bc_s.png');
+	background-repeat:no-repeat;
+	background-position:right;
+	color:#4D4D4D;
+}
+
+.navpath li.navelem a
+{
+	height:32px;
+	display:block;
+	text-decoration: none;
+	outline: none;
+}
+
+.navpath li.navelem a:hover
+{
+	color:#888888;
+}
+
+.navpath li.footer
+{
+        list-style-type:none;
+        float:right;
+        padding-left:10px;
+        padding-right:15px;
+        background-image:none;
+        background-repeat:no-repeat;
+        background-position:right;
+        color:#4D4D4D;
+        font-size: 8pt;
+}
+
+
+div.summary
+{
+	float: right;
+	font-size: 8pt;
+	padding-right: 5px;
+	width: 50%;
+	text-align: right;
+}
+
+div.summary a
+{
+	white-space: nowrap;
+}
+
+div.ingroups
+{
+	font-size: 8pt;
+	width: 50%;
+	text-align: left;
+}
+
+div.ingroups a
+{
+	white-space: nowrap;
+}
+
+div.header
+{
+        background-image:url('nav_h.png');
+        background-repeat:repeat-x;
+	background-color: #FAFAFA;
+	margin:  0px;
+	border-bottom: 1px solid #D0D0D0;
+}
+
+div.headertitle
+{
+	padding: 5px 5px 5px 7px;
+}
+
+dl
+{
+        padding: 0 0 0 10px;
+}
+
+/* dl.note, dl.warning, dl.attention, dl.pre, dl.post, dl.invariant, dl.deprecated, dl.todo, dl.test, dl.bug */
+dl.section
+{
+	margin-left: 0px;
+	padding-left: 0px;
+}
+
+dl.note
+{
+        margin-left:-7px;
+        padding-left: 3px;
+        border-left:4px solid;
+        border-color: #D0C000;
+}
+
+dl.warning, dl.attention
+{
+        margin-left:-7px;
+        padding-left: 3px;
+        border-left:4px solid;
+        border-color: #FF0000;
+}
+
+dl.pre, dl.post, dl.invariant
+{
+        margin-left:-7px;
+        padding-left: 3px;
+        border-left:4px solid;
+        border-color: #00D000;
+}
+
+dl.deprecated
+{
+        margin-left:-7px;
+        padding-left: 3px;
+        border-left:4px solid;
+        border-color: #505050;
+}
+
+dl.todo
+{
+        margin-left:-7px;
+        padding-left: 3px;
+        border:4px solid;
+        border-color: #00C0E0;
+}
+
+dl.test
+{
+        margin-left:-7px;
+        padding-left: 3px;
+        border-left:4px solid;
+        border-color: #3030E0;
+}
+
+dl.bug
+{
+        margin-left:-7px;
+        padding-left: 3px;
+        border-left:4px solid;
+        border-color: #C08050;
+}
+
+dl.section dd {
+	margin-bottom: 6px;
+}
+
+
+#projectlogo
+{
+	text-align: center;
+	vertical-align: bottom;
+	border-collapse: separate;
+}
+
+#projectlogo img
+{
+	border: 0px none;
+}
+
+#projectname
+{
+	font: 300% Tahoma, Arial,sans-serif;
+	margin: 0px;
+	padding: 2px 0px;
+}
+
+#projectbrief
+{
+	font: 120% Tahoma, Arial,sans-serif;
+	margin: 0px;
+	padding: 0px;
+}
+
+#projectnumber
+{
+	font: 50% Tahoma, Arial,sans-serif;
+	margin: 0px;
+	padding: 0px;
+}
+
+#titlearea
+{
+	padding: 0px;
+	margin: 0px;
+	width: 100%;
+	border-bottom: 1px solid #787878;
+}
+
+.image
+{
+        text-align: center;
+}
+
+.dotgraph
+{
+        text-align: center;
+}
+
+.mscgraph
+{
+        text-align: center;
+}
+
+.caption
+{
+	font-weight: bold;
+}
+
+div.zoom
+{
+	border: 1px solid #A6A6A6;
+}
+
+dl.citelist {
+        margin-bottom:50px;
+}
+
+dl.citelist dt {
+        color:#484848;
+        float:left;
+        font-weight:bold;
+        margin-right:10px;
+        padding:5px;
+}
+
+dl.citelist dd {
+        margin:2px 0;
+        padding:5px 0;
+}
+
+div.toc {
+        padding: 14px 25px;
+        background-color: #F6F6F6;
+        border: 1px solid #DFDFDF;
+        border-radius: 7px 7px 7px 7px;
+        float: right;
+        height: auto;
+        margin: 0 20px 10px 10px;
+        width: 200px;
+}
+
+div.toc li {
+        background: url("bdwn.png") no-repeat scroll 0 5px transparent;
+        font: 10px/1.2 Verdana,DejaVu Sans,Geneva,sans-serif;
+        margin-top: 5px;
+        padding-left: 10px;
+        padding-top: 2px;
+}
+
+div.toc h3 {
+        font: bold 12px/1.2 Arial,FreeSans,sans-serif;
+	color: #686868;
+        border-bottom: 0 none;
+        margin: 0;
+}
+
+div.toc ul {
+        list-style: none outside none;
+        border: medium none;
+        padding: 0px;
+}
+
+div.toc li.level1 {
+        margin-left: 0px;
+}
+
+div.toc li.level2 {
+        margin-left: 15px;
+}
+
+div.toc li.level3 {
+        margin-left: 30px;
+}
+
+div.toc li.level4 {
+        margin-left: 45px;
+}
+
+.inherit_header {
+        font-weight: bold;
+        color: gray;
+        cursor: pointer;
+	-webkit-touch-callout: none;
+	-webkit-user-select: none;
+	-khtml-user-select: none;
+	-moz-user-select: none;
+	-ms-user-select: none;
+	user-select: none;
+}
+
+.inherit_header td {
+        padding: 6px 0px 2px 5px;
+}
+
+.inherit {
+        display: none;
+}
+
+tr.heading h2 {
+        margin-top: 12px;
+        margin-bottom: 4px;
+}
+
+@media print
+{
+  #top { display: none; }
+  #side-nav { display: none; }
+  #nav-path { display: none; }
+  body { overflow:visible; }
+  h1, h2, h3, h4, h5, h6 { page-break-after: avoid; }
+  .summary { display: none; }
+  .memitem { page-break-inside: avoid; }
+  #doc-content
+  {
+    margin-left:0 !important;
+    height:auto !important;
+    width:auto !important;
+    overflow:inherit;
+    display:inline;
+  }
+}
diff --git a/NFD/docs/named_data_theme/static/foundation.css b/NFD/docs/named_data_theme/static/foundation.css
new file mode 100644
index 0000000..ff1330e
--- /dev/null
+++ b/NFD/docs/named_data_theme/static/foundation.css
@@ -0,0 +1,788 @@
+.c-1, .c-2, .c-3, .c-4, .c-5, .c-6, .c-7, .c-8, .c-9, .c-10, .c-11, .c-12 { float: left; }
+
+.c-1, .c-2, .c-3, .c-4, .c-5, .c-6, .c-7, .c-8, .c-9, .c-10, .c-11, .c-12 { position: relative; min-height: 1px; padding: 0 15px; }
+
+.c-1 { width: 8.33333%; }
+
+.c-2 { width: 16.66667%; }
+
+.c-3 { width: 25%; }
+
+.c-4 { width: 33.33333%; }
+
+.c-5 { width: 41.66667%; }
+
+.c-6 { width: 50%; }
+
+.c-7 { width: 58.33333%; }
+
+.c-8 { width: 66.66667%; }
+
+.c-9 { width: 75%; }
+
+.c-10 { width: 83.33333%; }
+
+.c-11 { width: 91.66667%; }
+
+.c-12 { width: 100%; }
+
+/* Requires: normalize.css */
+/* Global Reset & Standards ---------------------- */
+* { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; }
+
+html { font-size: 62.5%; }
+
+body { background: white; font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; font-size: 14px; line-height: 1; color: #222222; position: relative; -webkit-font-smoothing: antialiased; }
+
+/* Links ---------------------- */
+a { color: #fd7800; text-decoration: none; line-height: inherit; }
+
+a:hover { color: #2795b6; }
+
+a:focus { color: #fd7800; outline: none; }
+
+p a, p a:visited { line-height: inherit; }
+
+/* Misc ---------------------- */
+.left { float: left; }
+@media only screen and (max-width: 767px) { .left { float: none; } }
+
+.right { float: right; }
+@media only screen and (max-width: 767px) { .right { float: none; } }
+
+.text-left { text-align: left; }
+
+.text-right { text-align: right; }
+
+.text-center { text-align: center; }
+
+.hide { display: none; }
+
+.highlight { background: #ffff99; }
+
+#googlemap img, object, embed { max-width: none; }
+
+#map_canvas embed { max-width: none; }
+
+#map_canvas img { max-width: none; }
+
+#map_canvas object { max-width: none; }
+
+/* Reset for strange margins by default on <figure> elements */
+figure { margin: 0; }
+
+/* Base Type Styles Using Modular Scale ---------------------- */
+body, div, dl, dt, dd, ul, ol, li, h1, h2, h3, h4, h5, h6, pre, form, p, blockquote, th, td { margin: 0; padding: 0; font-size: 14px; direction: ltr; }
+
+p { font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; font-weight: normal; font-size: 14px; line-height: 1.6; margin-bottom: 17px; }
+p.lead { font-size: 17.5px; line-height: 1.6; margin-bottom: 17px; }
+
+aside p { font-size: 13px; line-height: 1.35; font-style: italic; }
+
+h1, h2, h3, h4, h5, h6 { font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; font-weight: bold; color: #222222; text-rendering: optimizeLegibility; line-height: 1.0; margin-bottom: 14px; margin-top: 14px; }
+h1 small, h2 small, h3 small, h4 small, h5 small, h6 small { font-size: 60%; color: #6f6f6f; line-height: 0; }
+
+h1 { font-size: 24px; }
+
+h2 { font-size: 18px; }
+
+h3 { font-size: 14px; }
+
+h4 { font-size: 12px; }
+
+h5 { font-weight: bold; font-size: 12px; }
+
+h6 { font-style: italic; font-size: 12px; }
+
+hr { border: solid #c6c6c6; border-width: 1px 0 0; clear: both; margin: 22px 0 21px; height: 0; }
+
+.subheader { line-height: 1.3; color: #6f6f6f; font-weight: 300; margin-bottom: 17px; }
+
+em, i { font-style: italic; line-height: inherit; }
+
+strong, b { font-weight: bold; line-height: inherit; }
+
+small { font-size: 60%; line-height: inherit; }
+
+code { font-weight: bold; background: #ffff99; }
+
+/* Lists ---------------------- */
+ul, ol { font-size: 14px; line-height: 1.6; margin-bottom: 17px; list-style-position: inside; }
+
+ul li ul, ul li ol { margin-left: 20px; margin-bottom: 0; }
+ul.square, ul.circle, ul.disc { margin-left: 17px; }
+ul.square { list-style-type: square; }
+ul.square li ul { list-style: inherit; }
+ul.circle { list-style-type: circle; }
+ul.circle li ul { list-style: inherit; }
+ul.disc { list-style-type: disc; }
+ul.disc li ul { list-style: inherit; }
+ul.no-bullet { list-style: none; }
+ul.large li { line-height: 21px; }
+
+ol li ul, ol li ol { margin-left: 20px; margin-bottom: 0; }
+
+/* Blockquotes ---------------------- */
+blockquote, blockquote p { line-height: 1.5; }
+
+blockquote { margin: 0 0 17px; padding: 9px 20px 0 19px; }
+blockquote cite { display: block; font-size: 13pt; color: #555555; }
+blockquote cite:before { content: "\2014 \0020"; }
+blockquote cite a, blockquote cite a:visited { color: #555555; }
+
+abbr, acronym { text-transform: uppercase; font-size: 90%; color: #222222; border-bottom: 1px solid #ddd; cursor: help; }
+
+abbr { text-transform: none; }
+
+/* Print styles.  Inlined to avoid required HTTP connection: www.phpied.com/delay-loading-your-print-css/ Credit to Paul Irish and HTML5 Boilerplate (html5boilerplate.com)
+*/
+.print-only { display: none !important; }
+
+@media print { * { background: transparent !important; color: black !important; box-shadow: none !important; text-shadow: none !important; filter: none !important; -ms-filter: none !important; }
+  /* Black prints faster: h5bp.com/s */
+  a, a:visited { text-decoration: underline; }
+  a[href]:after { content: " (" attr(href) ")"; }
+  abbr[title]:after { content: " (" attr(title) ")"; }
+  .ir a:after, a[href^="javascript:"]:after, a[href^="#"]:after { content: ""; }
+  /* Don't show links for images, or javascript/internal links */
+  pre, blockquote { border: 1px solid #999; page-break-inside: avoid; }
+  thead { display: table-header-group; }
+  /* h5bp.com/t */
+  tr, img { page-break-inside: avoid; }
+  img { max-width: 100% !important; }
+  @page { margin: 0.5cm; }
+  p, h2, h3 { orphans: 3; widows: 3; }
+  h2, h3 { page-break-after: avoid; }
+  .hide-on-print { display: none !important; }
+  .print-only { display: block !important; } }
+/* Requires globals.css */
+/* Standard Forms ---------------------- */
+form { margin: 0 0 19.41641px; }
+
+.row form .row { margin: 0 -6px; }
+.row form .row .column, .row form .row .columns { padding: 0 6px; }
+.row form .row.collapse { margin: 0; }
+.row form .row.collapse .column, .row form .row.collapse .columns { padding: 0; }
+
+label { font-size: 14px; color: #4d4d4d; cursor: pointer; display: block; font-weight: 500; margin-bottom: 3px; }
+label.right { float: none; text-align: right; }
+label.inline { line-height: 32px; margin: 0 0 12px 0; }
+
+@media only screen and (max-width: 767px) { label.right { text-align: left; } }
+.prefix, .postfix { display: block; position: relative; z-index: 2; text-align: center; width: 100%; padding-top: 0; padding-bottom: 0; height: 32px; line-height: 31px; }
+
+a.button.prefix, a.button.postfix { padding-left: 0; padding-right: 0; text-align: center; }
+
+span.prefix, span.postfix { background: #f2f2f2; border: 1px solid #cccccc; }
+
+.prefix { left: 2px; -moz-border-radius-topleft: 2px; -webkit-border-top-left-radius: 2px; border-top-left-radius: 2px; -moz-border-radius-bottomleft: 2px; -webkit-border-bottom-left-radius: 2px; border-bottom-left-radius: 2px; overflow: hidden; }
+
+.postfix { right: 2px; -moz-border-radius-topright: 2px; -webkit-border-top-right-radius: 2px; border-top-right-radius: 2px; -moz-border-radius-bottomright: 2px; -webkit-border-bottom-right-radius: 2px; border-bottom-right-radius: 2px; }
+
+input[type="text"], input[type="password"], input[type="date"], input[type="datetime"], input[type="email"], input[type="number"], input[type="search"], input[type="tel"], input[type="time"], input[type="url"], textarea { font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; border: 1px solid #cccccc; -webkit-border-radius: 2px; -moz-border-radius: 2px; -ms-border-radius: 2px; -o-border-radius: 2px; border-radius: 2px; -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); color: rgba(0, 0, 0, 0.75); display: block; font-size: 14px; margin: 0 0 12px 0; padding: 6px; height: 32px; width: 100%; -webkit-transition: all 0.15s linear; -moz-transition: all 0.15s linear; -o-transition: all 0.15s linear; transition: all 0.15s linear; }
+input[type="text"].oversize, input[type="password"].oversize, input[type="date"].oversize, input[type="datetime"].oversize, input[type="email"].oversize, input[type="number"].oversize, input[type="search"].oversize, input[type="tel"].oversize, input[type="time"].oversize, input[type="url"].oversize, textarea.oversize { font-size: 17px; padding: 4px 6px; }
+input[type="text"]:focus, input[type="password"]:focus, input[type="date"]:focus, input[type="datetime"]:focus, input[type="email"]:focus, input[type="number"]:focus, input[type="search"]:focus, input[type="tel"]:focus, input[type="time"]:focus, input[type="url"]:focus, textarea:focus { background: #fafafa; outline: none !important; border-color: #b3b3b3; }
+input[type="text"][disabled], input[type="password"][disabled], input[type="date"][disabled], input[type="datetime"][disabled], input[type="email"][disabled], input[type="number"][disabled], input[type="search"][disabled], input[type="tel"][disabled], input[type="time"][disabled], input[type="url"][disabled], textarea[disabled] { background-color: #ddd; }
+
+textarea { height: auto; }
+
+select { width: 100%; }
+
+/* Fieldsets */
+fieldset { border: solid 1px #ddd; border-radius: 3px; -webkit-border-radius: 3px; -moz-border-radius: 3px; padding: 12px 12px 0; margin: 18px 0; }
+fieldset legend { font-weight: bold; background: white; padding: 0 3px; margin: 0; margin-left: -3px; }
+
+/* Errors */
+.error input, input.error, .error textarea, textarea.error { border-color: #c60f13; background-color: rgba(198, 15, 19, 0.1); }
+
+.error label, label.error { color: #c60f13; }
+
+.error small, small.error { display: block; padding: 6px 4px; margin-top: -13px; margin-bottom: 12px; background: #c60f13; color: #fff; font-size: 12px; font-size: 1.2rem; font-weight: bold; -moz-border-radius-bottomleft: 2px; -webkit-border-bottom-left-radius: 2px; border-bottom-left-radius: 2px; -moz-border-radius-bottomright: 2px; -webkit-border-bottom-right-radius: 2px; border-bottom-right-radius: 2px; }
+
+@media only screen and (max-width: 767px) { input[type="text"].one, input[type="password"].one, input[type="date"].one, input[type="datetime"].one, input[type="email"].one, input[type="number"].one, input[type="search"].one, input[type="tel"].one, input[type="time"].one, input[type="url"].one, textarea.one, .row textarea.one { width: 100% !important; }
+  input[type="text"].two, .row input[type="text"].two, input[type="password"].two, .row input[type="password"].two, input[type="date"].two, .row input[type="date"].two, input[type="datetime"].two, .row input[type="datetime"].two, input[type="email"].two, .row input[type="email"].two, input[type="number"].two, .row input[type="number"].two, input[type="search"].two, .row input[type="search"].two, input[type="tel"].two, .row input[type="tel"].two, input[type="time"].two, .row input[type="time"].two, input[type="url"].two, .row input[type="url"].two, textarea.two, .row textarea.two { width: 100% !important; }
+  input[type="text"].three, .row input[type="text"].three, input[type="password"].three, .row input[type="password"].three, input[type="date"].three, .row input[type="date"].three, input[type="datetime"].three, .row input[type="datetime"].three, input[type="email"].three, .row input[type="email"].three, input[type="number"].three, .row input[type="number"].three, input[type="search"].three, .row input[type="search"].three, input[type="tel"].three, .row input[type="tel"].three, input[type="time"].three, .row input[type="time"].three, input[type="url"].three, .row input[type="url"].three, textarea.three, .row textarea.three { width: 100% !important; }
+  input[type="text"].four, .row input[type="text"].four, input[type="password"].four, .row input[type="password"].four, input[type="date"].four, .row input[type="date"].four, input[type="datetime"].four, .row input[type="datetime"].four, input[type="email"].four, .row input[type="email"].four, input[type="number"].four, .row input[type="number"].four, input[type="search"].four, .row input[type="search"].four, input[type="tel"].four, .row input[type="tel"].four, input[type="time"].four, .row input[type="time"].four, input[type="url"].four, .row input[type="url"].four, textarea.four, .row textarea.four { width: 100% !important; }
+  input[type="text"].five, .row input[type="text"].five, input[type="password"].five, .row input[type="password"].five, input[type="date"].five, .row input[type="date"].five, input[type="datetime"].five, .row input[type="datetime"].five, input[type="email"].five, .row input[type="email"].five, input[type="number"].five, .row input[type="number"].five, input[type="search"].five, .row input[type="search"].five, input[type="tel"].five, .row input[type="tel"].five, input[type="time"].five, .row input[type="time"].five, input[type="url"].five, .row input[type="url"].five, textarea.five, .row textarea.five { width: 100% !important; }
+  input[type="text"].six, .row input[type="text"].six, input[type="password"].six, .row input[type="password"].six, input[type="date"].six, .row input[type="date"].six, input[type="datetime"].six, .row input[type="datetime"].six, input[type="email"].six, .row input[type="email"].six, input[type="number"].six, .row input[type="number"].six, input[type="search"].six, .row input[type="search"].six, input[type="tel"].six, .row input[type="tel"].six, input[type="time"].six, .row input[type="time"].six, input[type="url"].six, .row input[type="url"].six, textarea.six, .row textarea.six { width: 100% !important; }
+  input[type="text"].seven, .row input[type="text"].seven, input[type="password"].seven, .row input[type="password"].seven, input[type="date"].seven, .row input[type="date"].seven, input[type="datetime"].seven, .row input[type="datetime"].seven, input[type="email"].seven, .row input[type="email"].seven, input[type="number"].seven, .row input[type="number"].seven, input[type="search"].seven, .row input[type="search"].seven, input[type="tel"].seven, .row input[type="tel"].seven, input[type="time"].seven, .row input[type="time"].seven, input[type="url"].seven, .row input[type="url"].seven, textarea.seven, .row textarea.seven { width: 100% !important; }
+  input[type="text"].eight, .row input[type="text"].eight, input[type="password"].eight, .row input[type="password"].eight, input[type="date"].eight, .row input[type="date"].eight, input[type="datetime"].eight, .row input[type="datetime"].eight, input[type="email"].eight, .row input[type="email"].eight, input[type="number"].eight, .row input[type="number"].eight, input[type="search"].eight, .row input[type="search"].eight, input[type="tel"].eight, .row input[type="tel"].eight, input[type="time"].eight, .row input[type="time"].eight, input[type="url"].eight, .row input[type="url"].eight, textarea.eight, .row textarea.eight { width: 100% !important; }
+  input[type="text"].nine, .row input[type="text"].nine, input[type="password"].nine, .row input[type="password"].nine, input[type="date"].nine, .row input[type="date"].nine, input[type="datetime"].nine, .row input[type="datetime"].nine, input[type="email"].nine, .row input[type="email"].nine, input[type="number"].nine, .row input[type="number"].nine, input[type="search"].nine, .row input[type="search"].nine, input[type="tel"].nine, .row input[type="tel"].nine, input[type="time"].nine, .row input[type="time"].nine, input[type="url"].nine, .row input[type="url"].nine, textarea.nine, .row textarea.nine { width: 100% !important; }
+  input[type="text"].ten, .row input[type="text"].ten, input[type="password"].ten, .row input[type="password"].ten, input[type="date"].ten, .row input[type="date"].ten, input[type="datetime"].ten, .row input[type="datetime"].ten, input[type="email"].ten, .row input[type="email"].ten, input[type="number"].ten, .row input[type="number"].ten, input[type="search"].ten, .row input[type="search"].ten, input[type="tel"].ten, .row input[type="tel"].ten, input[type="time"].ten, .row input[type="time"].ten, input[type="url"].ten, .row input[type="url"].ten, textarea.ten, .row textarea.ten { width: 100% !important; }
+  input[type="text"].eleven, .row input[type="text"].eleven, input[type="password"].eleven, .row input[type="password"].eleven, input[type="date"].eleven, .row input[type="date"].eleven, input[type="datetime"].eleven, .row input[type="datetime"].eleven, input[type="email"].eleven, .row input[type="email"].eleven, input[type="number"].eleven, .row input[type="number"].eleven, input[type="search"].eleven, .row input[type="search"].eleven, input[type="tel"].eleven, .row input[type="tel"].eleven, input[type="time"].eleven, .row input[type="time"].eleven, input[type="url"].eleven, .row input[type="url"].eleven, textarea.eleven, .row textarea.eleven { width: 100% !important; }
+  input[type="text"].twelve, .row input[type="text"].twelve, input[type="password"].twelve, .row input[type="password"].twelve, input[type="date"].twelve, .row input[type="date"].twelve, input[type="datetime"].twelve, .row input[type="datetime"].twelve, input[type="email"].twelve, .row input[type="email"].twelve, input[type="number"].twelve, .row input[type="number"].twelve, input[type="search"].twelve, .row input[type="search"].twelve, input[type="tel"].twelve, .row input[type="tel"].twelve, input[type="time"].twelve, .row input[type="time"].twelve, input[type="url"].twelve, .row input[type="url"].twelve, textarea.twelve, .row textarea.twelve { width: 100% !important; } }
+/* Custom Forms ---------------------- */
+form.custom { /* Custom input, disabled */ }
+form.custom span.custom { display: inline-block; width: 16px; height: 16px; position: relative; top: 2px; border: solid 1px #ccc; background: #fff; }
+form.custom span.custom.radio { -webkit-border-radius: 100px; -moz-border-radius: 100px; -ms-border-radius: 100px; -o-border-radius: 100px; border-radius: 100px; }
+form.custom span.custom.checkbox:before { content: ""; display: block; line-height: 0.8; height: 14px; width: 14px; text-align: center; position: absolute; top: 0; left: 0; font-size: 14px; color: #fff; }
+form.custom span.custom.radio.checked:before { content: ""; display: block; width: 8px; height: 8px; -webkit-border-radius: 100px; -moz-border-radius: 100px; -ms-border-radius: 100px; -o-border-radius: 100px; border-radius: 100px; background: #222; position: relative; top: 3px; left: 3px; }
+form.custom span.custom.checkbox.checked:before { content: "\00d7"; color: #222; }
+form.custom div.custom.dropdown { display: block; position: relative; width: auto; height: 28px; margin-bottom: 9px; margin-top: 2px; }
+form.custom div.custom.dropdown a.current { display: block; width: auto; line-height: 26px; min-height: 28px; padding: 0; padding-left: 6px; padding-right: 38px; border: solid 1px #ddd; color: #141414; background-color: #fff; white-space: nowrap; }
+form.custom div.custom.dropdown a.selector { position: absolute; width: 27px; height: 28px; display: block; right: 0; top: 0; border: solid 1px #ddd; }
+form.custom div.custom.dropdown a.selector:after { content: ""; display: block; content: ""; display: block; width: 0; height: 0; border: solid 5px; border-color: #aaaaaa transparent transparent transparent; position: absolute; left: 50%; top: 50%; margin-top: -2px; margin-left: -5px; }
+form.custom div.custom.dropdown:hover a.selector:after, form.custom div.custom.dropdown.open a.selector:after { content: ""; display: block; width: 0; height: 0; border: solid 5px; border-color: #222222 transparent transparent transparent; }
+form.custom div.custom.dropdown.open ul { display: block; z-index: 10; }
+form.custom div.custom.dropdown.small { width: 134px !important; }
+form.custom div.custom.dropdown.medium { width: 254px !important; }
+form.custom div.custom.dropdown.large { width: 434px !important; }
+form.custom div.custom.dropdown.expand { width: 100% !important; }
+form.custom div.custom.dropdown.open.small ul { width: 134px !important; }
+form.custom div.custom.dropdown.open.medium ul { width: 254px !important; }
+form.custom div.custom.dropdown.open.large ul { width: 434px !important; }
+form.custom div.custom.dropdown.open.expand ul { width: 100% !important; }
+form.custom div.custom.dropdown ul { position: absolute; width: auto; display: none; margin: 0; left: 0; top: 27px; margin: 0; padding: 0; background: #fff; background: rgba(255, 255, 255, 0.95); border: solid 1px #cccccc; }
+form.custom div.custom.dropdown ul li { color: #555; font-size: 13px; cursor: pointer; padding: 3px; padding-left: 6px; padding-right: 38px; min-height: 18px; line-height: 18px; margin: 0; white-space: nowrap; list-style: none; }
+form.custom div.custom.dropdown ul li.selected { background: #cdebf5; color: #000; }
+form.custom div.custom.dropdown ul li.selected:after { content: "\2013"; position: absolute; right: 10px; }
+form.custom div.custom.dropdown ul li:hover { background-color: #e3f4f9; color: #222; }
+form.custom div.custom.dropdown ul li:hover:after { content: "\2013"; position: absolute; right: 10px; color: #8ed3e7; }
+form.custom div.custom.dropdown ul li.selected:hover { background: #cdebf5; cursor: default; color: #000; }
+form.custom div.custom.dropdown ul li.selected:hover:after { color: #000; }
+form.custom div.custom.dropdown ul.show { display: block; }
+form.custom .custom.disabled { background-color: #ddd; }
+
+/* Correct FF custom dropdown height */
+@-moz-document url-prefix() { form.custom div.custom.dropdown a.selector { height: 30px; } }
+
+.lt-ie9 form.custom div.custom.dropdown a.selector { height: 30px; }
+
+/* The Grid ---------------------- */
+.row { width: 1000px; max-width: 100%; min-width: 768px; margin: 0 auto; }
+.row .row { width: auto; max-width: none; min-width: 0; margin: 0 -15px; }
+.row.collapse .column, .row.collapse .columns { padding: 0; }
+.row .row { width: auto; max-width: none; min-width: 0; margin: 0 -15px; }
+.row .row.collapse { margin: 0; }
+
+.column, .columns { float: left; min-height: 1px; padding: 0 15px; position: relative; }
+.column.centered, .columns.centered { float: none; margin: 0 auto; }
+
+[class*="column"] + [class*="column"]:last-child { float: right; }
+
+[class*="column"] + [class*="column"].end { float: left; }
+
+.one, .row .one { width: 8.33333%; }
+
+.two, .row .two { width: 16.66667%; }
+
+.three, .row .three { width: 25%; }
+
+.four, .row .four { width: 33.33333%; }
+
+.five, .row .five { width: 41.66667%; }
+
+.six, .row .six { width: 50%; }
+
+.seven, .row .seven { width: 58.33333%; }
+
+.eight, .row .eight { width: 66.66667%; }
+
+.nine, .row .nine { width: 75%; }
+
+.ten, .row .ten { width: 83.33333%; }
+
+.eleven, .row .eleven { width: 91.66667%; }
+
+.twelve, .row .twelve { width: 100%; }
+
+.row .offset-by-one { margin-left: 8.33333%; }
+
+.row .offset-by-two { margin-left: 16.66667%; }
+
+.row .offset-by-three { margin-left: 25%; }
+
+.row .offset-by-four { margin-left: 33.33333%; }
+
+.row .offset-by-five { margin-left: 41.66667%; }
+
+.row .offset-by-six { margin-left: 50%; }
+
+.row .offset-by-seven { margin-left: 58.33333%; }
+
+.row .offset-by-eight { margin-left: 66.66667%; }
+
+.row .offset-by-nine { margin-left: 75%; }
+
+.row .offset-by-ten { margin-left: 83.33333%; }
+
+.push-two { left: 16.66667%; }
+
+.pull-two { right: 16.66667%; }
+
+.push-three { left: 25%; }
+
+.pull-three { right: 25%; }
+
+.push-four { left: 33.33333%; }
+
+.pull-four { right: 33.33333%; }
+
+.push-five { left: 41.66667%; }
+
+.pull-five { right: 41.66667%; }
+
+.push-six { left: 50%; }
+
+.pull-six { right: 50%; }
+
+.push-seven { left: 58.33333%; }
+
+.pull-seven { right: 58.33333%; }
+
+.push-eight { left: 66.66667%; }
+
+.pull-eight { right: 66.66667%; }
+
+.push-nine { left: 75%; }
+
+.pull-nine { right: 75%; }
+
+.push-ten { left: 83.33333%; }
+
+.pull-ten { right: 83.33333%; }
+
+img, object, embed { max-width: 100%; height: auto; }
+
+object, embed { height: 100%; }
+
+img { -ms-interpolation-mode: bicubic; }
+
+#map_canvas img, .map_canvas img { max-width: none!important; }
+
+/* Nicolas Gallagher's micro clearfix */
+.row { *zoom: 1; }
+.row:before, .row:after { content: ""; display: table; }
+.row:after { clear: both; }
+
+/* Mobile Grid and Overrides ---------------------- */
+@media only screen and (max-width: 767px) { body { -webkit-text-size-adjust: none; -ms-text-size-adjust: none; width: 100%; min-width: 0; margin-left: 0; margin-right: 0; padding-left: 0; padding-right: 0; }
+  .row { width: auto; min-width: 0; margin-left: 0; margin-right: 0; }
+  .column, .columns { width: auto !important; float: none; }
+  .column:last-child, .columns:last-child { float: none; }
+  [class*="column"] + [class*="column"]:last-child { float: none; }
+  .column:before, .columns:before, .column:after, .columns:after { content: ""; display: table; }
+  .column:after, .columns:after { clear: both; }
+  .offset-by-one, .offset-by-two, .offset-by-three, .offset-by-four, .offset-by-five, .offset-by-six, .offset-by-seven, .offset-by-eight, .offset-by-nine, .offset-by-ten { margin-left: 0 !important; }
+  .push-two, .push-three, .push-four, .push-five, .push-six, .push-seven, .push-eight, .push-nine, .push-ten { left: auto; }
+  .pull-two, .pull-three, .pull-four, .pull-five, .pull-six, .pull-seven, .pull-eight, .pull-nine, .pull-ten { right: auto; }
+  /* Mobile 4-column Grid */
+  .row .mobile-one { width: 25% !important; float: left; padding: 0 15px; }
+  .row .mobile-one:last-child { float: right; }
+  .row.collapse .mobile-one { padding: 0; }
+  .row .mobile-two { width: 50% !important; float: left; padding: 0 15px; }
+  .row .mobile-two:last-child { float: right; }
+  .row.collapse .mobile-two { padding: 0; }
+  .row .mobile-three { width: 75% !important; float: left; padding: 0 15px; }
+  .row .mobile-three:last-child { float: right; }
+  .row.collapse .mobile-three { padding: 0; }
+  .row .mobile-four { width: 100% !important; float: left; padding: 0 15px; }
+  .row .mobile-four:last-child { float: right; }
+  .row.collapse .mobile-four { padding: 0; }
+  .push-one-mobile { left: 25%; }
+  .pull-one-mobile { right: 25%; }
+  .push-two-mobile { left: 50%; }
+  .pull-two-mobile { right: 50%; }
+  .push-three-mobile { left: 75%; }
+  .pull-three-mobile { right: 75%; } }
+/* Block Grids ---------------------- */
+/* These are 2-up, 3-up, 4-up and 5-up ULs, suited
+for repeating blocks of content. Add 'mobile' to
+them to switch them just like the layout grid
+(one item per line) on phones
+
+For IE7/8 compatibility block-grid items need to be
+the same height. You can optionally uncomment the
+lines below to support arbitrary height, but know
+that IE7/8 do not support :nth-child.
+-------------------------------------------------- */
+.block-grid { display: block; overflow: hidden; padding: 0; }
+.block-grid > li { display: block; height: auto; float: left; }
+.block-grid.one-up { margin: 0; }
+.block-grid.one-up > li { width: 100%; padding: 0 0 15px; }
+.block-grid.two-up { margin: 0 -15px; }
+.block-grid.two-up > li { width: 50%; padding: 0 15px 15px; }
+.block-grid.two-up > li:nth-child(2n+1) { clear: both; }
+.block-grid.three-up { margin: 0 -12px; }
+.block-grid.three-up > li { width: 33.33%; padding: 0 12px 12px; }
+.block-grid.three-up > li:nth-child(3n+1) { clear: both; }
+.block-grid.four-up { margin: 0 -10px; }
+.block-grid.four-up > li { width: 25%; padding: 0 10px 10px; }
+.block-grid.four-up > li:nth-child(4n+1) { clear: both; }
+.block-grid.five-up { margin: 0 -8px; }
+.block-grid.five-up > li { width: 20%; padding: 0 8px 8px; }
+.block-grid.five-up > li:nth-child(5n+1) { clear: both; }
+
+/* Mobile Block Grids */
+@media only screen and (max-width: 767px) { .block-grid.mobile > li { float: none; width: 100%; margin-left: 0; }
+  .block-grid > li { clear: none !important; }
+  .block-grid.mobile-two-up > li { width: 50%; }
+  .block-grid.mobile-two-up > li:nth-child(2n+1) { clear: both; }
+  .block-grid.mobile-three-up > li { width: 33.33%; }
+  .block-grid.mobile-three-up > li:nth-child(3n+1) { clear: both !important; }
+  .block-grid.mobile-four-up > li { width: 25%; }
+  .block-grid.mobile-four-up > li:nth-child(4n+1) { clear: both; }
+  .block-grid.mobile-five-up > li:nth-child(5n+1) { clear: both; } }
+/* Requires globals.css */
+/* Normal Buttons ---------------------- */
+.button { width: auto; background: #fd7800; border: 1px solid #ce6200; -webkit-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.5) inset; -moz-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.5) inset; box-shadow: 0 1px 0 rgba(255, 255, 255, 0.5) inset; color: white; cursor: pointer; display: inline-block; font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; font-size: 14px; font-weight: bold; line-height: 1; margin: 0; outline: none; padding: 10px 20px 11px; position: relative; text-align: center; text-decoration: none; -webkit-transition: background-color 0.15s ease-in-out; -moz-transition: background-color 0.15s ease-in-out; -o-transition: background-color 0.15s ease-in-out; transition: background-color 0.15s ease-in-out; /* Hovers */ /* Sizes */ /* Colors */ /* Radii */ /* Layout */ /* Disabled ---------- */ }
+.button:hover { color: white; background-color: #ce6200; }
+.button:active { -webkit-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.2) inset; -moz-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.2) inset; box-shadow: 0 1px 0 rgba(0, 0, 0, 0.2) inset; }
+.button:focus { -webkit-box-shadow: 0 0 4px #fd7800, 0 1px 0 rgba(255, 255, 255, 0.5) inset; -moz-box-shadow: 0 0 4px #fd7800, 0 1px 0 rgba(255, 255, 255, 0.5) inset; box-shadow: 0 0 4px #fd7800, 0 1px 0 rgba(255, 255, 255, 0.5) inset; color: white; }
+.button.large { font-size: 17px; padding: 15px 30px 16px; }
+.button.medium { font-size: 14px; }
+.button.small { font-size: 11px; padding: 7px 14px 8px; }
+.button.tiny { font-size: 10px; padding: 5px 10px 6px; }
+.button.expand { width: 100%; text-align: center; }
+.button.primary { background-color: #fd7800; border: 1px solid #1e728c; }
+.button.primary:hover { background-color: #2284a1; }
+.button.primary:focus { -webkit-box-shadow: 0 0 4px #fd7800, 0 1px 0 rgba(255, 255, 255, 0.5) inset; -moz-box-shadow: 0 0 4px #fd7800, 0 1px 0 rgba(255, 255, 255, 0.5) inset; box-shadow: 0 0 4px #fd7800, 0 1px 0 rgba(255, 255, 255, 0.5) inset; }
+.button.success { background-color: #5da423; border: 1px solid #396516; }
+.button.success:hover { background-color: #457a1a; }
+.button.success:focus { -webkit-box-shadow: 0 0 5px #5da423, 0 1px 0 rgba(255, 255, 255, 0.5) inset; -moz-box-shadow: 0 0 5px #5da423, 0 1px 0 rgba(255, 255, 255, 0.5) inset; box-shadow: 0 0 5px #5da423, 0 1px 0 rgba(255, 255, 255, 0.5) inset; }
+.button.alert { background-color: #c60f13; border: 1px solid #7f0a0c; }
+.button.alert:hover { background-color: #970b0e; }
+.button.alert:focus { -webkit-box-shadow: 0 0 4px #c60f13, 0 1px 0 rgba(255, 255, 255, 0.5) inset; -moz-box-shadow: 0 0 4px #c60f13, 0 1px 0 rgba(255, 255, 255, 0.5) inset; box-shadow: 0 0 4px #c60f13, 0 1px 0 rgba(255, 255, 255, 0.5) inset; }
+.button.secondary { background-color: #e9e9e9; color: #1d1d1d; border: 1px solid #c3c3c3; }
+.button.secondary:hover { background-color: #d0d0d0; }
+.button.secondary:focus { -webkit-box-shadow: 0 0 5px #e9e9e9, 0 1px 0 rgba(255, 255, 255, 0.5) inset; -moz-box-shadow: 0 0 5px #e9e9e9, 0 1px 0 rgba(255, 255, 255, 0.5) inset; box-shadow: 0 0 5px #e9e9e9, 0 1px 0 rgba(255, 255, 255, 0.5) inset; }
+.button.radius { -webkit-border-radius: 3px; -moz-border-radius: 3px; -ms-border-radius: 3px; -o-border-radius: 3px; border-radius: 3px; }
+.button.round { -webkit-border-radius: 1000px; -moz-border-radius: 1000px; -ms-border-radius: 1000px; -o-border-radius: 1000px; border-radius: 1000px; }
+.button.full-width { width: 100%; text-align: center; padding-left: 0px !important; padding-right: 0px !important; }
+.button.left-align { text-align: left; text-indent: 12px; }
+.button.disabled, .button[disabled] { opacity: 0.6; cursor: default; background: #fd7800; -webkit-box-shadow: none; -moz-box-shadow: none; box-shadow: none; }
+.button.disabled :hover, .button[disabled] :hover { background: #fd7800; }
+.button.disabled.success, .button[disabled].success { background-color: #5da423; }
+.button.disabled.success:hover, .button[disabled].success:hover { background-color: #5da423; }
+.button.disabled.alert, .button[disabled].alert { background-color: #c60f13; }
+.button.disabled.alert:hover, .button[disabled].alert:hover { background-color: #c60f13; }
+.button.disabled.secondary, .button[disabled].secondary { background-color: #e9e9e9; }
+.button.disabled.secondary:hover, .button[disabled].secondary:hover { background-color: #e9e9e9; }
+
+/* Don't use native buttons on iOS */
+input[type=submit].button, button.button { -webkit-appearance: none; }
+
+@media only screen and (max-width: 767px) { .button { display: block; }
+  button.button, input[type="submit"].button { width: 100%; padding-left: 0; padding-right: 0; } }
+/* Correct FF button padding */
+@-moz-document url-prefix() { button::-moz-focus-inner, input[type="reset"]::-moz-focus-inner, input[type="button"]::-moz-focus-inner, input[type="submit"]::-moz-focus-inner, input[type="file"] > input[type="button"]::-moz-focus-inner { border: none; padding: 0; }
+  input[type="submit"].tiny.button { padding: 3px 10px 4px; }
+  input[type="submit"].small.button { padding: 5px 14px 6px; }
+  input[type="submit"].button, input[type=submit].medium.button { padding: 8px 20px 9px; }
+  input[type="submit"].large.button { padding: 13px 30px 14px; } }
+
+/* Buttons with Dropdowns ---------------------- */
+.button.dropdown { position: relative; padding-right: 44px; /* Sizes */ /* Triangles */ /* Flyout List */ /* Split Dropdown Buttons */ }
+.button.dropdown.large { padding-right: 60px; }
+.button.dropdown.small { padding-right: 28px; }
+.button.dropdown.tiny { padding-right: 20px; }
+.button.dropdown:after { content: ""; display: block; width: 0; height: 0; border: solid 6px; border-color: white transparent transparent transparent; position: absolute; top: 50%; right: 20px; margin-top: -2px; }
+.button.dropdown.large:after { content: ""; display: block; width: 0; height: 0; border: solid 7px; border-color: white transparent transparent transparent; margin-top: -3px; right: 30px; }
+.button.dropdown.small:after { content: ""; display: block; width: 0; height: 0; border: solid 5px; border-color: white transparent transparent transparent; margin-top: -2px; right: 14px; }
+.button.dropdown.tiny:after { content: ""; display: block; width: 0; height: 0; border: solid 4px; border-color: white transparent transparent transparent; margin-top: -1px; right: 10px; }
+.button.dropdown > ul { -webkit-box-sizing: content-box; -moz-box-sizing: content-box; box-sizing: content-box; display: none; position: absolute; left: -1px; background: #fff; background: rgba(255, 255, 255, 0.95); list-style: none; margin: 0; padding: 0; border: 1px solid #cccccc; border-top: none; min-width: 100%; z-index: 40; }
+.button.dropdown > ul li { width: 100%; cursor: pointer; padding: 0; min-height: 18px; line-height: 18px; margin: 0; white-space: nowrap; list-style: none; }
+.button.dropdown > ul li a { display: block; color: #555; font-size: 13px; font-weight: normal; padding: 6px 14px; text-align: left; }
+.button.dropdown > ul li:hover { background-color: #e3f4f9; color: #222; }
+.button.dropdown > ul li.divider { min-height: 0; padding: 0; height: 1px; margin: 4px 0; background: #ededed; }
+.button.dropdown.up > ul { border-top: 1px solid #cccccc; border-bottom: none; }
+.button.dropdown ul.no-hover.show-dropdown { display: block !important; }
+.button.dropdown:hover > ul.no-hover { display: none; }
+.button.dropdown.split { padding: 0; position: relative; /* Sizes */ /* Triangle Spans */ /* Colors */ }
+.button.dropdown.split:after { display: none; }
+.button.dropdown.split:hover { background-color: #fd7800; }
+.button.dropdown.split.alert:hover { background-color: #c60f13; }
+.button.dropdown.split.success:hover { background-color: #5da423; }
+.button.dropdown.split.secondary:hover { background-color: #e9e9e9; }
+.button.dropdown.split > a { color: white; display: block; padding: 10px 50px 11px 20px; padding-left: 20px; padding-right: 50px; -webkit-transition: background-color 0.15s ease-in-out; -moz-transition: background-color 0.15s ease-in-out; -o-transition: background-color 0.15s ease-in-out; transition: background-color 0.15s ease-in-out; }
+.button.dropdown.split > a:hover { background-color: #2284a1; }
+.button.dropdown.split.large > a { padding: 15px 75px 16px 30px; padding-left: 30px; padding-right: 75px; }
+.button.dropdown.split.small > a { padding: 7px 35px 8px 14px; padding-left: 14px; padding-right: 35px; }
+.button.dropdown.split.tiny > a { padding: 5px 25px 6px 10px; padding-left: 10px; padding-right: 25px; }
+.button.dropdown.split > span { background-color: #fd7800; position: absolute; right: 0; top: 0; height: 100%; width: 30px; border-left: 1px solid #1e728c; -webkit-box-shadow: 1px 1px 0 rgba(255, 255, 255, 0.5) inset; -moz-box-shadow: 1px 1px 0 rgba(255, 255, 255, 0.5) inset; box-shadow: 1px 1px 0 rgba(255, 255, 255, 0.5) inset; -webkit-transition: background-color 0.15s ease-in-out; -moz-transition: background-color 0.15s ease-in-out; -o-transition: background-color 0.15s ease-in-out; transition: background-color 0.15s ease-in-out; }
+.button.dropdown.split > span:hover { background-color: #2284a1; }
+.button.dropdown.split > span:after { content: ""; display: block; width: 0; height: 0; border: solid 6px; border-color: white transparent transparent transparent; position: absolute; top: 50%; left: 50%; margin-left: -6px; margin-top: -2px; }
+.button.dropdown.split.secondary > span:after { content: ""; display: block; width: 0; height: 0; border: solid 6px; border-color: #1d1d1d transparent transparent transparent; }
+.button.dropdown.split.large span { width: 45px; }
+.button.dropdown.split.small span { width: 21px; }
+.button.dropdown.split.tiny span { width: 15px; }
+.button.dropdown.split.large span:after { content: ""; display: block; width: 0; height: 0; border: solid 7px; border-color: white transparent transparent transparent; margin-top: -3px; margin-left: -7px; }
+.button.dropdown.split.small span:after { content: ""; display: block; width: 0; height: 0; border: solid 4px; border-color: white transparent transparent transparent; margin-top: -1px; margin-left: -4px; }
+.button.dropdown.split.tiny span:after { content: ""; display: block; width: 0; height: 0; border: solid 3px; border-color: white transparent transparent transparent; margin-top: -1px; margin-left: -3px; }
+.button.dropdown.split.alert > span { background-color: #c60f13; border-left-color: #7f0a0c; }
+.button.dropdown.split.success > span { background-color: #5da423; border-left-color: #396516; }
+.button.dropdown.split.secondary > span { background-color: #e9e9e9; border-left-color: #c3c3c3; }
+.button.dropdown.split.secondary > a { color: #1d1d1d; }
+.button.dropdown.split.alert > a:hover, .button.dropdown.split.alert > span:hover { background-color: #970b0e; }
+.button.dropdown.split.success > a:hover, .button.dropdown.split.success > span:hover { background-color: #457a1a; }
+.button.dropdown.split.secondary > a:hover, .button.dropdown.split.secondary > span:hover { background-color: #d0d0d0; }
+
+/* Button Groups ---------------------- */
+ul.button-group { list-style: none; padding: 0; margin: 0 0 12px; *zoom: 1; }
+ul.button-group:before, ul.button-group:after { content: ""; display: table; }
+ul.button-group:after { clear: both; }
+ul.button-group li { padding: 0; margin: 0 0 0 -1px; float: left; }
+ul.button-group li:first-child { margin-left: 0; }
+ul.button-group.radius li a.button, ul.button-group.radius li a.button.radius, ul.button-group.radius li a.button-rounded { -webkit-border-radius: 0px; -moz-border-radius: 0px; -ms-border-radius: 0px; -o-border-radius: 0px; border-radius: 0px; }
+ul.button-group.radius li:first-child a.button, ul.button-group.radius li:first-child a.button.radius { -moz-border-radius-left3px: 5px; -webkit-border-left-3px-radius: 5px; border-left-3px-radius: 5px; }
+ul.button-group.radius li:first-child a.button.rounded { -moz-border-radius-left1000px: 5px; -webkit-border-left-1000px-radius: 5px; border-left-1000px-radius: 5px; }
+ul.button-group.radius li:last-child a.button, ul.button-group.radius li:last-child a.button.radius { -moz-border-radius-right3px: 5px; -webkit-border-right-3px-radius: 5px; border-right-3px-radius: 5px; }
+ul.button-group.radius li:last-child a.button.rounded { -moz-border-radius-right1000px: 5px; -webkit-border-right-1000px-radius: 5px; border-right-1000px-radius: 5px; }
+ul.button-group.even a.button { width: 100%; }
+ul.button-group.even.two-up li { width: 50%; }
+ul.button-group.even.three-up li { width: 33.3%; }
+ul.button-group.even.three-up li:first-child { width: 33.4%; }
+ul.button-group.even.four-up li { width: 25%; }
+ul.button-group.even.five-up li { width: 20%; }
+
+@media only screen and (max-width: 767px) { .button-group button.button, .button-group input[type="submit"].button { width: auto; padding: 10px 20px 11px; }
+  .button-group button.button.large, .button-group input[type="submit"].button.large { padding: 15px 30px 16px; }
+  .button-group button.button.medium, .button-group input[type="submit"].button.medium { padding: 10px 20px 11px; }
+  .button-group button.button.small, .button-group input[type="submit"].button.small { padding: 7px 14px 8px; }
+  .button-group button.button.tiny, .button-group input[type="submit"].button.tiny { padding: 5px 10px 6px; }
+  .button-group.even button.button, .button-group.even input[type="submit"].button { width: 100%; padding-left: 0; padding-right: 0; } }
+div.button-bar { overflow: hidden; }
+div.button-bar ul.button-group { float: left; margin-right: 8px; }
+div.button-bar ul.button-group:last-child { margin-left: 0; }
+
+/* CSS for jQuery Reveal Plugin Maintained for Foundation. foundation.zurb.com Free to use under the MIT license. http://www.opensource.org/licenses/mit-license.php */
+/* Reveal Modals ---------------------- */
+.reveal-modal-bg { position: fixed; height: 100%; width: 100%; background: #000; background: rgba(0, 0, 0, 0.45); z-index: 40; display: none; top: 0; left: 0; }
+
+.reveal-modal { background: white; visibility: hidden; display: none; top: 100px; left: 50%; margin-left: -260px; width: 520px; position: absolute; z-index: 41; padding: 30px; -webkit-box-shadow: 0 0 10px rgba(0, 0, 0, 0.4); -moz-box-shadow: 0 0 10px rgba(0, 0, 0, 0.4); box-shadow: 0 0 10px rgba(0, 0, 0, 0.4); }
+.reveal-modal *:first-child { margin-top: 0; }
+.reveal-modal *:last-child { margin-bottom: 0; }
+.reveal-modal .close-reveal-modal { font-size: 22px; font-size: 2.2rem; line-height: .5; position: absolute; top: 8px; right: 11px; color: #aaa; text-shadow: 0 -1px 1px rgba(0, 0, 0, 0.6); font-weight: bold; cursor: pointer; }
+.reveal-modal.small { width: 30%; margin-left: -15%; }
+.reveal-modal.medium { width: 40%; margin-left: -20%; }
+.reveal-modal.large { width: 60%; margin-left: -30%; }
+.reveal-modal.xlarge { width: 70%; margin-left: -35%; }
+.reveal-modal.expand { width: 90%; margin-left: -45%; }
+.reveal-modal .row { min-width: 0; margin-bottom: 10px; }
+
+/* Mobile */
+@media only screen and (max-width: 767px) { .reveal-modal-bg { position: absolute; }
+  .reveal-modal, .reveal-modal.small, .reveal-modal.medium, .reveal-modal.large, .reveal-modal.xlarge { width: 80%; top: 15px; left: 50%; margin-left: -40%; padding: 20px; height: auto; } }
+  /* NOTES Close button entity is &#215;
+ Example markup <div id="myModal" class="reveal-modal"> <h2>Awesome. I have it.</h2> <p class="lead">Your couch.  I it's mine.</p> <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. In ultrices aliquet placerat. Duis pulvinar orci et nisi euismod vitae tempus lorem consectetur. Duis at magna quis turpis mattis venenatis eget id diam. </p> <a class="close-reveal-modal">&#215;</a> </div> */
+/* Requires -globals.css -app.js */
+/* Tabs ---------------------- */
+dl.tabs { border-bottom: solid 1px #e6e6e6; display: block; height: 40px; padding: 0; margin-bottom: 20px; }
+dl.tabs.contained { margin-bottom: 0; }
+dl.tabs dt { color: #b3b3b3; cursor: default; display: block; float: left; font-size: 12px; height: 40px; line-height: 40px; padding: 0; padding-right: 9px; padding-left: 20px; width: auto; text-transform: uppercase; }
+dl.tabs dt:first-child { padding: 0; padding-right: 9px; }
+dl.tabs dd { display: block; float: left; padding: 0; margin: 0; }
+dl.tabs dd a { color: #6f6f6f; display: block; font-size: 14px; height: 40px; line-height: 40px; padding: 0px 23.8px; }
+dl.tabs dd a:focus { font-weight: bold; color: #fd7800; }
+dl.tabs dd.active { border-top: 3px solid #fd7800; margin-top: -3px; }
+dl.tabs dd.active a { cursor: default; color: #3c3c3c; background: #fff; border-left: 1px solid #e6e6e6; border-right: 1px solid #e6e6e6; font-weight: bold; }
+dl.tabs dd:first-child { margin-left: 0; }
+dl.tabs.vertical { height: auto; border-bottom: 1px solid #e6e6e6; }
+dl.tabs.vertical dt, dl.tabs.vertical dd { float: none; height: auto; }
+dl.tabs.vertical dd { border-left: 3px solid #cccccc; }
+dl.tabs.vertical dd a { background: #f2f2f2; border: none; border: 1px solid #e6e6e6; border-width: 1px 1px 0 0; color: #555; display: block; font-size: 14px; height: auto; line-height: 1; padding: 15px 20px; -webkit-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.5) inset; -moz-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.5) inset; box-shadow: 0 1px 0 rgba(255, 255, 255, 0.5) inset; }
+dl.tabs.vertical dd.active { margin-top: 0; border-top: 1px solid #4d4d4d; border-left: 4px solid #1a1a1a; }
+dl.tabs.vertical dd.active a { background: #4d4d4d; border: none; color: #fff; height: auto; margin: 0; position: static; top: 0; -webkit-box-shadow: 0 0 0; -moz-box-shadow: 0 0 0; box-shadow: 0 0 0; }
+dl.tabs.vertical dd:first-child a.active { margin: 0; }
+dl.tabs.pill { border-bottom: none; margin-bottom: 10px; }
+dl.tabs.pill dd { margin-right: 10px; }
+dl.tabs.pill dd:last-child { margin-right: 0; }
+dl.tabs.pill dd a { -webkit-border-radius: 1000px; -moz-border-radius: 1000px; -ms-border-radius: 1000px; -o-border-radius: 1000px; border-radius: 1000px; background: #e6e6e6; height: 26px; line-height: 26px; color: #666; }
+dl.tabs.pill dd.active { border: none; margin-top: 0; }
+dl.tabs.pill dd.active a { background-color: #fd7800; border: none; color: #fff; }
+dl.tabs.pill.contained { border-bottom: solid 1px #eee; margin-bottom: 0; }
+dl.tabs.pill.two-up dd, dl.tabs.pill.three-up dd, dl.tabs.pill.four-up dd, dl.tabs.pill.five-up dd { margin-right: 0; }
+dl.tabs.two-up dt a, dl.tabs.two-up dd a, dl.tabs.three-up dt a, dl.tabs.three-up dd a, dl.tabs.four-up dt a, dl.tabs.four-up dd a, dl.tabs.five-up dt a, dl.tabs.five-up dd a { padding: 0 17px; text-align: center; overflow: hidden; }
+dl.tabs.two-up dt, dl.tabs.two-up dd { width: 50%; }
+dl.tabs.three-up dt, dl.tabs.three-up dd { width: 33.33%; }
+dl.tabs.four-up dt, dl.tabs.four-up dd { width: 25%; }
+dl.tabs.five-up dt, dl.tabs.five-up dd { width: 20%; }
+
+ul.tabs-content { display: block; margin: 0 0 20px; padding: 0; }
+ul.tabs-content > li { display: none; }
+ul.tabs-content > li.active { display: block; }
+ul.tabs-content.contained { padding: 0; }
+ul.tabs-content.contained > li { border: solid 0 #e6e6e6; border-width: 0 1px 1px 1px; padding: 20px; }
+ul.tabs-content.contained.vertical > li { border-width: 1px 1px 1px 1px; }
+
+.no-js ul.tabs-content > li { display: block; }
+
+@media only screen and (max-width: 767px) { dl.tabs.mobile { width: auto; margin: 20px -20px 40px; height: auto; }
+  dl.tabs.mobile dt, dl.tabs.mobile dd { float: none; height: auto; }
+  dl.tabs.mobile dd a { display: block; width: auto; height: auto; padding: 18px 20px; line-height: 1; border: solid 0 #ccc; border-width: 1px 0 0; margin: 0; color: #555; background: #eee; font-size: 15px; font-size: 1.5rem; }
+  dl.tabs.mobile dd a.active { height: auto; margin: 0; border-width: 1px 0 0; }
+  .tabs.mobile { border-bottom: solid 1px #ccc; height: auto; }
+  .tabs.mobile dd a { padding: 18px 20px; border: none; border-left: none; border-right: none; border-top: 1px solid #ccc; background: #fff; }
+  .tabs.mobile dd a.active { border: none; background: #fd7800; color: #fff; margin: 0; position: static; top: 0; height: auto; }
+  .tabs.mobile dd:first-child a.active { margin: 0; }
+  dl.contained.mobile { margin-bottom: 0; }
+  dl.contained.tabs.mobile dd a { padding: 18px 20px; }
+  dl.tabs.mobile + ul.contained { margin-left: -20px; margin-right: -20px; border-width: 0 0 1px 0; } }
+/* Requires: globals.css */
+/* Table of Contents
+
+:: Visibility
+:: Alerts
+:: Labels
+:: Tooltips
+:: Panels
+:: Accordion
+:: Side Nav
+:: Sub Nav
+:: Pagination
+:: Breadcrumbs
+:: Lists
+:: Link Lists
+:: Keystroke Chars
+:: Image Thumbnails
+:: Video
+:: Tables
+:: Microformats
+:: Progress Bars
+
+*/
+/* Visibility Classes ---------------------- */
+/* Standard (large) display targeting */
+.show-for-small, .show-for-medium, .show-for-medium-down, .hide-for-large, .hide-for-large-up, .show-for-xlarge { display: none !important; }
+
+.hide-for-xlarge, .show-for-large, .show-for-large-up, .hide-for-small, .hide-for-medium, .hide-for-medium-down { display: block !important; }
+
+/* Very large display targeting */
+@media only screen and (min-width: 1441px) { .hide-for-small, .hide-for-medium, .hide-for-medium-down, .hide-for-large, .show-for-large-up, .show-for-xlarge { display: block !important; }
+  .show-for-small, .show-for-medium, .show-for-medium-down, .show-for-large, .hide-for-large-up, .hide-for-xlarge { display: none !important; } }
+/* Medium display targeting */
+@media only screen and (max-width: 1279px) and (min-width: 768px) { .hide-for-small, .show-for-medium, .show-for-medium-down, .hide-for-large, .hide-for-large-up, .hide-for-xlarge { display: block !important; }
+  .show-for-small, .hide-for-medium, .hide-for-medium-down, .show-for-large, .show-for-large-up, .show-for-xlarge { display: none !important; } }
+/* Small display targeting */
+@media only screen and (max-width: 767px) { .show-for-small, .hide-for-medium, .show-for-medium-down, .hide-for-large, .hide-for-large-up, .hide-for-xlarge { display: block !important; }
+  .hide-for-small, .show-for-medium, .hide-for-medium-down, .show-for-large, .show-for-large-up, .show-for-xlarge { display: none !important; } }
+/* Orientation targeting */
+.show-for-landscape, .hide-for-portrait { display: block !important; }
+
+.hide-for-landscape, .show-for-portrait { display: none !important; }
+
+@media screen and (orientation: landscape) { .show-for-landscape, .hide-for-portrait { display: block !important; }
+  .hide-for-landscape, .show-for-portrait { display: none !important; } }
+@media screen and (orientation: portrait) { .show-for-portrait, .hide-for-landscape { display: block !important; }
+  .hide-for-portrait, .show-for-landscape { display: none !important; } }
+/* Touch-enabled device targeting */
+.show-for-touch { display: none !important; }
+
+.hide-for-touch { display: block !important; }
+
+.touch .show-for-touch { display: block !important; }
+
+.touch .hide-for-touch { display: none !important; }
+
+/* Specific overrides for elements that require something other than display: block */
+table.show-for-xlarge, table.show-for-large, table.hide-for-small, table.hide-for-medium { display: table !important; }
+
+@media only screen and (max-width: 1279px) and (min-width: 768px) { .touch table.hide-for-xlarge, .touch table.hide-for-large, .touch table.hide-for-small, .touch table.show-for-medium { display: table !important; } }
+@media only screen and (max-width: 767px) { table.hide-for-xlarge, table.hide-for-large, table.hide-for-medium, table.show-for-small { display: table !important; } }
+/* Alerts ---------------------- */
+div.alert-box { display: block; padding: 6px 7px 7px; font-weight: bold; font-size: 14px; color: white; background-color: #fd7800; border: 1px solid rgba(0, 0, 0, 0.1); margin-bottom: 12px; -webkit-border-radius: 3px; -moz-border-radius: 3px; -ms-border-radius: 3px; -o-border-radius: 3px; border-radius: 3px; text-shadow: 0 -1px rgba(0, 0, 0, 0.3); position: relative; }
+div.alert-box.success { background-color: #5da423; color: #fff; text-shadow: 0 -1px rgba(0, 0, 0, 0.3); }
+div.alert-box.alert { background-color: #c60f13; color: #fff; text-shadow: 0 -1px rgba(0, 0, 0, 0.3); }
+div.alert-box.secondary { background-color: #e9e9e9; color: #505050; text-shadow: 0 1px rgba(255, 255, 255, 0.3); }
+div.alert-box a.close { color: #333; position: absolute; right: 4px; top: -1px; font-size: 17px; opacity: 0.2; padding: 4px; }
+div.alert-box a.close:hover, div.alert-box a.close:focus { opacity: 0.4; }
+
+/* Labels ---------------------- */
+
+
+/* Tooltips ---------------------- */
+.has-tip { border-bottom: dotted 1px #cccccc; cursor: help; font-weight: bold; color: #333333; }
+.has-tip:hover { border-bottom: dotted 1px #196177; color: #fd7800; }
+.has-tip.tip-left, .has-tip.tip-right { float: none !important; }
+
+.tooltip { display: none; background: black; background: rgba(0, 0, 0, 0.85); position: absolute; color: white; font-weight: bold; font-size: 12px; font-size: 1.2rem; padding: 5px; z-index: 999; -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; line-height: normal; }
+.tooltip > .nub { display: block; width: 0; height: 0; border: solid 5px; border-color: transparent transparent black transparent; border-color: transparent transparent rgba(0, 0, 0, 0.85) transparent; position: absolute; top: -10px; left: 10px; }
+.tooltip.tip-override > .nub { border-color: transparent transparent black transparent !important; border-color: transparent transparent rgba(0, 0, 0, 0.85) transparent !important; top: -10px !important; }
+.tooltip.tip-top > .nub { border-color: black transparent transparent transparent; border-color: rgba(0, 0, 0, 0.85) transparent transparent transparent; top: auto; bottom: -10px; }
+.tooltip.tip-left, .tooltip.tip-right { float: none !important; }
+.tooltip.tip-left > .nub { border-color: transparent transparent transparent black; border-color: transparent transparent transparent rgba(0, 0, 0, 0.85); right: -10px; left: auto; }
+.tooltip.tip-right > .nub { border-color: transparent black transparent transparent; border-color: transparent rgba(0, 0, 0, 0.85) transparent transparent; right: auto; left: -10px; }
+.tooltip.noradius { -webkit-border-radius: 0; -moz-border-radius: 0; -ms-border-radius: 0; -o-border-radius: 0; border-radius: 0; }
+.tooltip.opened { color: #fd7800 !important; border-bottom: dotted 1px #196177 !important; }
+
+.tap-to-close { display: block; font-size: 10px; font-size: 1rem; color: #888888; font-weight: normal; }
+
+@media only screen and (max-width: 767px) { .tooltip { font-size: 14px; font-size: 1.4rem; line-height: 1.4; padding: 7px 10px 9px 10px; }
+  .tooltip > .nub, .tooltip.top > .nub, .tooltip.left > .nub, .tooltip.right > .nub { border-color: transparent transparent black transparent; border-color: transparent transparent rgba(0, 0, 0, 0.85) transparent; top: -12px; left: 10px; } }
+/* Panels ---------------------- */
+.panel { background: #f2f2f2; border: solid 1px #e6e6e6; margin: 0 0 22px 0; padding: 20px; }
+.panel > :first-child { margin-top: 0; }
+.panel > :last-child { margin-bottom: 0; }
+.panel.callout { background: #fd7800; color: #fff; border-color: #2284a1; -webkit-box-shadow: inset 0px 1px 0px rgba(255, 255, 255, 0.5); -moz-box-shadow: inset 0px 1px 0px rgba(255, 255, 255, 0.5); box-shadow: inset 0px 1px 0px rgba(255, 255, 255, 0.5); }
+.panel.callout a { color: #fff; }
+.panel.callout .button { background: white; border: none; color: #fd7800; text-shadow: none; }
+.panel.callout .button:hover { background: rgba(255, 255, 255, 0.8); }
+.panel.radius { -webkit-border-radius: 3px; -moz-border-radius: 3px; -ms-border-radius: 3px; -o-border-radius: 3px; border-radius: 3px; }
+
+/* Accordion ---------------------- */
+ul.accordion { margin: 0 0 22px 0; border-bottom: 1px solid #e9e9e9; }
+ul.accordion > li { list-style: none; margin: 0; padding: 0; border-top: 1px solid #e9e9e9; }
+ul.accordion > li .title { cursor: pointer; background: #f6f6f6; padding: 15px; margin: 0; position: relative; border-left: 1px solid #e9e9e9; border-right: 1px solid #e9e9e9; -webkit-transition: 0.15s background linear; -moz-transition: 0.15s background linear; -o-transition: 0.15s background linear; transition: 0.15s background linear; }
+ul.accordion > li .title h1, ul.accordion > li .title h2, ul.accordion > li .title h3, ul.accordion > li .title h4, ul.accordion > li .title h5 { margin: 0; }
+ul.accordion > li .title:after { content: ""; display: block; width: 0; height: 0; border: solid 6px; border-color: transparent #9d9d9d transparent transparent; position: absolute; right: 15px; top: 21px; }
+ul.accordion > li .content { display: none; padding: 15px; }
+ul.accordion > li.active { border-top: 3px solid #fd7800; }
+ul.accordion > li.active .title { background: white; padding-top: 13px; }
+ul.accordion > li.active .title:after { content: ""; display: block; width: 0; height: 0; border: solid 6px; border-color: #9d9d9d transparent transparent transparent; }
+ul.accordion > li.active .content { background: white; display: block; border-left: 1px solid #e9e9e9; border-right: 1px solid #e9e9e9; }
+
+/* Side Nav ---------------------- */
+ul.side-nav { display: block; list-style: none; margin: 0; padding: 17px 0; }
+ul.side-nav li { display: block; list-style: none; margin: 0 0 7px 0; }
+ul.side-nav li a { display: block; }
+ul.side-nav li.active a { color: #4d4d4d; font-weight: bold; }
+ul.side-nav li.divider { border-top: 1px solid #e6e6e6; height: 0; padding: 0; }
+
+/* Sub Navs http://www.zurb.com/article/292/how-to-create-simple-and-effective-sub-na ---------------------- */
+dl.sub-nav { display: block; width: auto; overflow: hidden; margin: -4px 0 18px; margin-right: 0; margin-left: -9px; padding-top: 4px; }
+dl.sub-nav dt, dl.sub-nav dd { float: left; display: inline; margin-left: 9px; margin-bottom: 10px; }
+dl.sub-nav dt { color: #999; font-weight: normal; }
+dl.sub-nav dd a { text-decoration: none; -webkit-border-radius: 1000px; -moz-border-radius: 1000px; -ms-border-radius: 1000px; -o-border-radius: 1000px; border-radius: 1000px; }
+dl.sub-nav dd.active a { font-weight: bold; background: #fd7800; color: #fff; padding: 3px 9px; cursor: default; }
+
+/* Pagination ---------------------- */
+ul.pagination { display: block; height: 24px; margin-left: -5px; }
+ul.pagination li { float: left; display: block; height: 24px; color: #999; font-size: 14px; margin-left: 5px; }
+ul.pagination li a { display: block; padding: 1px 7px 1px; color: #555; }
+ul.pagination li:hover a, ul.pagination li a:focus { background: #e6e6e6; }
+ul.pagination li.unavailable a { cursor: default; color: #999; }
+ul.pagination li.unavailable:hover a, ul.pagination li.unavailable a:focus { background: transparent; }
+ul.pagination li.current a { background: #fd7800; color: white; font-weight: bold; cursor: default; }
+ul.pagination li.current a:hover { background: #fd7800; }
+
+/* Breadcrums ---------------------- */
+ul.breadcrumbs { display: block; background: #f6f6f6; padding: 6px 10px 7px; border: 1px solid #e9e9e9; -webkit-border-radius: 2px; -moz-border-radius: 2px; -ms-border-radius: 2px; -o-border-radius: 2px; border-radius: 2px; overflow: hidden; }
+ul.breadcrumbs li { margin: 0; padding: 0 12px 0 0; float: left; list-style: none; }
+ul.breadcrumbs li a, ul.breadcrumbs li span { text-transform: uppercase; font-size: 11px; font-size: 1.1rem; padding-left: 12px; }
+ul.breadcrumbs li:first-child a, ul.breadcrumbs li:first-child span { padding-left: 0; }
+ul.breadcrumbs li:before { content: "/"; color: #aaa; }
+ul.breadcrumbs li:first-child:before { content: " "; }
+ul.breadcrumbs li.current a { cursor: default; color: #333; }
+ul.breadcrumbs li:hover a, ul.breadcrumbs li a:focus { text-decoration: underline; }
+ul.breadcrumbs li.current:hover a, ul.breadcrumbs li.current a:focus { text-decoration: none; }
+ul.breadcrumbs li.unavailable a { color: #999; }
+ul.breadcrumbs li.unavailable:hover a, ul.breadcrumbs li.unavailable a:focus { text-decoration: none; color: #999; cursor: default; }
+
+/* Link List */
+ul.link-list { margin: 0 0 17px -22px; padding: 0; list-style: none; overflow: hidden; }
+ul.link-list li { list-style: none; float: left; margin-left: 22px; display: block; }
+ul.link-list li a { display: block; }
+
+/* Keytroke Characters ---------------------- */
+.keystroke, kbd { font-family: "Consolas", "Menlo", "Courier", monospace; font-size: 13px; padding: 2px 4px 0px; margin: 0; background: #ededed; border: solid 1px #dbdbdb; -webkit-border-radius: 3px; -moz-border-radius: 3px; -ms-border-radius: 3px; -o-border-radius: 3px; border-radius: 3px; }
+
+/* Image Thumbnails ---------------------- */
+.th { display: block; }
+.th img { display: block; border: solid 4px #fff; -webkit-box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.2); -moz-box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.2); box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.2); -webkit-border-radius: 3px; -moz-border-radius: 3px; -ms-border-radius: 3px; -o-border-radius: 3px; border-radius: 3px; -webkit-transition-property: border, box-shadow; -moz-transition-property: border, box-shadow; -o-transition-property: border, box-shadow; transition-property: border, box-shadow; -webkit-transition-duration: 300ms; -moz-transition-duration: 300ms; -o-transition-duration: 300ms; transition-duration: 300ms; }
+.th:hover img { -webkit-box-shadow: 0 0 6px 1px rgba(43, 166, 203, 0.5); -moz-box-shadow: 0 0 6px 1px rgba(43, 166, 203, 0.5); box-shadow: 0 0 6px 1px rgba(43, 166, 203, 0.5); }
+
+/* Video - Mad props to http://www.alistapart.com/articles/creating-intrinsic-ratios-for-video/ ---------------------- */
+.flex-video { position: relative; padding-top: 25px; padding-bottom: 67.5%; height: 0; margin-bottom: 16px; overflow: hidden; }
+.flex-video.widescreen { padding-bottom: 57.25%; }
+.flex-video.vimeo { padding-top: 0; }
+.flex-video iframe, .flex-video object, .flex-video embed, .flex-video video { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
diff --git a/NFD/docs/named_data_theme/static/named_data_doxygen.css b/NFD/docs/named_data_theme/static/named_data_doxygen.css
new file mode 100644
index 0000000..02bcbf6
--- /dev/null
+++ b/NFD/docs/named_data_theme/static/named_data_doxygen.css
@@ -0,0 +1,776 @@
+@import url("base.css");
+
+@import url("foundation.css");
+
+table {
+      border: 0;
+}
+
+pre {
+    padding: 10px;
+    background-color: #fafafa;
+    color: #222;
+    line-height: 1.0em;
+    border: 2px solid #C6C9CB;
+    font-size: 0.9em;
+    /* margin: 1.5em 0 1.5em 0; */
+    margin: 0;
+    border-right-style: none;
+    border-left-style: none;
+}
+
+/* General */
+/*////////////////////////////////////////////////////////////////////////////////////////////*/
+
+a:link {
+    text-decoration: none;
+}
+a:visited {
+    text-decoration: none;
+}
+a:active,
+a:hover {
+    text-decoration: none;
+}
+
+h1,h2,h3,h4,h5,h6 {
+    color: #000;
+    margin-bottom: 18px;
+}
+
+h1 { font-weight: normal; font-size: 24px; line-height: 24px;  }
+h2 { font-weight: normal; font-size: 18px; line-height: 18px;  }
+h3 { font-weight: bold;   font-size: 18px; line-height: 18px; }
+h4 { font-weight: normal; font-size: 18px; line-height: 178px; }
+
+hr {
+    background-color: #c6c6c6;
+    border:0;
+    height: 1px;
+    margin-bottom: 18px;
+    clear:both;
+}
+
+div.hr {
+  height: 1px;
+  background: #c6c6c6;
+}
+
+div.hr2 {
+  height: 1px;
+  background: #c6c6c6;
+}
+
+div.hr hr, div.hr2 hr {
+  display: none;
+}
+
+p {
+    padding: 0 0 0.5em;
+    line-height:1.6em;
+}
+ul {
+    list-style: square;
+    margin: 0 0 18px 0;
+}
+ol {
+    list-style: decimal;
+    margin: 0 0 18px 1.5em;
+}
+ol ol {
+    list-style:upper-alpha;
+}
+ol ol ol {
+    list-style:lower-roman;
+}
+ol ol ol ol {
+    list-style:lower-alpha;
+}
+ul ul,
+ol ol,
+ul ol,
+ol ul {
+    margin-bottom:0;
+}
+dl {
+    margin:0 0 24px 0;
+}
+dt {
+    font-weight: bold;
+}
+dd {
+    margin-bottom: 18px;
+}
+strong {
+    font-weight: bold;
+    color: #000;
+}
+cite,
+em,
+i {
+    font-style: italic;
+    border: none;
+}
+big {
+    font-size: 131.25%;
+}
+ins {
+    background: #FFFFCC;
+    border: none;
+    color: #333;
+}
+del {
+    text-decoration: line-through;
+    color: #555;
+}
+blockquote {
+    font-style: italic;
+    padding: 0 3em;
+}
+blockquote cite,
+blockquote em,
+blockquote i {
+    font-style: normal;
+}
+pre {
+    background: #f7f7f7;
+    color: #222;
+    padding: 1.5em;
+}
+abbr,
+acronym {
+    border-bottom: 1px solid #666;
+    cursor: help;
+}
+ins {
+    text-decoration: none;
+}
+sup,
+sub {
+    height: 0;
+    line-height: 1;
+    vertical-align: baseline;
+    position: relative;
+    font-size: 10px;
+}
+sup {
+    bottom: 1ex;
+}
+sub {
+    top: .5ex;
+}
+
+p,
+ul,
+ol,
+dd,
+hr {
+    margin-bottom:10px;
+}
+ul ul,
+ol ol,
+ul ol,
+ol ul {
+    margin-bottom:0;
+}
+pre,
+kbd,
+tt,
+var {
+}
+code {
+    font-size: 13px;
+}
+strong,
+b,
+dt,
+th {
+    color: #000;
+}
+
+
+/* main_container */
+/*////////////////////////////////////////////////////////////////////////////////////////////*/
+
+#wrapper {
+    padding: 0px 0px;
+    margin-top: 20px;
+}
+
+/* header*/
+/*////////////////////////////////////////////////////////////////////////////////////////////*/
+
+#search-header{
+    margin-top:15px;
+    padding-bottom:13px;
+}
+
+#search-header #search{
+    background: #222;
+
+}
+
+#search-header #search #s{
+    background: #222;
+    font-size:12px;
+    color: #aaa;
+}
+
+#header_container{
+    padding-bottom: 25px;
+    padding-top: 0px;
+    background: #fff;
+}
+
+#header {
+
+}
+
+#header2 {
+
+}
+
+#content_container{
+    padding-top: 15px;
+}
+
+#left-col {
+    padding: 10px 20px;
+    padding-left: 0px;
+    background: #fff;
+
+}
+
+
+/*footer*/
+/*////////////////////////////////////////////////////////////////////////////////////////////*/
+
+
+#footer {
+    padding: 5px 20px;
+    background: #ddd;
+}
+
+#footer-container{
+    padding: 5px 20px;
+    background: #303030;
+    border-top: 8px solid #000;
+    font-size:11px;
+}
+
+#footer-info {
+    color:#ccc;
+    text-align:left;
+    background: #1b1b1b;
+    padding: 20px 0;
+}
+
+
+#footer-info a{
+    text-decoration:none;
+    color: #fff;
+}
+
+#footer-info a:hover{
+    color: #ebebeb;
+}
+
+#copyright{float: left;}
+
+.scroll-top {
+    text-align:right;
+}
+
+#footer-widget{
+    padding: 8px 0px 8px 0px;
+    color:#6f6f6f;
+}
+
+#footer-widget #search {
+    width:120px;
+    height:28px;
+    background: #222;
+    margin-left: 0px;
+    position: relative;
+    border: 1px solid #666;
+}
+
+#footer-widget #search #s {
+    width:110px;
+    height:23px;
+    border:0px;
+    margin-left:7px;
+    margin-right:10px;
+    margin-top:3px;
+    color:#fff;
+    display: inline;
+    background: #222;
+    float: left;
+}
+
+#footer-widget #calendar_wrap {
+    padding: 8px 0px;
+}
+
+#footer-widget #wp-calendar td{
+    padding:2px;
+}
+
+
+#footer-widget .textwidget {
+    padding: 5px 0px;
+    line-height: 23px;
+}
+
+
+#footer-widget .widget_tag_cloud a{
+    text-decoration: none;
+    margin: 5px;
+    line-height: 24px;
+    margin-left: 0px;
+    color: #6f6f6f;
+}
+
+#footer-widget .widget_tag_cloud a:hover{
+    color: #fff;
+}
+
+#footer-widget .widget-container ul li a    {
+    color:#fd7800;
+}
+
+#footer-widget .widget-container ul li a:hover    {
+    color: #ccc;
+}
+
+#footer-widget .widget-container h3 {
+    color: #a5a5a5;
+    text-transform: uppercase;
+    margin-bottom: 0px;
+    padding-top: 10px;
+    padding-left: 0px;
+    font-size: 25px;
+    padding-bottom: 8px;
+    font-weight: bold;
+}
+
+#footer-widget .widget-container ul li {
+    padding: 5px 0px;
+    background: none;
+    }
+
+#footer-widget ul {
+    margin-left: 0px;
+    }
+
+#footer-bar1 {
+    padding-right: 40px;
+}
+#footer-bar2 {
+    padding-right: 40px;
+}
+#footer-bar3 {
+}
+#footer-bar4 {
+}
+
+span#follow-box{
+    position: absolute;
+    right: 100px;
+}
+
+span#follow-box img{
+    margin: 0 2px;
+}
+
+/*logo*/
+/*////////////////////////////////////////////////////////////////////////////////////////////*/
+
+#logo {
+    margin: 0px 0px 0px 0px;
+}
+
+#logo2 {
+    margin: 0px 0px 0px 0px;
+}
+
+#logo img{
+    border: none;
+}
+
+#logo2{
+    text-decoration: none;
+    font-size: 42px;
+    letter-spacing: -1pt;
+    font-weight: bold;
+    font-family:arial, "Times New Roman", Times, serif;
+    text-align: left;
+    line-height: 57px;
+    padding-left: 0px;
+}
+
+#logo2 a, #slogan{
+    color: #fd7800;
+}
+
+#slogan{
+    text-align: left;
+    padding-left: 0px;
+}
+
+/*search*/
+/*////////////////////////////////////////////////////////////////////////////////////////////*/
+
+#search {
+    width:180px;
+    height:28px;
+    border: 1px solid #ccc;
+    margin-left: 10px;
+    position: relative;
+}
+
+#sidebar #search {
+    margin-top: 20px;
+}
+
+#search #searchsubmit {
+    background:url(images/go-btn.png) no-repeat top right;
+    width:28px;
+    height:28px;
+    border:0px;
+    position:absolute;
+    right: -35px;
+}
+
+#search #s {
+    width:170px;
+    height:23px;
+    border:0px;
+    margin-left:7px;
+    margin-right:10px;
+    margin-top:3px;
+    color:#000;
+    display: inline;
+    float: left;
+}
+
+/*menu bar*/
+/*////////////////////////////////////////////////////////////////////////////////////////////*/
+
+#menu_container{
+    padding-top: 0px;
+}
+
+
+/*responsive menu*/
+/*////////////////////////////////////////////////////////////////////////////////////////////*/
+
+/* default style */
+.selectnav { display: none; }
+
+/* small screen */
+@media screen and (max-width: 600px) {
+  .js #nav { display: none; }
+   .js #nav2 { display: none; }
+  .js .selectnav { display: block; }
+}
+
+
+/*welcome*/
+/*////////////////////////////////////////////////////////////////////////////////////////////*/
+#welcome_container h1{
+    margin-top: 0px;
+}
+
+/*homepage boxes*/
+/*////////////////////////////////////////////////////////////////////////////////////////////*/
+
+#box_container{
+    padding-top: 35px;
+    padding-bottom: 15px;
+}
+
+.box-head {
+    float: left;
+    padding-bottom: 20px;
+}
+
+.box-head img{
+
+}
+
+.title-head{
+    padding-top:2px;
+}
+
+.title-box{
+    color: #333;
+    line-height: 15px;
+    text-transform: uppercase;
+}
+
+.title-box h1 {
+    font-size: 18px;
+    margin-bottom: 3px;
+}
+
+.box-content {
+    float: left;
+    padding-top: 10px;
+    line-height: 20px;
+}
+
+
+/* POST */
+/*////////////////////////////////////////////////////////////////////////////////////////////*/
+
+
+.post {
+    overflow: hidden;
+
+}
+
+.post-shadow{
+    background: url("images/post_shadow.png") no-repeat bottom;
+    height: 9px;
+    margin-bottom: 25px;
+}
+
+.post ol{
+    margin-left: 20px;
+}
+
+.post ul {
+    margin-left: 15px;
+}
+.post-entry ul { margin: 0 0 10px 10px; }
+.post-entry ul li {
+    display: block;
+    margin: 5px 0;
+    padding: 0 0 0 20px;
+    /*background: url(images/bullet.png) no-repeat 0 7px;*/
+}
+
+.post-entry ol {
+    list-style: decimal;
+    margin: 0 0 18px 1.6em;
+}
+.post-entry ol li {
+    list-style: decimal;
+ }
+
+.post-entry {
+    padding-bottom: 10px;
+    padding-top: 10px;
+    overflow: hidden;
+
+}
+
+.post-head {
+    margin-bottom: 5px;
+    padding-top: 15px;
+}
+
+.post-head h1 a, .post-head h1 {
+    text-decoration:none;
+    color:#000;
+    margin: 0px;
+    font-size: 27px;
+}
+
+.post-head h1 a:hover {
+    color:#777;
+}
+
+
+.post-head-notfound h1, .post-head-404 h1, .post-head-archive h1, .post-head-search h1 {
+    margin-bottom: 10px;
+    font-weight:normal;
+    text-decoration:none;
+    color:#000;
+    font-size: 27px;
+}
+
+.post-thumb img {
+    border: 0px solid #ebebeb;
+}
+
+.post-entry img{
+    margin-bottom: 10px;
+    height:auto;
+    max-width:100% !important;
+}
+
+.meta-data{
+    line-height: 16px;
+    padding: 6px 3px;
+    margin-bottom: 3px;
+    font-size: 11px;
+    border-bottom: 1px solid #e9e9e9;
+}
+
+.meta-data a{
+    color: #fd7800;
+}
+
+.meta-data a:hover{
+    color: #777;
+}
+
+.read-more {
+color: #000;
+    background: #fff;
+      padding: 4px 8px;
+      border-radius: 3px;
+      display: inline-block;
+      font-size: 11px;
+      font-weight: bold;
+      text-decoration: none;
+      text-transform: capitalize;
+      cursor: pointer;
+      margin-top: 20px;
+}
+
+.read-more:hover{
+    background: #fff;
+    color: #666;
+}
+
+.clear {
+    clear:both;
+}
+
+.sticky {
+
+}
+
+/* content */
+/*////////////////////////////////////////////////////////////////////////////////////////////*/
+#content_container table {
+    border: 1px solid #e7e7e7;
+    margin: 0 -1px 24px 0;
+    text-align: left;
+    width: 100%;
+
+}
+#content_container tr th,
+#content_container thead th {
+    color: #888;
+    font-size: 12px;
+    font-weight: bold;
+    line-height: 18px;
+    padding: 9px 10px;
+}
+#content_container tr td {
+
+    padding: 6px 10px;
+}
+#content_container tr.odd td {
+    background: #f2f7fc;
+}
+
+/*--navigation--*/
+/*////////////////////////////////////////////////////////////////////////////////////////////*/
+
+.navigation {
+    float: left;
+    width: 100%;
+    margin: 20px 0;
+}
+
+
+.navigation .alignleft a {
+    float: left;
+}
+
+.navigation .alignright a {
+    float: right;
+}
+
+#nav-single {
+    overflow:hidden;
+    margin-top:20px;
+    margin-bottom:10px;
+}
+.nav-previous {
+    float: left;
+    width: 50%;
+}
+.nav-next {
+    float: right;
+    text-align: right;
+    width: 50%;
+}
+
+/*--sub head and breadcrumbs--*/
+/*////////////////////////////////////////////////////////////////////////////////////////////*/
+
+#subhead_container{
+    padding: 7px 0px;
+}
+
+#subhead h1{
+    color: #000;
+    padding-top: 10px;
+    padding-left: 0px;
+    font-size: 30px;
+}
+
+#breadcrumbs {
+    padding-left: 25px;
+    margin-bottom: 15px;
+    color: #9e9e9e;
+    margin:0 auto;
+    width: 964px;
+    font-size: 10px;
+}
+
+#breadcrumbs a{
+    text-decoration: none;
+    color: #9e9e9e;
+}
+
+/*Alignments */
+/*////////////////////////////////////////////////////////////////////////////////////////////*/
+
+.alignleft,
+img.alignleft {
+    display: inline;
+    float: left;
+    margin-right: 22px;
+    margin-top: 9px;
+}
+
+.alignright,
+img.alignright {
+    display: inline;
+    float: right;
+    margin-left: 22px;
+    margin-top: 8px;
+}
+.aligncenter,
+img.aligncenter {
+    clear: both;
+    display: block;
+    margin-left: auto;
+    margin-right: auto;
+}
+
+.alignleft,
+.alignright,
+.aligncenter,
+img.alignleft,
+img.alignright,
+img.aligncenter
+{
+    margin-bottom: 10px;
+}
+
+
+a img.aligncenter {
+    display:block;
+    margin-left:auto;
+    margin-right:auto;
+}
+
+img { -webkit-box-sizing: content-box; -moz-box-sizing: content-box; box-sizing: content-box; }
diff --git a/NFD/docs/named_data_theme/static/named_data_style.css_t b/NFD/docs/named_data_theme/static/named_data_style.css_t
new file mode 100644
index 0000000..3edfb72
--- /dev/null
+++ b/NFD/docs/named_data_theme/static/named_data_style.css_t
@@ -0,0 +1,813 @@
+@import url("base.css");
+
+@import url("foundation.css");
+
+table {
+      border: 0;
+}
+
+pre {
+    padding: 10px;
+    background-color: #fafafa;
+    color: #222;
+    /* line-height: 1.0em; */
+    border: 2px solid #C6C9CB;
+    font-size: 0.9em;
+    /* margin: 1.5em 0 1.5em 0; */
+    margin: 0;
+    border-right-style: none;
+    border-left-style: none;
+}
+
+/* General */
+/*////////////////////////////////////////////////////////////////////////////////////////////*/
+
+a:link {
+    text-decoration: none;
+}
+a:visited {
+    text-decoration: none;
+}
+a:active,
+a:hover {
+    text-decoration: none;
+}
+
+h1,h2,h3,h4,h5,h6 {
+    color: #000;
+    margin-bottom: 18px;
+}
+
+h1 { font-weight: bold; font-size: 24px; }
+h2 { font-weight: bold; font-size: 18px; }
+h3 { font-weight: bold; font-size: 16px; }
+h4 { font-weight: bold; font-size: 14px; }
+
+hr {
+    background-color: #c6c6c6;
+    border:0;
+    height: 1px;
+    margin-bottom: 18px;
+    clear:both;
+}
+
+div.hr {
+  height: 1px;
+  background: #c6c6c6;
+}
+
+div.hr2 {
+  height: 1px;
+  background: #c6c6c6;
+}
+
+div.hr hr, div.hr2 hr {
+  display: none;
+}
+
+p {
+    padding: 0;
+    line-height:1.6em;
+}
+ul {
+    list-style: square;
+    margin: 0 0 18px 0;
+}
+ol {
+    list-style: decimal;
+    margin: 0 0 18px 1.5em;
+}
+ol ol {
+    list-style:upper-alpha;
+}
+ol ol ol {
+    list-style:lower-roman;
+}
+ol ol ol ol {
+    list-style:lower-alpha;
+}
+ul ul,
+ol ol,
+ul ol,
+ol ul {
+    margin-bottom:0;
+}
+dl {
+    margin:0 0 24px 0;
+}
+dt {
+    font-weight: bold;
+}
+dd {
+    margin-bottom: 18px;
+}
+strong {
+    font-weight: bold;
+    color: #000;
+}
+cite,
+em,
+i {
+    font-style: italic;
+    border: none;
+}
+big {
+    font-size: 131.25%;
+}
+ins {
+    background: #FFFFCC;
+    border: none;
+    color: #333;
+}
+del {
+    text-decoration: line-through;
+    color: #555;
+}
+blockquote {
+    padding: 0 3em;
+}
+blockquote cite,
+blockquote em,
+blockquote i {
+    font-style: normal;
+}
+pre {
+    background: #f7f7f7;
+    color: #222;
+    padding: 1.5em;
+}
+abbr,
+acronym {
+    border-bottom: 1px solid #666;
+    cursor: help;
+}
+ins {
+    text-decoration: none;
+}
+sup,
+sub {
+    height: 0;
+    line-height: 1;
+    vertical-align: baseline;
+    position: relative;
+    font-size: 10px;
+}
+sup {
+    bottom: 1ex;
+}
+sub {
+    top: .5ex;
+}
+
+p,
+ul,
+ol,
+dd,
+hr {
+    margin-bottom:10px;
+}
+ul ul,
+ol ol,
+ul ol,
+ol ul {
+    margin-bottom:0;
+}
+pre,
+kbd,
+tt,
+var {
+}
+code {
+    font-size: 13px;
+}
+strong,
+b,
+dt,
+th {
+    color: #000;
+}
+
+
+/* main_container */
+/*////////////////////////////////////////////////////////////////////////////////////////////*/
+
+#wrapper {
+    padding: 0px 0px;
+    margin-top: 20px;
+}
+
+/* header*/
+/*////////////////////////////////////////////////////////////////////////////////////////////*/
+
+#search-header{
+    margin-top:15px;
+    padding-bottom:13px;
+}
+
+#search-header #search{
+    background: #222;
+
+}
+
+#search-header #search #s{
+    background: #222;
+    font-size:12px;
+    color: #aaa;
+}
+
+#header_container{
+    padding-bottom: 25px;
+    padding-top: 0px;
+    background: #fff;
+}
+
+#header {
+
+}
+
+#header2 {
+
+}
+
+#content_container{
+    padding-top: 15px;
+}
+
+#left-col {
+    padding: 10px 20px;
+    padding-left: 0px;
+    background: #fff;
+
+}
+
+
+/*footer*/
+/*////////////////////////////////////////////////////////////////////////////////////////////*/
+
+
+#footer {
+    padding: 5px 20px;
+    background: #ddd;
+}
+
+#footer-container{
+    padding: 5px 20px;
+    background: #303030;
+    border-top: 8px solid #000;
+    font-size:11px;
+}
+
+#footer-info {
+    color:#ccc;
+    text-align:left;
+    background: #1b1b1b;
+    padding: 20px 0;
+}
+
+
+#footer-info a{
+    text-decoration:none;
+    color: #fff;
+}
+
+#footer-info a:hover{
+    color: #ebebeb;
+}
+
+#copyright{float: left;}
+
+.scroll-top {
+    text-align:right;
+}
+
+#footer-widget{
+    padding: 8px 0px 8px 0px;
+    color:#6f6f6f;
+}
+
+#footer-widget #search {
+    width:120px;
+    height:28px;
+    background: #222;
+    margin-left: 0px;
+    position: relative;
+    border: 1px solid #666;
+}
+
+#footer-widget #search #s {
+    width:110px;
+    height:23px;
+    border:0px;
+    margin-left:7px;
+    margin-right:10px;
+    margin-top:3px;
+    color:#fff;
+    display: inline;
+    background: #222;
+    float: left;
+}
+
+#footer-widget #calendar_wrap {
+    padding: 8px 0px;
+}
+
+#footer-widget #wp-calendar td{
+    padding:2px;
+}
+
+
+#footer-widget .textwidget {
+    padding: 5px 0px;
+    line-height: 23px;
+}
+
+
+#footer-widget .widget_tag_cloud a{
+    text-decoration: none;
+    margin: 5px;
+    line-height: 24px;
+    margin-left: 0px;
+    color: #6f6f6f;
+}
+
+#footer-widget .widget_tag_cloud a:hover{
+    color: #fff;
+}
+
+#footer-widget .widget-container ul li a    {
+    color:#fd7800;
+}
+
+#footer-widget .widget-container ul li a:hover    {
+    color: #ccc;
+}
+
+#footer-widget .widget-container h3 {
+    color: #a5a5a5;
+    text-transform: uppercase;
+    margin-bottom: 0px;
+    padding-top: 10px;
+    padding-left: 0px;
+    font-size: 25px;
+    padding-bottom: 8px;
+    font-weight: bold;
+}
+
+#footer-widget .widget-container ul li {
+    padding: 5px 0px;
+    background: none;
+    }
+
+#footer-widget ul {
+    margin-left: 0px;
+    }
+
+#footer-bar1 {
+    padding-right: 40px;
+}
+#footer-bar2 {
+    padding-right: 40px;
+}
+#footer-bar3 {
+}
+#footer-bar4 {
+}
+
+span#follow-box{
+    position: absolute;
+    right: 100px;
+}
+
+span#follow-box img{
+    margin: 0 2px;
+}
+
+/*logo*/
+/*////////////////////////////////////////////////////////////////////////////////////////////*/
+
+#logo {
+    margin: 0px 0px 0px 0px;
+}
+
+#logo2 {
+    margin: 0px 0px 0px 0px;
+}
+
+#logo img{
+    border: none;
+}
+
+#logo2{
+    text-decoration: none;
+    font-size: 42px;
+    letter-spacing: -1pt;
+    font-weight: bold;
+    font-family:arial, "Times New Roman", Times, serif;
+    text-align: left;
+    line-height: 57px;
+    padding-left: 0px;
+}
+
+#logo2 a, #slogan{
+    color: #fd7800;
+}
+
+#slogan{
+    text-align: left;
+    padding-left: 0px;
+}
+
+/*search*/
+/*////////////////////////////////////////////////////////////////////////////////////////////*/
+
+#search {
+    width:180px;
+    height:28px;
+    border: 1px solid #ccc;
+    margin-left: 10px;
+    position: relative;
+}
+
+#sidebar #search {
+    margin-top: 20px;
+}
+
+#search #searchsubmit {
+    background:url(images/go-btn.png) no-repeat top right;
+    width:28px;
+    height:28px;
+    border:0px;
+    position:absolute;
+    right: -35px;
+}
+
+#search #s {
+    width:170px;
+    height:23px;
+    border:0px;
+    margin-left:7px;
+    margin-right:10px;
+    margin-top:3px;
+    color:#000;
+    display: inline;
+    float: left;
+}
+
+/*menu bar*/
+/*////////////////////////////////////////////////////////////////////////////////////////////*/
+
+#menu_container{
+    padding-top: 0px;
+}
+
+
+/*responsive menu*/
+/*////////////////////////////////////////////////////////////////////////////////////////////*/
+
+/* default style */
+.selectnav { display: none; }
+
+/* small screen */
+@media screen and (max-width: 600px) {
+  .js #nav { display: none; }
+   .js #nav2 { display: none; }
+  .js .selectnav { display: block; }
+}
+
+
+/*welcome*/
+/*////////////////////////////////////////////////////////////////////////////////////////////*/
+#welcome_container h1{
+    margin-top: 0px;
+}
+
+/*homepage boxes*/
+/*////////////////////////////////////////////////////////////////////////////////////////////*/
+
+#box_container{
+    padding-top: 35px;
+    padding-bottom: 15px;
+}
+
+.box-head {
+    float: left;
+    padding-bottom: 20px;
+}
+
+.box-head img{
+
+}
+
+.title-head{
+    padding-top:2px;
+}
+
+.title-box{
+    color: #333;
+    line-height: 15px;
+    text-transform: uppercase;
+}
+
+.title-box h1 {
+    font-size: 18px;
+    margin-bottom: 3px;
+}
+
+.box-content {
+    float: left;
+    padding-top: 10px;
+    line-height: 20px;
+}
+
+
+/* POST */
+/*////////////////////////////////////////////////////////////////////////////////////////////*/
+
+
+.post {
+    overflow: hidden;
+
+}
+
+.post-shadow{
+    background: url("images/post_shadow.png") no-repeat bottom;
+    height: 9px;
+    margin-bottom: 25px;
+}
+
+.post ol{
+    margin-left: 20px;
+}
+
+.post ul {
+    margin-left: 15px;
+}
+.post-entry ul { margin: 0 0 10px 10px; }
+.post-entry ul li {
+    display: block;
+    margin: 5px 0;
+    padding: 0 0 0 20px;
+    /*background: url(images/bullet.png) no-repeat 0 7px;*/
+}
+
+.post-entry ol {
+    list-style: decimal;
+    margin: 0 0 18px 1.6em;
+}
+.post-entry ol li {
+    list-style: decimal;
+ }
+
+.post-entry {
+    padding-bottom: 10px;
+    padding-top: 10px;
+    overflow: hidden;
+
+}
+
+.post-head {
+    margin-bottom: 5px;
+    padding-top: 15px;
+}
+
+.post-head h1 a, .post-head h1 {
+    text-decoration:none;
+    color:#000;
+    margin: 0px;
+    font-size: 27px;
+}
+
+.post-head h1 a:hover {
+    color:#777;
+}
+
+
+.post-head-notfound h1, .post-head-404 h1, .post-head-archive h1, .post-head-search h1 {
+    margin-bottom: 10px;
+    font-weight:normal;
+    text-decoration:none;
+    color:#000;
+    font-size: 27px;
+}
+
+.post-thumb img {
+    border: 0px solid #ebebeb;
+}
+
+.post-entry img{
+    margin-bottom: 10px;
+    height:auto;
+      max-width:100% !important;
+}
+
+.meta-data{
+    line-height: 16px;
+    padding: 6px 3px;
+    margin-bottom: 3px;
+    font-size: 11px;
+    border-bottom: 1px solid #e9e9e9;
+}
+
+.meta-data a{
+    color: #fd7800;
+}
+
+.meta-data a:hover{
+    color: #777;
+}
+
+.read-more {
+color: #000;
+    background: #fff;
+      padding: 4px 8px;
+      border-radius: 3px;
+      display: inline-block;
+      font-size: 11px;
+      font-weight: bold;
+      text-decoration: none;
+      text-transform: capitalize;
+      cursor: pointer;
+      margin-top: 20px;
+}
+
+.read-more:hover{
+    background: #fff;
+    color: #666;
+}
+
+.clear {
+    clear:both;
+}
+
+.sticky {
+
+}
+
+/* content */
+/*////////////////////////////////////////////////////////////////////////////////////////////*/
+#content_container table {
+    border: 1px solid #e7e7e7;
+    margin: 0 -1px 24px 0;
+    text-align: left;
+    width: 100%;
+
+}
+#content_container tr th,
+#content_container thead th {
+    color: #888;
+    font-size: 12px;
+    font-weight: bold;
+    line-height: 18px;
+    padding: 9px 10px;
+}
+#content_container tr td {
+
+    padding: 6px 10px;
+}
+#content_container tr.odd td {
+    background: #f2f7fc;
+}
+
+/* sidebar*/
+/*////////////////////////////////////////////////////////////////////////////////////////////*/
+
+#sidebar {
+    padding:0px 20px 20px 0px;
+}
+
+#sidebar ul  {
+    list-style: none;
+}
+
+#sidebar { word-wrap: break-word;}
+
+
+/*--navigation--*/
+/*////////////////////////////////////////////////////////////////////////////////////////////*/
+
+.navigation {
+    float: left;
+    width: 100%;
+    margin: 20px 0;
+}
+
+
+.navigation .alignleft a {
+    float: left;
+}
+
+.navigation .alignright a {
+    float: right;
+}
+
+#nav-single {
+    overflow:hidden;
+    margin-top:20px;
+    margin-bottom:10px;
+}
+.nav-previous {
+    float: left;
+    width: 50%;
+}
+.nav-next {
+    float: right;
+    text-align: right;
+    width: 50%;
+}
+
+/*--slider--*/
+/*////////////////////////////////////////////////////////////////////////////////////////////*/
+
+#slider_container {
+    background: #fff;
+}
+
+.flex-caption{
+background: #232323;
+color: #fff;
+padding: 7px;
+}
+
+.flexslider p{
+    margin: 0px;
+}
+
+/*--sub head and breadcrumbs--*/
+/*////////////////////////////////////////////////////////////////////////////////////////////*/
+
+#subhead_container{
+    padding: 7px 0px;
+}
+
+#subhead h1{
+    color: #000;
+    padding-top: 10px;
+    padding-left: 0px;
+    font-size: 30px;
+}
+
+#breadcrumbs {
+    padding-left: 25px;
+    margin-bottom: 15px;
+    color: #9e9e9e;
+    margin:0 auto;
+    width: 964px;
+    font-size: 10px;
+}
+
+#breadcrumbs a{
+    text-decoration: none;
+    color: #9e9e9e;
+}
+
+/*Alignments */
+/*////////////////////////////////////////////////////////////////////////////////////////////*/
+
+.alignleft,
+img.alignleft {
+    display: inline;
+    float: left;
+    margin-right: 22px;
+    margin-top: 9px;
+}
+
+.alignright,
+img.alignright {
+    display: inline;
+    float: right;
+    margin-left: 22px;
+    margin-top: 8px;
+}
+.aligncenter,
+img.aligncenter {
+    clear: both;
+    display: block;
+    margin-left: auto;
+    margin-right: auto;
+}
+
+.alignleft,
+.alignright,
+.aligncenter,
+img.alignleft,
+img.alignright,
+img.aligncenter
+{
+    margin-bottom: 10px;
+}
+
+
+a img.aligncenter {
+    display:block;
+    margin-left:auto;
+    margin-right:auto;
+}
+
+
+table {
+    border-collapse:collapse;
+}
+table, th, td {
+    border: 1px solid black;
+    padding: 5px;
+}
\ No newline at end of file
diff --git a/NFD/docs/named_data_theme/static/nav_f.png b/NFD/docs/named_data_theme/static/nav_f.png
new file mode 100644
index 0000000..f09ac2f
--- /dev/null
+++ b/NFD/docs/named_data_theme/static/nav_f.png
Binary files differ
diff --git a/NFD/docs/named_data_theme/static/tab_b.png b/NFD/docs/named_data_theme/static/tab_b.png
new file mode 100644
index 0000000..801fb4e
--- /dev/null
+++ b/NFD/docs/named_data_theme/static/tab_b.png
Binary files differ
diff --git a/NFD/docs/named_data_theme/theme.conf b/NFD/docs/named_data_theme/theme.conf
new file mode 100644
index 0000000..aa5a7ff
--- /dev/null
+++ b/NFD/docs/named_data_theme/theme.conf
@@ -0,0 +1,15 @@
+[theme]
+inherit = agogo
+stylesheet = named_data_style.css
+# pygments_style = sphinx
+
+theme_bodyfont = "normal 12px Verdana, sans-serif"
+theme_bgcolor = "#ccc"
+
+theme_documentwidth = "100%"
+theme_textalign = "left"
+
+[options]
+
+stickysidebar = true
+collapsiblesidebar = true
diff --git a/NFD/docs/overview.rst b/NFD/docs/overview.rst
new file mode 100644
index 0000000..437649d
--- /dev/null
+++ b/NFD/docs/overview.rst
@@ -0,0 +1,90 @@
+NFD Overview
+============
+
+.. toctree::
+   :maxdepth: 2
+
+   RELEASE_NOTES
+
+NDN Forwarding Daemon (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 release, NFD will become a core
+component of the `NDN Platform <http://named-data.net/codebase/platform/>`__ and will
+follow the same release cycle.
+
+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/>`__, 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 `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
+design emphasizes *modularity* and *extensibility* to allow easy experiments with new
+protocol features, algorithms, and applications. We have not fully optimized the code for
+performance.  The intention is that performance optimizations are one type of experiments
+that developers can conduct by trying out different data structures and different
+algorithms; over time, better implementations may emerge within the same design framework.
+To facilitate such experimentation with the forwarder, the NFD team has also written a
+`developer's guide <http://named-data.net/wp-content/uploads/2014/07/NFD-developer-guide.pdf>`_,
+which details the current implementation and provides tips for extending all aspects of
+NFD.
+
+NFD will keep evolving in three aspects: improvement of the modularity framework, keeping
+up with the NDN protocol spec, and addition of other new features. We hope to keep the
+modular framework stable and lean, allowing researchers to implement and experiment
+with various features, some of which may eventually work into the protocol spec.
+
+The design and development of NFD benefited from our earlier experience with `CCNx
+<http://www.ccnx.org>`__ software package. However, NFD is not in any part derived from
+CCNx codebase and does not maintain compatibility with CCNx.
+
+
+Major Modules of NFD
+--------------------
+
+NFD has the following major modules:
+
+- Core
+    Provides various common services shared between different NFD modules. These include
+    hash computation routines, DNS resolver, config file, face monitoring, and
+    several other modules.
+
+- Faces
+    Implements the NDN face abstraction on top of different lower level transport
+    mechanisms.
+
+- Tables
+    Implements the Content Store (CS), the Pending Interest Table (PIT), the Forwarding
+    Information Base (FIB), and other data structures to support forwarding of NDN Data
+    and Interest packets.
+
+- Forwarding
+    Implements basic packet processing pathways, which interact with Faces, Tables,
+    and Strategies.
+
+  + **Strategy Support**, a major part of the forwarding module
+      Implements a framework to support different forwarding strategies. It includes
+      StrategyChoice, Measurements, Strategies, and hooks in the forwarding pipelines. The
+      StrategyChoice records the choice of the strategy for a namespace, and Measurement
+      records are used by strategies to store past performance results for namespaces.
+
+- Management
+    Implements the `NFD Management Protocol
+    <http://redmine.named-data.net/projects/nfd/wiki/Management>`_, which allows
+    applications to configure NFD and set/query NFD's internal states.  Protocol interaction
+    is done via NDN's Interest/Data exchange between applications and NFD.
+
+- RIB Management
+    Manages the routing information base (RIB).  The RIB may be updated by different parties
+    in different ways, including various routing protocols, application's prefix
+    registrations, and command-line manipulation by sysadmins.  The RIB management module
+    processes all these requests to generate a consistent forwarding table, and then syncs
+    it up with the NFD's FIB, which contains only the minimal information needed for
+    forwarding decisions. Strictly speaking RIB management is part of the NFD management
+    module. However, due to its importance to the overall operations and its more complex
+    processing, we make it a separate module.
diff --git a/NFD/docs/schema.rst b/NFD/docs/schema.rst
new file mode 100644
index 0000000..f4c7611
--- /dev/null
+++ b/NFD/docs/schema.rst
@@ -0,0 +1,12 @@
+.. _nfd-status xml schema:
+
+NFD Status XML Schema
+=====================
+
+The NFD status XML schema describes the structure of the NFD status XML
+document generated by command ``nfd-status``.
+
+Users can use this schema to validate the generated XML document.
+
+.. literalinclude:: _static/nfd-status.xsd
+   :language: xml
diff --git a/NFD/nfd.conf.sample.in b/NFD/nfd.conf.sample.in
new file mode 100644
index 0000000..2f1e31f
--- /dev/null
+++ b/NFD/nfd.conf.sample.in
@@ -0,0 +1,316 @@
+; The general section contains settings of nfd process.
+general
+{
+  ; Specify a user and/or group for NFD to drop privileges to
+  ; when not performing privileged tasks. NFD does not drop
+  ; privileges by default.
+
+  ; user ndn-user
+  ; group ndn-user
+}
+
+log
+{
+  ; default_level specifies the logging level for modules
+  ; that are not explicitly named. All debugging levels
+  ; listed above the selected value are enabled.
+  ;
+  ; Valid values:
+  ;
+  ;  NONE ; no messages
+  ;  ERROR ; error messages
+  ;  WARN ; warning messages
+  ;  INFO ; informational messages (default)
+  ;  DEBUG ; debugging messages
+  ;  TRACE ; trace messages (most verbose)
+  ;  ALL ; all messages
+
+  default_level INFO
+
+  ; You may override default_level by assigning a logging level
+  ; to the desired module name. Module names can be found in two ways:
+  ;
+  ; Run:
+  ;   nfd --modules
+  ;   nrd --modules
+  ;
+  ; Or look for NFD_LOG_INIT(<module name>) statements in .cpp files
+  ;
+  ; Example module-level settings:
+  ;
+  ; FibManager DEBUG
+  ; Forwarder INFO
+}
+
+; The tables section configures the CS, PIT, FIB, Strategy Choice, and Measurements
+tables
+{
+
+  ; ContentStore size limit in number of packets
+  ; default is 65536, about 500MB with 8KB packet size
+  cs_max_packets 65536
+
+  ; Set the forwarding strategy for the specified prefixes:
+  ;   <prefix> <strategy>
+  strategy_choice
+  {
+    /               /localhost/nfd/strategy/best-route
+    /localhost      /localhost/nfd/strategy/broadcast
+    /localhost/nfd  /localhost/nfd/strategy/best-route
+    /ndn/broadcast  /localhost/nfd/strategy/broadcast
+  }
+}
+
+; The face_system section defines what faces and channels are created.
+face_system
+{
+  ; The unix section contains settings of Unix stream faces and channels.
+  ; Unix channel is always listening; delete unix section to disable
+  ; Unix stream faces and channels.
+  ;
+  ; The ndn-cxx library expects unix:///var/run/nfd.sock
+  ; to be used as the default transport option. Please change
+  ; the "transport" field in client.conf to an appropriate tcp4 FaceUri
+  ; if you need to disable unix sockets.
+  unix
+  {
+    path /var/run/nfd.sock ; Unix stream listener path
+  }
+
+  ; The tcp section contains settings of TCP faces and channels.
+  tcp
+  {
+    listen yes ; set to 'no' to disable TCP listener, default 'yes'
+    port 6363 ; TCP listener port number
+    enable_v4 yes ; set to 'no' to disable IPv4 channels, default 'yes'
+    enable_v6 yes ; set to 'no' to disable IPv6 channels, default 'yes'
+  }
+
+  ; The udp section contains settings of UDP faces and channels.
+  ; UDP channel is always listening; delete udp section to disable UDP
+  udp
+  {
+    port 6363 ; UDP unicast port number
+    enable_v4 yes ; set to 'no' to disable IPv4 channels, default 'yes'
+    enable_v6 yes ; set to 'no' to disable IPv6 channels, default 'yes'
+    idle_timeout 600 ; idle time (seconds) before closing a UDP unicast face
+    keep_alive_interval 25; interval (seconds) between keep-alive refreshes
+
+    ; UDP multicast settings
+    ; NFD creates one UDP multicast face per NIC
+    ;
+    ; In multi-homed Linux machines these settings will NOT work without
+    ; root or settings the appropriate permissions:
+    ;
+    ;    sudo setcap cap_net_raw=eip /full/path/nfd
+    ;
+    mcast yes ; set to 'no' to disable UDP multicast, default 'yes'
+    mcast_port 56363 ; UDP multicast port number
+    mcast_group 224.0.23.170 ; UDP multicast group (IPv4 only)
+  }
+
+  ; The ether section contains settings of Ethernet faces and channels.
+  ; These settings will NOT work without root or setting the appropriate
+  ; permissions:
+  ;
+  ;    sudo setcap cap_net_raw,cap_net_admin=eip /full/path/nfd
+  ;
+  ; You may need to install a package to use setcap:
+  ;
+  ; **Ubuntu:**
+  ;
+  ;    sudo apt-get install libcap2-bin
+  ;
+  ; **Mac OS X:**
+  ;
+  ;    curl https://bugs.wireshark.org/bugzilla/attachment.cgi?id=3373 -o ChmodBPF.tar.gz
+  ;    tar zxvf ChmodBPF.tar.gz
+  ;    open ChmodBPF/Install\ ChmodBPF.app
+  ;
+  ; or manually:
+  ;
+  ;    sudo chgrp admin /dev/bpf*
+  ;    sudo chmod g+rw /dev/bpf*
+
+  @IF_HAVE_LIBPCAP@ether
+  @IF_HAVE_LIBPCAP@{
+  @IF_HAVE_LIBPCAP@  ; Ethernet multicast settings
+  @IF_HAVE_LIBPCAP@  ; NFD creates one Ethernet multicast face per NIC
+  @IF_HAVE_LIBPCAP@
+  @IF_HAVE_LIBPCAP@  mcast yes ; set to 'no' to disable Ethernet multicast, default 'yes'
+  @IF_HAVE_LIBPCAP@  mcast_group 01:00:5E:00:17:AA ; Ethernet multicast group
+  @IF_HAVE_LIBPCAP@}
+
+  ; The websocket section contains settings of WebSocket faces and channels.
+
+  @IF_HAVE_WEBSOCKET@websocket
+  @IF_HAVE_WEBSOCKET@{
+  @IF_HAVE_WEBSOCKET@  listen yes ; set to 'no' to disable WebSocket listener, default 'yes'
+  @IF_HAVE_WEBSOCKET@  port 9696 ; WebSocket listener port number
+  @IF_HAVE_WEBSOCKET@  enable_v4 yes ; set to 'no' to disable listening on IPv4 socket, default 'yes'
+  @IF_HAVE_WEBSOCKET@  enable_v6 yes ; set to 'no' to disable listening on IPv6 socket, default 'yes'
+  @IF_HAVE_WEBSOCKET@}
+}
+
+; The authorizations section grants privileges to authorized keys.
+authorizations
+{
+  ; An authorize section grants privileges to a NDN certificate.
+  authorize
+  {
+    ; If you do not already have NDN certificate, you can generate
+    ; one with the following commands.
+    ;
+    ; 1. Generate and install a self-signed identity certificate:
+    ;
+    ;      ndnsec-keygen /`whoami` | ndnsec-install-cert -
+    ;
+    ; Note that the argument to ndnsec-key will be the identity name of the
+    ; new key (in this case, /your-username). Identities are hierarchical NDN
+    ; names and may have multiple components (e.g. `/ndn/ucla/edu/alice`).
+    ; You may create additional keys and identities as you see fit.
+    ;
+    ; 2. Dump the NDN certificate to a file:
+    ;
+    ;      sudo mkdir -p @SYSCONFDIR@/ndn/keys/
+    ;      ndnsec-cert-dump -i /`whoami` >  default.ndncert
+    ;      sudo mv default.ndncert @SYSCONFDIR@/ndn/keys/default.ndncert
+    ;
+    ; The "certfile" field below specifies the default key directory for
+    ; your machine. You may move your newly created key to the location it
+    ; specifies or path.
+
+    ; certfile keys/default.ndncert ; NDN identity certificate file
+    certfile any ; "any" authorizes command interests signed under any certificate,
+                 ; i.e., no actual validation.
+    privileges ; set of privileges granted to this identity
+    {
+      faces
+      fib
+      strategy-choice
+    }
+  }
+
+  ; You may have multiple authorize sections that specify additional
+  ; certificates and their privileges.
+
+  ; authorize
+  ; {
+  ;   certfile keys/this_cert_does_not_exist.ndncert
+  ;   authorize
+  ;   privileges
+  ;   {
+  ;     faces
+  ;   }
+  ; }
+}
+
+rib
+{
+  ; The following localhost_security allows anyone to register routing entries in local RIB
+  localhost_security
+  {
+    trust-anchor
+    {
+      type any
+    }
+  }
+
+  ; localhop_security should be enabled when NFD runs on a hub.
+  ; "/localhop/nfd/fib" command prefix will be disabled when localhop_security section is missing.
+  ; localhop_security
+  ; {
+  ;   ; This section defines the trust model for NFD RIB Management. It consists of rules and
+  ;   ; trust-anchors, which are briefly defined in this file.  For more information refer to
+  ;   ; manpage of ndn-validator.conf:
+  ;   ;
+  ;   ;     man ndn-validator.conf
+  ;   ;
+  ;   ; A trust-anchor is a pre-trusted certificate.  This can be any certificate that is the
+  ;   ; root of certification chain (e.g., NDN testbed root certificate) or an existing
+  ;   ; default system certificate `default.ndncert`.
+  ;   ;
+  ;   ; A rule defines conditions a valid packet MUST have. A packet must satisfy one of the
+  ;   ; rules defined here. A rule can be broken into two parts: matching & checking. A packet
+  ;   ; will be matched against rules from the first to the last until a matched rule is
+  ;   ; encountered. The matched rule will be used to check the packet. If a packet does not
+  ;   ; match any rule, it will be treated as invalid.  The matching part of a rule consists
+  ;   ; of `for` and `filter` sections. They collectively define which packets can be checked
+  ;   ; with this rule. `for` defines packet type (data or interest) and `filter` defines
+  ;   ; conditions on other properties of a packet. Right now, you can only define conditions
+  ;   ; on packet name, and you can only specify ONLY ONE filter for packet name.  The
+  ;   ; checking part of a rule consists of `checker`, which defines the conditions that a
+  ;   ; VALID packet MUST have. See comments in checker section for more details.
+  ;
+  ;   rule
+  ;   {
+  ;     id "NRD Prefix Registration Command Rule"
+  ;     for interest                         ; rule for Interests (to validate CommandInterests)
+  ;     filter
+  ;     {
+  ;       type name                          ; condition on interest name (w/o signature)
+  ;       regex ^[<localhop><localhost>]<nfd><rib>[<register><unregister>]<>$ ; prefix before
+  ;                                                                           ; timestamp
+  ;     }
+  ;     checker
+  ;     {
+  ;       type customized
+  ;       sig-type rsa-sha256                ; interest must have a rsa-sha256 signature
+  ;       key-locator
+  ;       {
+  ;         type name                        ; key locator must be the certificate name of the
+  ;                                          ; signing key
+  ;         regex ^[^<KEY>]*<KEY><>*<ksk-.*><ID-CERT>$
+  ;       }
+  ;     }
+  ;   }
+  ;   rule
+  ;   {
+  ;     id "NDN Testbed Hierarchy Rule"
+  ;     for data                             ; rule for Data (to validate NDN certificates)
+  ;     filter
+  ;     {
+  ;       type name                          ; condition on data name
+  ;       regex ^[^<KEY>]*<KEY><>*<ksk-.*><ID-CERT><>$
+  ;     }
+  ;     checker
+  ;     {
+  ;       type hierarchical                  ; the certificate name of the signing key and
+  ;                                          ; the data name must follow the hierarchical model
+  ;       sig-type rsa-sha256                ; data must have a rsa-sha256 signature
+  ;     }
+  ;   }
+  ;   trust-anchor
+  ;   {
+  ;     type file
+  ;     file-name keys/default.ndncert ; the file name, by default this file should be placed in the
+  ;                                    ; same folder as this config file.
+  ;   }
+  ;   ; trust-anchor ; Can be repeated multiple times to specify multiple trust anchors
+  ;   ; {
+  ;   ;   type file
+  ;   ;   file-name keys/ndn-testbed.ndncert
+  ;   ; }
+  ; }
+
+  ; The following localhop_security should be enabled when NFD runs on a hub,
+  ; which accepts all remote registrations and is a short-term solution.
+  ; localhop_security
+  ; {
+  ;   trust-anchor
+  ;   {
+  ;     type any
+  ;   }
+  ; }
+
+  remote_register
+  {
+    cost 15 ; forwarding cost of prefix registered on remote router
+    timeout 10000 ; timeout (in milliseconds) of remote prefix registration command
+    retry 0 ; maximum number of retries for each remote prefix registration command
+
+    refresh_interval 300 ; interval (in seconds) before refreshing the registration
+    ; This setting should be less than face_system.udp.idle_time,
+    ; so that the face is kept alive on the remote router.
+  }
+}
diff --git a/NFD/rib/face-entry.hpp b/NFD/rib/face-entry.hpp
new file mode 100644
index 0000000..c9eeb33
--- /dev/null
+++ b/NFD/rib/face-entry.hpp
@@ -0,0 +1,94 @@
+/* -*- 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_RIB_FACE_ENTRY_HPP
+#define NFD_RIB_FACE_ENTRY_HPP
+
+#include "common.hpp"
+#include "core/scheduler.hpp"
+
+namespace nfd {
+namespace rib {
+
+/** \class FaceEntry
+ *  \brief represents a route for a name prefix
+ */
+class FaceEntry
+{
+public:
+  FaceEntry()
+    : faceId(0)
+    , origin(0)
+    , flags(0)
+    , cost(0)
+    , expires(time::steady_clock::TimePoint::min())
+    , m_expirationEvent()
+  {
+  }
+
+public:
+  void
+  setExpirationEvent(const EventId eid)
+  {
+    m_expirationEvent = eid;
+  }
+
+  const EventId&
+  getExpirationEvent() const
+  {
+    return m_expirationEvent;
+  }
+
+public:
+  uint64_t faceId;
+  uint64_t origin;
+  uint64_t flags;
+  uint64_t cost;
+  time::steady_clock::TimePoint expires;
+
+private:
+  EventId m_expirationEvent;
+};
+
+inline bool
+compareFaceIdAndOrigin(const FaceEntry& entry1, const FaceEntry& entry2)
+{
+  return (entry1.faceId == entry2.faceId && entry1.origin == entry2.origin);
+}
+
+inline bool
+compareFaceId(const FaceEntry& entry, const uint64_t faceId)
+{
+  return (entry.faceId == faceId);
+}
+
+// Method definition in rib-entry.cpp
+std::ostream&
+operator<<(std::ostream& os, const FaceEntry& entry);
+
+} // namespace rib
+} // namespace nfd
+
+#endif // NFD_RIB_RIB_ENTRY_HPP
diff --git a/NFD/rib/fib-update.cpp b/NFD/rib/fib-update.cpp
new file mode 100644
index 0000000..cda4f14
--- /dev/null
+++ b/NFD/rib/fib-update.cpp
@@ -0,0 +1,57 @@
+/* -*- 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/>.
+ */
+
+#include "fib-update.hpp"
+
+namespace nfd {
+namespace rib {
+
+shared_ptr<FibUpdate>
+FibUpdate::createAddUpdate(const Name& name, const uint64_t faceId, const uint64_t cost)
+{
+  shared_ptr<FibUpdate> update = make_shared<FibUpdate>();
+
+  update->name = name;
+  update->faceId = faceId;
+  update->cost = cost;
+  update->action = ADD_NEXTHOP;
+
+  return update;
+}
+
+shared_ptr<FibUpdate>
+FibUpdate::createRemoveUpdate(const Name& name, const uint64_t faceId)
+{
+  shared_ptr<FibUpdate> update = make_shared<FibUpdate>();
+
+  update->name = name;
+  update->faceId = faceId;
+  update->action = REMOVE_NEXTHOP;
+
+  return update;
+}
+
+} // namespace rib
+} // namespace nfd
diff --git a/NFD/rib/fib-update.hpp b/NFD/rib/fib-update.hpp
new file mode 100644
index 0000000..02713ab
--- /dev/null
+++ b/NFD/rib/fib-update.hpp
@@ -0,0 +1,90 @@
+/* -*- 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_RIB_FIB_UPDATE_HPP
+#define NFD_RIB_FIB_UPDATE_HPP
+
+#include "common.hpp"
+
+namespace nfd {
+namespace rib {
+
+/** \class FibUpdate
+ *  \brief represents a FIB update
+ */
+class FibUpdate
+{
+public:
+  FibUpdate()
+    : faceId(0)
+    , cost(0)
+  {
+  }
+
+  static shared_ptr<FibUpdate>
+  createAddUpdate(const Name& name, const uint64_t faceId, const uint64_t cost);
+
+  static shared_ptr<FibUpdate>
+  createRemoveUpdate(const Name& name, const uint64_t faceId);
+
+  enum Action
+  {
+    ADD_NEXTHOP    = 0,
+    REMOVE_NEXTHOP = 1
+  };
+
+public:
+  Name name;
+  uint64_t faceId;
+  uint64_t cost;
+  Action action;
+};
+
+inline std::ostream&
+operator<<(std::ostream& os, const FibUpdate& update)
+{
+  os << "FibUpdate("
+     << " Name: " << update.name << ", "
+     << "faceId: " << update.faceId << ", ";
+
+  if (update.action == FibUpdate::ADD_NEXTHOP)
+    {
+      os << "cost: " << update.cost << ", "
+         << "action: ADD_NEXTHOP";
+    }
+  else
+    {
+      os << "action: REMOVE_NEXTHOP";
+    }
+
+  os << ")";
+
+  return os;
+}
+
+} // namespace rib
+} // namespace nfd
+
+#endif // NFD_RIB_FIB_UPDATE_HPP
diff --git a/NFD/rib/main.cpp b/NFD/rib/main.cpp
new file mode 100644
index 0000000..bfea4f7
--- /dev/null
+++ b/NFD/rib/main.cpp
@@ -0,0 +1,279 @@
+/* -*- 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/>.
+ */
+
+#include <getopt.h>
+
+#include "version.hpp"
+#include "common.hpp"
+#include "rib-manager.hpp"
+#include "core/config-file.hpp"
+#include "core/global-io.hpp"
+#include "core/logger.hpp"
+
+namespace nfd {
+namespace rib {
+
+NFD_LOG_INIT("NRD");
+
+struct ProgramOptions
+{
+  bool showUsage;
+  bool showVersion;
+  bool showModules;
+  std::string config;
+};
+
+class Nrd : noncopyable
+{
+public:
+  class IgnoreNfdAndLogSections
+  {
+  public:
+    void
+    operator()(const std::string& filename,
+               const std::string& sectionName,
+               const ConfigSection& section,
+               bool isDryRun)
+    {
+      // Ignore "log" and sections belonging to NFD,
+      // but raise an error if we're missing a handler for an "rib_" section.
+
+      if (sectionName.find("rib_") != 0 || sectionName == "log")
+        {
+          // do nothing
+        }
+      else
+        {
+          // missing NRD section
+          ConfigFile::throwErrorOnUnknownSection(filename, sectionName, section, isDryRun);
+        }
+    }
+  };
+
+  Nrd()
+    : m_face(getGlobalIoService())
+  {
+  }
+
+  void
+  initialize(const std::string& configFile)
+  {
+    initializeLogging(configFile);
+
+    m_ribManager = make_shared<RibManager>(ndn::ref(m_face));
+
+    ConfigFile config((IgnoreNfdAndLogSections()));
+    m_ribManager->setConfigFile(config);
+
+    // parse config file
+    config.parse(configFile, true);
+    config.parse(configFile, false);
+
+    m_ribManager->registerWithNfd();
+    m_ribManager->enableLocalControlHeader();
+  }
+
+  void
+  initializeLogging(const std::string& configFile)
+  {
+    ConfigFile config(&ConfigFile::ignoreUnknownSection);
+    LoggerFactory::getInstance().setConfigFile(config);
+
+    config.parse(configFile, true);
+    config.parse(configFile, false);
+  }
+
+  static void
+  printUsage(std::ostream& os, const std::string& programName)
+  {
+    os << "Usage: \n"
+       << "  " << programName << " [options]\n"
+       << "\n"
+       << "Run NRD daemon\n"
+       << "\n"
+       << "Options:\n"
+       << "  [--help]    - print this help message\n"
+       << "  [--version] - print version and exit\n"
+       << "  [--modules] - list available logging modules\n"
+       << "  [--config /path/to/nfd.conf] - path to configuration file "
+       << "(default: " << DEFAULT_CONFIG_FILE << ")\n"
+      ;
+  }
+
+  static void
+  printModules(std::ostream& os)
+  {
+    using namespace std;
+
+    os << "Available logging modules: \n";
+
+    list<string> modules(LoggerFactory::getInstance().getModules());
+    for (list<string>::const_iterator i = modules.begin(); i != modules.end(); ++i)
+      {
+        os << *i << "\n";
+      }
+  }
+
+  static bool
+  parseCommandLine(int argc, char** argv, ProgramOptions& options)
+  {
+    options.showUsage = false;
+    options.showVersion = false;
+    options.showModules = false;
+    options.config = DEFAULT_CONFIG_FILE;
+
+    while (true) {
+      int optionIndex = 0;
+      static ::option longOptions[] = {
+        { "help"   , no_argument      , 0, 0 },
+        { "modules", no_argument      , 0, 0 },
+        { "config" , required_argument, 0, 0 },
+        { "version", no_argument      , 0, 0 },
+        { 0        , 0                , 0, 0 }
+      };
+      int c = getopt_long_only(argc, argv, "", longOptions, &optionIndex);
+      if (c == -1)
+        break;
+
+      switch (c) {
+      case 0:
+        switch (optionIndex) {
+        case 0: // help
+          options.showUsage = true;
+          break;
+        case 1: // modules
+          options.showModules = true;
+          break;
+        case 2: // config
+          options.config = ::optarg;
+          break;
+        case 3: // version
+          options.showVersion = true;
+          break;
+        default:
+          return false;
+        }
+        break;
+      }
+    }
+    return true;
+  }
+
+
+  void
+  terminate(const boost::system::error_code& error,
+            int signalNo,
+            boost::asio::signal_set& signalSet)
+  {
+    if (error)
+      return;
+
+    if (signalNo == SIGINT ||
+        signalNo == SIGTERM)
+      {
+        getGlobalIoService().stop();
+        NFD_LOG_INFO("Caught signal '" << strsignal(signalNo) << "', exiting...");
+      }
+    else
+      {
+        /// \todo May be try to reload config file
+        signalSet.async_wait(bind(&Nrd::terminate, this, _1, _2,
+                                  ref(signalSet)));
+      }
+  }
+
+private:
+  shared_ptr<RibManager> m_ribManager;
+  ndn::Face m_face;
+};
+
+} // namespace rib
+} // namespace nfd
+
+int
+main(int argc, char** argv)
+{
+  using namespace nfd::rib;
+
+  ProgramOptions options;
+  bool isCommandLineValid = Nrd::parseCommandLine(argc, argv, options);
+  if (!isCommandLineValid) {
+    Nrd::printUsage(std::cerr, argv[0]);
+    return 1;
+  }
+  if (options.showUsage) {
+    Nrd::printUsage(std::cout, argv[0]);
+    return 0;
+  }
+
+  if (options.showModules) {
+    Nrd::printModules(std::cout);
+    return 0;
+  }
+
+  if (options.showVersion) {
+    std::cout << NFD_VERSION_BUILD_STRING << std::endl;
+    return 0;
+  }
+
+  Nrd nrdInstance;
+
+  try {
+    nrdInstance.initialize(options.config);
+  }
+  catch (boost::filesystem::filesystem_error& e) {
+    if (e.code() == boost::system::errc::permission_denied) {
+      NFD_LOG_FATAL("Permissions denied for " << e.path1() << ". " <<
+                    argv[0] << " should be run as superuser");
+    }
+    else {
+      NFD_LOG_FATAL(e.what());
+    }
+    return 1;
+  }
+  catch (const std::exception& e) {
+    NFD_LOG_FATAL(e.what());
+    return 2;
+  }
+
+  boost::asio::signal_set signalSet(nfd::getGlobalIoService());
+  signalSet.add(SIGINT);
+  signalSet.add(SIGTERM);
+  signalSet.add(SIGHUP);
+  signalSet.add(SIGUSR1);
+  signalSet.add(SIGUSR2);
+  signalSet.async_wait(bind(&Nrd::terminate, &nrdInstance, _1, _2,
+                            ndn::ref(signalSet)));
+
+  try {
+    nfd::getGlobalIoService().run();
+  }
+  catch (std::exception& e) {
+    NFD_LOG_FATAL(e.what());
+    return 3;
+  }
+
+  return 0;
+}
diff --git a/NFD/rib/remote-registrator.cpp b/NFD/rib/remote-registrator.cpp
new file mode 100644
index 0000000..a587653
--- /dev/null
+++ b/NFD/rib/remote-registrator.cpp
@@ -0,0 +1,451 @@
+/* -*- 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/>.
+ */
+
+#include "remote-registrator.hpp"
+#include "core/logger.hpp"
+#include "core/scheduler.hpp"
+
+namespace nfd {
+namespace rib {
+
+NFD_LOG_INIT("RemoteRegistrator");
+
+using ndn::nfd::ControlParameters;
+using ndn::nfd::CommandOptions;
+
+const Name RemoteRegistrator::LOCAL_REGISTRATION_PREFIX = "/localhost";
+const Name RemoteRegistrator::REMOTE_HUB_PREFIX = "/localhop/nfd/rib";
+const name::Component RemoteRegistrator::IGNORE_COMMPONENT("rib");
+
+RemoteRegistrator::RemoteRegistrator(ndn::nfd::Controller& controller,
+                                     ndn::KeyChain& keyChain,
+                                     Rib& rib)
+  : m_nfdController(controller)
+  , m_keyChain(keyChain)
+  , m_rib(rib)
+  , m_refreshInterval(time::seconds(25))
+  , m_hasConnectedHub(false)
+  , m_nRetries(0)
+{
+}
+
+RemoteRegistrator::~RemoteRegistrator()
+{
+  // cancel all periodically refresh events.
+  for (auto&& entry : m_regEntries)
+    {
+      scheduler::cancel(entry.second);
+    }
+}
+
+void
+RemoteRegistrator::loadConfig(const ConfigSection& configSection)
+{
+  size_t cost = 15, timeout = 10000;
+  size_t retry = 0;
+  size_t interval = 0;
+  const size_t intervalDef = 25, intervalMax = 600;
+
+  NFD_LOG_INFO("Load remote_register section in rib section");
+  for (auto&& i : configSection)
+    {
+      if (i.first == "cost")
+        {
+          cost = i.second.get_value<size_t>();
+        }
+      else if (i.first == "timeout")
+        {
+          timeout = i.second.get_value<size_t>();
+        }
+      else if (i.first == "retry")
+        {
+          retry = i.second.get_value<size_t>();
+        }
+      else if (i.first == "refresh_interval")
+        {
+          interval = i.second.get_value<size_t>();
+        }
+      else
+        {
+          throw ConfigFile::Error("Unrecognized option \"" + i.first +
+                                  "\" in \"remote-registrator\" section");
+        }
+    }
+
+   m_controlParameters
+     .setCost(cost)
+     .setOrigin(ndn::nfd::ROUTE_ORIGIN_CLIENT)// set origin to client.
+     .setFaceId(0);// the remote hub will take the input face as the faceId.
+
+   Name commandPrefix = REMOTE_HUB_PREFIX;
+   if (IGNORE_COMMPONENT == commandPrefix.at(-1))
+     {
+       commandPrefix = commandPrefix.getPrefix(-1);
+     }
+
+   m_commandOptions
+     .setPrefix(commandPrefix)
+     .setTimeout(time::milliseconds(timeout));
+
+   m_nRetries = retry;
+
+   if (interval == 0)
+     {
+       interval = intervalDef;
+     }
+
+   interval = std::min(interval, intervalMax);
+
+   m_refreshInterval = time::seconds(interval);
+}
+
+void
+RemoteRegistrator::enable()
+{
+  // do remote registration after an entry is inserted into the RIB.
+  m_afterInsertConnection =
+    m_rib.afterInsertEntry.connect([this] (const Name& prefix) {
+        registerPrefix(prefix);
+      });
+
+  // do remote unregistration after an entry is erased from the RIB.
+  m_afterEraseConnection =
+    m_rib.afterEraseEntry.connect([this] (const Name& prefix) {
+        unregisterPrefix(prefix);
+      });
+}
+
+void
+RemoteRegistrator::disable()
+{
+  m_afterInsertConnection.disconnect();
+  m_afterEraseConnection.disconnect();
+}
+
+void
+RemoteRegistrator::registerPrefix(const Name& prefix)
+{
+  if (LOCAL_REGISTRATION_PREFIX.isPrefixOf(prefix))
+    {
+      NFD_LOG_INFO("local registration only for " << prefix);
+      return;
+    }
+
+  bool isHubPrefix = prefix == REMOTE_HUB_PREFIX;
+
+  if (isHubPrefix)
+    {
+      NFD_LOG_INFO("this is a prefix registered by some hub: " << prefix);
+
+      m_hasConnectedHub = true;
+
+      redoRegistration();
+      return;
+    }
+
+  if (!m_hasConnectedHub)
+    {
+      NFD_LOG_INFO("no hub connected when registering " << prefix);
+      return;
+    }
+
+  std::pair<Name, size_t> identity = findIdentityForRegistration(prefix);
+
+  if (0 == identity.second)
+    {
+      NFD_LOG_INFO("no proper identity found for registering " << prefix);
+      return;
+    }
+
+  Name prefixForRegistration;
+  if (identity.first.size() == identity.second)
+    {
+      prefixForRegistration = identity.first;
+    }
+  else
+    {
+      prefixForRegistration = identity.first.getPrefix(-1);
+    }
+
+  if (m_regEntries.find(prefixForRegistration) != m_regEntries.end())
+    {
+      NFD_LOG_INFO("registration already in process for " << prefix);
+      return;
+    }
+
+  // make copies of m_controlParameters and m_commandOptions to
+  // avoid unreasonable overwriting during concurrent registration
+  // and unregistration.
+  ControlParameters parameters = m_controlParameters;
+  CommandOptions    options    = m_commandOptions;
+
+  startRegistration(parameters.setName(prefixForRegistration),
+                    options.setSigningIdentity(identity.first),
+                    m_nRetries);
+}
+
+void
+RemoteRegistrator::unregisterPrefix(const Name& prefix)
+{
+  if (prefix == REMOTE_HUB_PREFIX)
+    {
+      NFD_LOG_INFO("disconnected to hub with prefix: " << prefix);
+
+      // for phase 1: suppose there is at most one hub connected.
+      // if the hub prefix has been unregistered locally, there may
+      // be no connected hub.
+      m_hasConnectedHub = false;
+
+      clearRefreshEvents();
+      return;
+    }
+
+  if (!m_hasConnectedHub)
+    {
+      NFD_LOG_INFO("no hub connected when unregistering " << prefix);
+      return;
+    }
+
+  std::pair<Name, size_t> identity = findIdentityForRegistration(prefix);
+
+  if (0 == identity.second)
+    {
+      NFD_LOG_INFO("no proper identity found for unregistering " << prefix);
+      return;
+    }
+
+  Name prefixForRegistration;
+  if (identity.first.size() == identity.second)
+    {
+      prefixForRegistration = identity.first;
+    }
+  else
+    {
+      prefixForRegistration = identity.first.getPrefix(-1);
+    }
+
+  RegisteredEntryIt iRegEntry = m_regEntries.find(prefixForRegistration);
+  if (m_regEntries.end() == iRegEntry)
+    {
+      NFD_LOG_INFO("no existing entry found when unregistering " << prefix);
+      return;
+    }
+
+  for (auto&& entry : m_rib)
+    {
+      if (prefixForRegistration.isPrefixOf(entry.first) &&
+          findIdentityForRegistration(entry.first) == identity)
+        {
+          NFD_LOG_INFO("this identity should be kept for other rib entry: "
+                       << entry.first);
+          return;
+        }
+    }
+
+  scheduler::cancel(iRegEntry->second);
+  m_regEntries.erase(iRegEntry);
+
+  // make copies of m_controlParameters and m_commandOptions to
+  // avoid unreasonable overwriting during concurrent registration
+  // and unregistration.
+  ControlParameters parameters = m_controlParameters;
+  CommandOptions    options    = m_commandOptions;
+
+  startUnregistration(parameters.setName(prefixForRegistration).unsetCost(),
+                      options.setSigningIdentity(identity.first),
+                      m_nRetries);
+}
+
+std::pair<Name, size_t>
+RemoteRegistrator::findIdentityForRegistration(const Name& prefix)
+{
+  std::pair<Name, size_t> candidateIdentity;
+  std::vector<Name> identities;
+  bool isPrefix = false;
+  size_t maxLength = 0, curLength = 0;
+
+  // get all identies from the key-cahin except the default one.
+  m_keyChain.getAllIdentities(identities, false);
+
+  // get the default identity.
+  identities.push_back(m_keyChain.getDefaultIdentity());
+
+  // longest prefix matching to all indenties.
+  for (auto&& i : identities)
+    {
+      if (!i.empty() && IGNORE_COMMPONENT == i.at(-1))
+        {
+          isPrefix = i.getPrefix(-1).isPrefixOf(prefix);
+          curLength = i.size() - 1;
+        }
+      else
+        {
+          isPrefix = i.isPrefixOf(prefix);
+          curLength = i.size();
+        }
+
+      if (isPrefix && curLength > maxLength)
+        {
+          candidateIdentity.first = i;
+          maxLength = curLength;
+        }
+    }
+
+  candidateIdentity.second = maxLength;
+
+  return candidateIdentity;
+}
+
+void
+RemoteRegistrator::startRegistration(const ControlParameters& parameters,
+                                     const CommandOptions& options,
+                                     int nRetries)
+{
+  NFD_LOG_INFO("start register " << parameters.getName());
+
+  m_nfdController.start<ndn::nfd::RibRegisterCommand>(
+     parameters,
+     bind(&RemoteRegistrator::onRegSuccess,
+          this, parameters, options),
+     bind(&RemoteRegistrator::onRegFailure,
+          this, _1, _2, parameters, options, nRetries),
+     options);
+}
+
+void
+RemoteRegistrator::startUnregistration(const ControlParameters& parameters,
+                                       const CommandOptions& options,
+                                       int nRetries)
+{
+  NFD_LOG_INFO("start unregister " << parameters.getName());
+
+  m_nfdController.start<ndn::nfd::RibUnregisterCommand>(
+     parameters,
+     bind(&RemoteRegistrator::onUnregSuccess,
+          this, parameters, options),
+     bind(&RemoteRegistrator::onUnregFailure,
+          this, _1, _2, parameters, options, nRetries),
+     options);
+}
+
+void
+RemoteRegistrator::onRegSuccess(const ControlParameters& parameters,
+                                const CommandOptions& options)
+{
+  NFD_LOG_INFO("success to register " << parameters.getName());
+
+  RegisteredEntryIt iRegEntry = m_regEntries.find(parameters.getName());
+
+  if (m_regEntries.end() != iRegEntry)
+    {
+      NFD_LOG_DEBUG("Existing Entry: (" << iRegEntry->first
+                                        << ", " << iRegEntry->second
+                                        << ")");
+
+      scheduler::cancel(iRegEntry->second);
+      iRegEntry->second = scheduler::schedule(
+                            m_refreshInterval,
+                            bind(&RemoteRegistrator::startRegistration,
+                                 this, parameters, options, m_nRetries));
+    }
+  else
+    {
+      NFD_LOG_DEBUG("New Entry");
+      m_regEntries.insert(RegisteredEntry(
+                              parameters.getName(),
+                              scheduler::schedule(
+                                m_refreshInterval,
+                                bind(&RemoteRegistrator::startRegistration,
+                                     this, parameters, options, m_nRetries))));
+    }
+}
+
+void
+RemoteRegistrator::onRegFailure(uint32_t code, const std::string& reason,
+                                const ControlParameters& parameters,
+                                const CommandOptions& options,
+                                int nRetries)
+{
+  NFD_LOG_INFO("fail to register " << parameters.getName()
+                                   << "\n\t reason:" << reason
+                                   << "\n\t remain retries:" << nRetries);
+
+  if (nRetries > 0)
+    {
+      startRegistration(parameters, options, nRetries - 1);
+    }
+}
+
+void
+RemoteRegistrator::onUnregSuccess(const ControlParameters& parameters,
+                                  const CommandOptions& options)
+{
+  NFD_LOG_INFO("success to unregister " << parameters.getName());
+}
+
+void
+RemoteRegistrator::onUnregFailure(uint32_t code, const std::string& reason,
+                                  const ControlParameters& parameters,
+                                  const CommandOptions& options,
+                                  int nRetries)
+{
+  NFD_LOG_INFO("fail to unregister " << parameters.getName()
+                                     << "\n\t reason:" << reason
+                                     << "\n\t remain retries:" << nRetries);
+
+  if (nRetries > 0)
+    {
+      startUnregistration(parameters, options, nRetries - 1);
+    }
+}
+
+void
+RemoteRegistrator::redoRegistration()
+{
+  NFD_LOG_INFO("redo " << m_regEntries.size()
+                       << " registration when new Hub connection is built.");
+
+  for (auto&& entry : m_regEntries)
+    {
+      // make copies to avoid unreasonable overwrite.
+      ControlParameters parameters = m_controlParameters;
+      CommandOptions    options    = m_commandOptions;
+      startRegistration(parameters.setName(entry.first),
+                        options.setSigningIdentity(entry.first),
+                        m_nRetries);
+    }
+}
+
+void
+RemoteRegistrator::clearRefreshEvents()
+{
+  for (auto&& entry : m_regEntries)
+    {
+      scheduler::cancel(entry.second);
+    }
+}
+
+} // namespace rib
+} // namespace nfd
diff --git a/NFD/rib/remote-registrator.hpp b/NFD/rib/remote-registrator.hpp
new file mode 100644
index 0000000..0d9eea3
--- /dev/null
+++ b/NFD/rib/remote-registrator.hpp
@@ -0,0 +1,250 @@
+/* -*- 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_RIB_REMOTE_REGISTRATOR_HPP
+#define NFD_RIB_REMOTE_REGISTRATOR_HPP
+
+#include "rib.hpp"
+#include "core/config-file.hpp"
+#include "rib-status-publisher.hpp"
+
+#include <unordered_map>
+#include <ndn-cxx/security/key-chain.hpp>
+#include <ndn-cxx/management/nfd-controller.hpp>
+#include <ndn-cxx/management/nfd-control-command.hpp>
+#include <ndn-cxx/management/nfd-control-parameters.hpp>
+#include <ndn-cxx/management/nfd-command-options.hpp>
+#include <ndn-cxx/util/signal.hpp>
+
+namespace nfd {
+namespace rib {
+
+/**
+ * @brief define the RemoteRegistrator class, which handles
+ *        the registration/unregistration to remote hub(s).
+ */
+class RemoteRegistrator : noncopyable
+{
+public:
+  class Error : public std::runtime_error
+  {
+  public:
+    explicit
+    Error(const std::string& what)
+      : std::runtime_error(what)
+    {
+    }
+  };
+
+  RemoteRegistrator(ndn::nfd::Controller& controller,
+                    ndn::KeyChain& keyChain,
+                    Rib& rib);
+
+  ~RemoteRegistrator();
+
+  /**
+   * @brief load the "remote_register" section from config file
+   *
+   * @param configSection the sub section in "rib" section.
+   */
+  void
+  loadConfig(const ConfigSection& configSection);
+
+  /**
+   * @brief enable remote registration/unregistration.
+   *
+   */
+  void
+  enable();
+
+  /**
+   * @brief disable remote registration/unregistration.
+   *
+   */
+  void
+  disable();
+
+  /**
+   * @brief register a prefix to remote hub(s).
+   *
+   * For the input prefix, we find the longest identity
+   * in the key-chain that can sign it, and then
+   * register this identity to remote hub(s).
+   *
+   * @param prefix the prefix being registered in local RIB.
+   */
+  void
+  registerPrefix(const Name& prefix);
+
+  /**
+   * @brief unregister a prefix from remote hub(s).
+   *
+   * For the input prefix, if the longest identity can sign it
+   * is already registered remotely, that identity should be
+   * unregistered from remote hub(s).
+   *
+   * @param prefix the prefix being unregistered in local RIB.
+   */
+  void
+  unregisterPrefix(const Name& prefix);
+
+private:
+  /**
+   * @brief find the most proper identity that can sign the
+   *        registration/unregistration command for the input prefix.
+   *
+   * @return the identity and the length of the longest match to the
+   *         input prefix.
+   *
+   * @retval { ignored, 0 } no matching identity
+   */
+  std::pair<Name, size_t>
+  findIdentityForRegistration(const Name& prefix);
+
+  /**
+   * @brief make and send the remote registration command.
+   *
+   * @param nRetries remaining number of retries.
+   */
+  void
+  startRegistration(const ndn::nfd::ControlParameters& parameters,
+                    const ndn::nfd::CommandOptions& options,
+                    int nRetries);
+
+  /**
+   * @brief make and send the remote unregistration command.
+   *
+   * @param nRetries remaining number of retries.
+   */
+  void
+  startUnregistration(const ndn::nfd::ControlParameters& parameters,
+                      const ndn::nfd::CommandOptions& options,
+                      int nRetries);
+  /**
+   * @brief refresh the remotely registered entry if registration
+   *        successes by re-sending the registration command.
+   *
+   * The interval of sending refresh command is defined in the
+   * "remote_register" section of the config file.
+   *
+   * @param parameters the same paremeters from startRegistration.
+   * @param options the same options as in startRegistration.
+   */
+  void
+  onRegSuccess(const ndn::nfd::ControlParameters& parameters,
+               const ndn::nfd::CommandOptions& options);
+
+  /**
+   * @brief retry to send registration command if registration fails.
+   *
+   * the number of retries is defined in the "remote_register"
+   * section of the config file.
+   *
+   * @param code error code.
+   * @param reason error reason in string.
+   * @param parameters the same paremeters from startRegistration.
+   * @param options the same options from startRegistration.
+   * @param nRetries remaining number of retries.
+   */
+  void
+  onRegFailure(uint32_t code, const std::string& reason,
+               const ndn::nfd::ControlParameters& parameters,
+               const ndn::nfd::CommandOptions& options,
+               int nRetries);
+
+  void
+  onUnregSuccess(const ndn::nfd::ControlParameters& parameters,
+                 const ndn::nfd::CommandOptions& options);
+
+  /**
+   * @brief retry to send unregistration command if registration fails.
+   *
+   * the number of retries is defined in the "remote_register"
+   * section of the config file.
+   *
+   * @param code error code.
+   * @param reason error reason in string.
+   * @param parameters the same paremeters as in startRegistration.
+   * @param options the same options as in startRegistration.
+   * @param nRetries remaining number of retries.
+   */
+  void
+  onUnregFailure(uint32_t code, const std::string& reason,
+                 const ndn::nfd::ControlParameters& parameters,
+                 const ndn::nfd::CommandOptions& options,
+                 int nRetries);
+
+  /**
+   * @brief re-register all prefixes
+   *
+   * This is called when a HUB connection is established.
+   */
+  void
+  redoRegistration();
+
+  /**
+   * @brief clear all refresh events
+   *
+   * This is called when all HUB connections are lost.
+   */
+  void
+  clearRefreshEvents();
+
+PUBLIC_WITH_TESTS_ELSE_PRIVATE:
+  /**
+   * When a locally registered prefix triggles remote
+   * registration, we actually register the longest
+   * identity that can sign this prefix to remote hub(s).
+   *
+   * Thus, the remotely reigstered prefix does not equal
+   * to Route Name. So it needs seperate sotrage instead
+   * of storing within the RIB.
+   */
+  typedef std::unordered_map<Name, EventId> RegisteredList;
+  typedef RegisteredList::iterator RegisteredEntryIt;
+  typedef RegisteredList::value_type RegisteredEntry;
+  RegisteredList m_regEntries;
+
+private:
+  ndn::nfd::Controller& m_nfdController;
+  ndn::KeyChain& m_keyChain;
+  Rib& m_rib;
+  ndn::util::signal::ScopedConnection m_afterInsertConnection;
+  ndn::util::signal::ScopedConnection m_afterEraseConnection;
+  ndn::nfd::ControlParameters m_controlParameters;
+  ndn::nfd::CommandOptions m_commandOptions;
+  time::seconds m_refreshInterval;
+  bool m_hasConnectedHub;
+  int m_nRetries;
+
+  static const Name LOCAL_REGISTRATION_PREFIX; // /localhost
+  static const Name REMOTE_HUB_PREFIX; // /localhop/nfd/rib
+  static const name::Component IGNORE_COMMPONENT; // rib
+};
+
+} // namespace rib
+} // namespace nfd
+
+#endif // NFD_RIB_REMOTE_REGISTRATOR_HPP
diff --git a/NFD/rib/rib-entry.cpp b/NFD/rib/rib-entry.cpp
new file mode 100644
index 0000000..3e24275
--- /dev/null
+++ b/NFD/rib/rib-entry.cpp
@@ -0,0 +1,258 @@
+/* -*- 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/>.
+ */
+
+#include "rib-entry.hpp"
+
+#include "core/logger.hpp"
+
+#include <ndn-cxx/management/nfd-control-command.hpp>
+
+NFD_LOG_INIT("RibEntry");
+
+namespace nfd {
+namespace rib {
+
+RibEntry::FaceList::iterator
+RibEntry::findFace(const FaceEntry& face)
+{
+  return std::find_if(begin(), end(), bind(&compareFaceIdAndOrigin, _1, face));
+}
+
+bool
+RibEntry::insertFace(const FaceEntry& entry)
+{
+  iterator it = findFace(entry);
+
+  if (it == end())
+    {
+      if (entry.flags & ndn::nfd::ROUTE_FLAG_CAPTURE)
+        {
+          m_nFacesWithCaptureSet++;
+        }
+
+      m_faces.push_back(entry);
+      return true;
+    }
+  else
+    {
+      return false;
+    }
+}
+
+void
+RibEntry::eraseFace(const FaceEntry& face)
+{
+  RibEntry::iterator it = std::find_if(begin(), end(), bind(&compareFaceIdAndOrigin, _1, face));
+  eraseFace(it);
+}
+
+bool
+RibEntry::hasFace(const FaceEntry& face)
+{
+  RibEntry::const_iterator it = std::find_if(cbegin(), cend(),
+                                             bind(&compareFaceIdAndOrigin, _1, face));
+
+  return it != cend();
+}
+
+bool
+RibEntry::hasFaceId(const uint64_t faceId) const
+{
+  RibEntry::const_iterator it = std::find_if(cbegin(), cend(), bind(&compareFaceId, _1, faceId));
+
+  return it != cend();
+}
+
+void
+RibEntry::addChild(shared_ptr<RibEntry> child)
+{
+  BOOST_ASSERT(!static_cast<bool>(child->getParent()));
+  child->setParent(this->shared_from_this());
+  m_children.push_back(child);
+}
+
+void
+RibEntry::removeChild(shared_ptr<RibEntry> child)
+{
+  BOOST_ASSERT(child->getParent().get() == this);
+  child->setParent(shared_ptr<RibEntry>());
+  m_children.remove(child);
+}
+
+RibEntry::FaceList::iterator
+RibEntry::eraseFace(FaceList::iterator face)
+{
+  if (face != m_faces.end())
+    {
+      if (face->flags & ndn::nfd::ROUTE_FLAG_CAPTURE)
+        {
+          m_nFacesWithCaptureSet--;
+        }
+
+      //cancel any scheduled event
+      NFD_LOG_TRACE("Cancelling expiration eventId: " << face->getExpirationEvent());
+      scheduler::cancel(face->getExpirationEvent());
+
+      return m_faces.erase(face);
+    }
+
+  return m_faces.end();
+}
+
+void
+RibEntry::addInheritedFace(const FaceEntry& face)
+{
+  m_inheritedFaces.push_back(face);
+}
+
+void
+RibEntry::removeInheritedFace(const FaceEntry& face)
+{
+  FaceList::iterator it = findInheritedFace(face);
+  m_inheritedFaces.erase(it);
+}
+
+RibEntry::FaceList::iterator
+RibEntry::findInheritedFace(const FaceEntry& face)
+{
+  return std::find_if(m_inheritedFaces.begin(), m_inheritedFaces.end(),
+                      bind(&compareFaceId, _1, face.faceId));
+}
+
+bool
+RibEntry::hasInheritedFace(const FaceEntry& face)
+{
+  FaceList::const_iterator it = findInheritedFace(face);
+
+  return (it != m_inheritedFaces.end());
+}
+
+bool
+RibEntry::hasCapture() const
+{
+  return m_nFacesWithCaptureSet > 0;
+}
+
+bool
+RibEntry::hasChildInheritOnFaceId(uint64_t faceId) const
+{
+  for (RibEntry::const_iterator it = m_faces.begin(); it != m_faces.end(); ++it)
+    {
+      if (it->faceId == faceId && (it->flags & ndn::nfd::ROUTE_FLAG_CHILD_INHERIT))
+        {
+          return true;
+        }
+    }
+
+  return false;
+}
+
+shared_ptr<FaceEntry>
+RibEntry::getFaceWithLowestCostByFaceId(uint64_t faceId)
+{
+  shared_ptr<FaceEntry> face;
+
+  for (FaceList::iterator it = begin(); it != end(); ++it)
+    {
+      // Correct face ID
+      if (it->faceId == faceId)
+        {
+          // If this is the first face with this ID found
+          if (!static_cast<bool>(face))
+            {
+              face = make_shared<FaceEntry>(*it);
+            }
+          else if (it->cost < face->cost) // Found a face with a lower cost
+            {
+              face = make_shared<FaceEntry>(*it);
+            }
+        }
+    }
+
+    return face;
+}
+
+shared_ptr<FaceEntry>
+RibEntry::getFaceWithLowestCostAndChildInheritByFaceId(uint64_t faceId)
+{
+  shared_ptr<FaceEntry> face;
+
+  for (FaceList::iterator it = begin(); it != end(); ++it)
+    {
+      // Correct face ID and Child Inherit flag set
+      if (it->faceId == faceId && it->flags & ndn::nfd::ROUTE_FLAG_CHILD_INHERIT)
+        {
+          // If this is the first face with this ID found
+          if (!static_cast<bool>(face))
+            {
+              face = make_shared<FaceEntry>(*it);
+            }
+          else if (it->cost < face->cost) // Found a face with a lower cost
+            {
+              face = make_shared<FaceEntry>(*it);
+            }
+        }
+    }
+
+    return face;
+}
+
+std::ostream&
+operator<<(std::ostream& os, const FaceEntry& entry)
+{
+  os << "FaceEntry("
+     << "faceid: " << entry.faceId
+     << ", origin: " << entry.origin
+     << ", cost: " << entry.cost
+     << ", flags: " << entry.flags;
+  if (entry.expires != time::steady_clock::TimePoint::max()) {
+    os << ", expires in: " << (entry.expires - time::steady_clock::now());
+  }
+  else {
+    os << ", never expires";
+  }
+  os << ")";
+
+  return os;
+}
+
+std::ostream&
+operator<<(std::ostream& os, const RibEntry& entry)
+{
+  os << "RibEntry {\n";
+  os << "\tName: " << entry.getName() << "\n";
+
+  for (RibEntry::FaceList::const_iterator faceIt = entry.cbegin(); faceIt != entry.cend(); ++faceIt)
+    {
+      os << "\t" << (*faceIt) << "\n";
+    }
+
+  os << "}";
+
+  return os;
+}
+
+} // namespace rib
+} // namespace nfd
diff --git a/NFD/rib/rib-entry.hpp b/NFD/rib/rib-entry.hpp
new file mode 100644
index 0000000..b17085a
--- /dev/null
+++ b/NFD/rib/rib-entry.hpp
@@ -0,0 +1,259 @@
+/* -*- 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_RIB_RIB_ENTRY_HPP
+#define NFD_RIB_RIB_ENTRY_HPP
+
+#include "face-entry.hpp"
+
+namespace nfd {
+namespace rib {
+
+/** \class RibEntry
+ *  \brief represents a namespace
+ */
+class RibEntry : public enable_shared_from_this<RibEntry>
+{
+public:
+  typedef std::list<FaceEntry> FaceList;
+  typedef FaceList::iterator iterator;
+  typedef FaceList::const_iterator const_iterator;
+
+  RibEntry()
+    : m_nFacesWithCaptureSet(0)
+  {
+  }
+
+  void
+  setName(const Name& prefix);
+
+  const Name&
+  getName() const;
+
+  shared_ptr<RibEntry>
+  getParent() const;
+
+  bool
+  hasParent() const;
+
+  void
+  addChild(shared_ptr<RibEntry> child);
+
+  void
+  removeChild(shared_ptr<RibEntry> child);
+
+  std::list<shared_ptr<RibEntry> >&
+  getChildren();
+
+  bool
+  hasChildren() const;
+
+  /** \brief inserts a new face into the entry's face list
+   *  If another entry already exists with the same faceId and origin,
+   *  the new face is not inserted.
+   *  \return{ true if the face is inserted, false otherwise }
+   */
+  bool
+  insertFace(const FaceEntry& face);
+
+  /** \brief erases a FaceEntry with the same faceId and origin
+   */
+  void
+  eraseFace(const FaceEntry& face);
+
+  /** \brief erases a FaceEntry with the passed iterator
+   *  \return{ an iterator to the element that followed the erased iterator }
+   */
+  iterator
+  eraseFace(FaceList::iterator face);
+
+  bool
+  hasFaceId(const uint64_t faceId) const;
+
+  FaceList&
+  getFaces();
+
+  iterator
+  findFace(const FaceEntry& face);
+
+  bool
+  hasFace(const FaceEntry& face);
+
+  void
+  addInheritedFace(const FaceEntry& face);
+
+  void
+  removeInheritedFace(const FaceEntry& face);
+
+  /** \brief Returns the faces this namespace has inherited.
+   *  The inherited faces returned represent inherited entries this namespace has in the FIB.
+   *  \return{ faces inherited by this namespace }
+   */
+  FaceList&
+  getInheritedFaces();
+
+  /** \brief Finds an inherited face with a matching face ID.
+   *  \return{ An iterator to the matching face if one is found;
+   *           otherwise, an iterator to the end of the entry's
+   *           inherited face list }
+   */
+  FaceList::iterator
+  findInheritedFace(const FaceEntry& face);
+
+  /** \brief Determines if the entry has an inherited face with a matching face ID.
+   *  \return{ True, if a matching inherited face is found; otherwise, false. }
+   */
+  bool
+  hasInheritedFace(const FaceEntry& face);
+
+  bool
+  hasCapture() const;
+
+  /** \brief Determines if the entry has an inherited face with the passed
+   *         face ID and its child inherit flag set.
+   *  \return{ True, if a matching inherited face is found; otherwise, false. }
+   */
+  bool
+  hasChildInheritOnFaceId(uint64_t faceId) const;
+
+  /** \brief Returns the face with the lowest cost that has the passed face ID.
+   *  \return{ The face with with the lowest cost that has the passed face ID}
+   */
+  shared_ptr<FaceEntry>
+  getFaceWithLowestCostByFaceId(uint64_t faceId);
+
+  /** \brief Returns the face with the lowest cost that has the passed face ID
+   *         and its child inherit flag set.
+   *  \return{ The face with with the lowest cost that has the passed face ID
+   *           and its child inherit flag set }
+   */
+  shared_ptr<FaceEntry>
+  getFaceWithLowestCostAndChildInheritByFaceId(uint64_t faceId);
+
+  const_iterator
+  cbegin() const;
+
+  const_iterator
+  cend() const;
+
+  iterator
+  begin();
+
+  iterator
+  end();
+
+private:
+  void
+  setParent(shared_ptr<RibEntry> parent);
+
+private:
+  Name m_name;
+  std::list<shared_ptr<RibEntry> > m_children;
+  shared_ptr<RibEntry> m_parent;
+  FaceList m_faces;
+  FaceList m_inheritedFaces;
+
+  /** \brief The number of faces on this namespace with the capture flag set.
+   *
+   *  This count is used to check if the namespace will block inherited faces.
+   *  If the number is greater than zero, a route on the namespace has it's capture
+   *  flag set which means the namespace should not inherit any faces.
+   */
+  uint64_t m_nFacesWithCaptureSet;
+};
+
+inline void
+RibEntry::setName(const Name& prefix)
+{
+  m_name = prefix;
+}
+
+inline const Name&
+RibEntry::getName() const
+{
+  return m_name;
+}
+
+inline void
+RibEntry::setParent(shared_ptr<RibEntry> parent)
+{
+  m_parent = parent;
+}
+
+inline shared_ptr<RibEntry>
+RibEntry::getParent() const
+{
+  return m_parent;
+}
+
+inline std::list<shared_ptr<RibEntry> >&
+RibEntry::getChildren()
+{
+  return m_children;
+}
+
+inline RibEntry::FaceList&
+RibEntry::getFaces()
+{
+  return m_faces;
+}
+
+inline RibEntry::FaceList&
+RibEntry::getInheritedFaces()
+{
+  return m_inheritedFaces;
+}
+
+inline RibEntry::const_iterator
+RibEntry::cbegin() const
+{
+  return m_faces.begin();
+}
+
+inline RibEntry::const_iterator
+RibEntry::cend() const
+{
+  return m_faces.end();
+}
+
+inline RibEntry::iterator
+RibEntry::begin()
+{
+  return m_faces.begin();
+}
+
+inline RibEntry::iterator
+RibEntry::end()
+{
+  return m_faces.end();
+}
+
+std::ostream&
+operator<<(std::ostream& os, const RibEntry& entry);
+
+} // namespace rib
+} // namespace nfd
+
+#endif // NFD_RIB_RIB_ENTRY_HPP
diff --git a/NFD/rib/rib-manager.cpp b/NFD/rib/rib-manager.cpp
new file mode 100644
index 0000000..7d0f5b3
--- /dev/null
+++ b/NFD/rib/rib-manager.cpp
@@ -0,0 +1,880 @@
+/* -*- 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/>.
+ */
+
+#include "rib-manager.hpp"
+#include "core/global-io.hpp"
+#include "core/logger.hpp"
+#include "core/scheduler.hpp"
+#include <ndn-cxx/management/nfd-face-status.hpp>
+
+namespace nfd {
+namespace rib {
+
+NFD_LOG_INIT("RibManager");
+
+const Name RibManager::COMMAND_PREFIX = "/localhost/nfd/rib";
+const Name RibManager::REMOTE_COMMAND_PREFIX = "/localhop/nfd/rib";
+const Name RibManager::FACES_LIST_DATASET_PREFIX = "/localhost/nfd/faces/list";
+
+const size_t RibManager::COMMAND_UNSIGNED_NCOMPS =
+  RibManager::COMMAND_PREFIX.size() +
+  1 + // verb
+  1;  // verb options
+
+const size_t RibManager::COMMAND_SIGNED_NCOMPS =
+  RibManager::COMMAND_UNSIGNED_NCOMPS +
+  4; // (timestamp, nonce, signed info tlv, signature tlv)
+
+const RibManager::SignedVerbAndProcessor RibManager::SIGNED_COMMAND_VERBS[] =
+  {
+    SignedVerbAndProcessor(
+                           Name::Component("register"),
+                           &RibManager::registerEntry
+                           ),
+
+    SignedVerbAndProcessor(
+                           Name::Component("unregister"),
+                           &RibManager::unregisterEntry
+                           ),
+  };
+
+const RibManager::UnsignedVerbAndProcessor RibManager::UNSIGNED_COMMAND_VERBS[] =
+  {
+    UnsignedVerbAndProcessor(
+                             Name::Component("list"),
+                             &RibManager::listEntries
+                             ),
+  };
+
+const Name RibManager::LIST_COMMAND_PREFIX("/localhost/nfd/rib/list");
+const size_t RibManager::LIST_COMMAND_NCOMPS = LIST_COMMAND_PREFIX.size();
+
+const time::seconds RibManager::ACTIVE_FACE_FETCH_INTERVAL = time::seconds(300);
+
+RibManager::RibManager(ndn::Face& face)
+  : m_face(face)
+  , m_nfdController(m_face, m_keyChain)
+  , m_localhostValidator(m_face)
+  , m_localhopValidator(m_face)
+  , m_faceMonitor(m_face)
+  , m_isLocalhopEnabled(false)
+  , m_remoteRegistrator(m_nfdController, m_keyChain, m_managedRib)
+  , m_ribStatusPublisher(m_managedRib, face, LIST_COMMAND_PREFIX, m_keyChain)
+  , m_lastTransactionId(0)
+  , m_signedVerbDispatch(SIGNED_COMMAND_VERBS,
+                         SIGNED_COMMAND_VERBS +
+                         (sizeof(SIGNED_COMMAND_VERBS) / sizeof(SignedVerbAndProcessor)))
+  , m_unsignedVerbDispatch(UNSIGNED_COMMAND_VERBS,
+                           UNSIGNED_COMMAND_VERBS +
+                           (sizeof(UNSIGNED_COMMAND_VERBS) / sizeof(UnsignedVerbAndProcessor)))
+{
+}
+
+RibManager::~RibManager()
+{
+  scheduler::cancel(m_activeFaceFetchEvent);
+}
+
+void
+RibManager::startListening(const Name& commandPrefix, const ndn::OnInterest& onRequest)
+{
+  NFD_LOG_INFO("Listening on: " << commandPrefix);
+
+  m_nfdController.start<ndn::nfd::FibAddNextHopCommand>(
+    ControlParameters()
+      .setName(commandPrefix)
+      .setFaceId(0),
+    bind(&RibManager::onNrdCommandPrefixAddNextHopSuccess, this, cref(commandPrefix)),
+    bind(&RibManager::onNrdCommandPrefixAddNextHopError, this, cref(commandPrefix), _2));
+
+  m_face.setInterestFilter(commandPrefix, onRequest);
+}
+
+void
+RibManager::registerWithNfd()
+{
+  //check whether the components of localhop and localhost prefixes are same
+  BOOST_ASSERT(COMMAND_PREFIX.size() == REMOTE_COMMAND_PREFIX.size());
+
+  this->startListening(COMMAND_PREFIX, bind(&RibManager::onLocalhostRequest, this, _2));
+
+  if (m_isLocalhopEnabled) {
+    this->startListening(REMOTE_COMMAND_PREFIX,
+                         bind(&RibManager::onLocalhopRequest, this, _2));
+  }
+
+  NFD_LOG_INFO("Start monitoring face create/destroy events");
+  m_faceMonitor.onNotification += bind(&RibManager::onNotification, this, _1);
+  m_faceMonitor.start();
+
+  scheduleActiveFaceFetch(ACTIVE_FACE_FETCH_INTERVAL);
+}
+
+void
+RibManager::setConfigFile(ConfigFile& configFile)
+{
+  configFile.addSectionHandler("rib",
+                               bind(&RibManager::onConfig, this, _1, _2, _3));
+}
+
+void
+RibManager::onConfig(const ConfigSection& configSection,
+                     bool isDryRun,
+                     const std::string& filename)
+{
+  bool isRemoteRegisterEnabled = false;
+
+  for (ConfigSection::const_iterator i = configSection.begin();
+       i != configSection.end(); ++i)
+    {
+      if (i->first == "localhost_security")
+          m_localhostValidator.load(i->second, filename);
+      else if (i->first == "localhop_security")
+        {
+          m_localhopValidator.load(i->second, filename);
+          m_isLocalhopEnabled = true;
+        }
+      else if (i->first == "remote_register")
+        {
+          m_remoteRegistrator.loadConfig(i->second);
+          isRemoteRegisterEnabled = true;
+          // avoid other actions when isDryRun == true
+          if (isDryRun)
+            {
+              continue;
+            }
+
+          m_remoteRegistrator.enable();
+        }
+      else
+        throw Error("Unrecognized rib property: " + i->first);
+    }
+
+  if (!isRemoteRegisterEnabled)
+    {
+      m_remoteRegistrator.disable();
+    }
+}
+
+void
+RibManager::sendResponse(const Name& name,
+                         const ControlResponse& response)
+{
+  const Block& encodedControl = response.wireEncode();
+
+  shared_ptr<Data> responseData = make_shared<Data>(name);
+  responseData->setContent(encodedControl);
+
+  m_keyChain.sign(*responseData);
+  m_face.put(*responseData);
+}
+
+void
+RibManager::sendResponse(const Name& name,
+                         uint32_t code,
+                         const std::string& text)
+{
+  ControlResponse response(code, text);
+  sendResponse(name, response);
+}
+
+void
+RibManager::onLocalhostRequest(const Interest& request)
+{
+  const Name& command = request.getName();
+  const Name::Component& verb = command.get(COMMAND_PREFIX.size());
+
+  UnsignedVerbDispatchTable::const_iterator unsignedVerbProcessor = m_unsignedVerbDispatch.find(verb);
+
+  if (unsignedVerbProcessor != m_unsignedVerbDispatch.end())
+    {
+      NFD_LOG_DEBUG("command result: processing unsigned verb: " << verb);
+      (unsignedVerbProcessor->second)(this, request);
+    }
+  else
+    {
+      m_localhostValidator.validate(request,
+                                    bind(&RibManager::onCommandValidated, this, _1),
+                                    bind(&RibManager::onCommandValidationFailed, this, _1, _2));
+    }
+}
+
+void
+RibManager::onLocalhopRequest(const Interest& request)
+{
+  m_localhopValidator.validate(request,
+                               bind(&RibManager::onCommandValidated, this, _1),
+                               bind(&RibManager::onCommandValidationFailed, this, _1, _2));
+}
+
+void
+RibManager::onCommandValidated(const shared_ptr<const Interest>& request)
+{
+  // REMOTE_COMMAND_PREFIX number of componenets are same as
+  // NRD_COMMAND_PREFIX's so no extra checks are required.
+
+  const Name& command = request->getName();
+  const Name::Component& verb = command[COMMAND_PREFIX.size()];
+  const Name::Component& parameterComponent = command[COMMAND_PREFIX.size() + 1];
+
+  SignedVerbDispatchTable::const_iterator verbProcessor = m_signedVerbDispatch.find(verb);
+  if (verbProcessor != m_signedVerbDispatch.end())
+    {
+      ControlParameters parameters;
+      if (!extractParameters(parameterComponent, parameters))
+        {
+          NFD_LOG_DEBUG("command result: malformed verb: " << verb);
+          if (static_cast<bool>(request))
+            sendResponse(command, 400, "Malformed command");
+          return;
+        }
+
+      NFD_LOG_DEBUG("command result: processing verb: " << verb);
+      (verbProcessor->second)(this, request, parameters);
+    }
+  else
+    {
+      NFD_LOG_DEBUG("Unsupported command: " << verb);
+      if (static_cast<bool>(request))
+        sendResponse(request->getName(), 501, "Unsupported command");
+    }
+}
+
+void
+RibManager::registerEntry(const shared_ptr<const Interest>& request,
+                          ControlParameters& parameters)
+{
+  ndn::nfd::RibRegisterCommand command;
+
+  if (!validateParameters(command, parameters))
+    {
+      NFD_LOG_DEBUG("register result: FAIL reason: malformed");
+
+      if (static_cast<bool>(request))
+        {
+          sendResponse(request->getName(), 400, "Malformed command");
+        }
+
+      return;
+    }
+
+  bool isSelfRegistration = (!parameters.hasFaceId() || parameters.getFaceId() == 0);
+  if (isSelfRegistration)
+    {
+      parameters.setFaceId(request->getIncomingFaceId());
+    }
+
+  FaceEntry faceEntry;
+  faceEntry.faceId = parameters.getFaceId();
+  faceEntry.origin = parameters.getOrigin();
+  faceEntry.cost = parameters.getCost();
+  faceEntry.flags = parameters.getFlags();
+
+  if (parameters.hasExpirationPeriod() &&
+      parameters.getExpirationPeriod() != time::milliseconds::max())
+    {
+      faceEntry.expires = time::steady_clock::now() + parameters.getExpirationPeriod();
+
+      // Schedule a new event, the old one will be cancelled during rib insertion.
+      EventId eventId;
+      eventId = scheduler::schedule(parameters.getExpirationPeriod(),
+                                    bind(&RibManager::expireEntry,
+                                    this, shared_ptr<Interest>(), parameters));
+      NFD_LOG_TRACE("Scheduled unregistration at: " << faceEntry.expires <<
+                    " with EventId: " << eventId);
+
+      //set the  NewEventId of this entry
+      faceEntry.setExpirationEvent(eventId);
+    }
+  else
+    {
+      faceEntry.expires = time::steady_clock::TimePoint::max();
+    }
+
+  NFD_LOG_TRACE("register prefix: " << faceEntry);
+
+  m_managedRib.insert(parameters.getName(), faceEntry);
+  m_registeredFaces.insert(faceEntry.faceId);
+
+  sendUpdatesToFib(request, parameters);
+}
+
+void
+RibManager::expireEntry(const shared_ptr<const Interest>& request, ControlParameters& params)
+{
+  FaceEntry face;
+  face.faceId = params.getFaceId();
+  face.origin = params.getOrigin();
+  face.cost = params.getCost();
+  face.flags = params.getFlags();
+
+  NFD_LOG_DEBUG(face << " for " << params.getName() << " has expired");
+  unregisterEntry(request, params);
+}
+
+void
+RibManager::unregisterEntry(const shared_ptr<const Interest>& request,
+                            ControlParameters& params)
+{
+  ndn::nfd::RibUnregisterCommand command;
+
+  //passing all parameters gives error in validation.
+  //so passing only the required arguments.
+  ControlParameters parameters;
+  parameters.setName(params.getName());
+  if (params.hasFaceId())
+    parameters.setFaceId(params.getFaceId());
+  if (params.hasOrigin())
+    parameters.setOrigin(params.getOrigin());
+
+  if (!validateParameters(command, parameters))
+    {
+      NFD_LOG_DEBUG("unregister result: FAIL reason: malformed");
+      if (static_cast<bool>(request))
+        sendResponse(request->getName(), 400, "Malformed command");
+      return;
+    }
+
+  bool isSelfRegistration = (!parameters.hasFaceId() || parameters.getFaceId() == 0);
+  if (isSelfRegistration)
+    {
+      parameters.setFaceId(request->getIncomingFaceId());
+    }
+
+  FaceEntry faceEntry;
+  faceEntry.faceId = parameters.getFaceId();
+  faceEntry.origin = parameters.getOrigin();
+
+  NFD_LOG_TRACE("unregister prefix: " << faceEntry);
+
+  m_managedRib.erase(parameters.getName(), faceEntry);
+
+  sendUpdatesToFib(request, parameters);
+}
+
+void
+RibManager::onCommandValidationFailed(const shared_ptr<const Interest>& request,
+                                      const std::string& failureInfo)
+{
+  NFD_LOG_DEBUG("RibRequestValidationFailed: " << failureInfo);
+  if (static_cast<bool>(request))
+    sendResponse(request->getName(), 403, failureInfo);
+}
+
+
+bool
+RibManager::extractParameters(const Name::Component& parameterComponent,
+                              ControlParameters& extractedParameters)
+{
+  try
+    {
+      Block rawParameters = parameterComponent.blockFromValue();
+      extractedParameters.wireDecode(rawParameters);
+    }
+  catch (const tlv::Error&)
+    {
+      return false;
+    }
+
+  NFD_LOG_DEBUG("Parameters parsed OK");
+  return true;
+}
+
+bool
+RibManager::validateParameters(const ControlCommand& command,
+                               ControlParameters& parameters)
+{
+  try
+    {
+      command.validateRequest(parameters);
+    }
+  catch (const ControlCommand::ArgumentError&)
+    {
+      return false;
+    }
+
+  command.applyDefaultsToRequest(parameters);
+
+  return true;
+}
+
+void
+RibManager::onCommandError(uint32_t code, const std::string& error,
+                           const shared_ptr<const Interest>& request,
+                           const FaceEntry& faceEntry)
+{
+  NFD_LOG_ERROR("NFD returned an error: " << error << " (code: " << code << ")");
+
+  ControlResponse response;
+
+  if (code == 404)
+    {
+      response.setCode(code);
+      response.setText(error);
+    }
+  else
+    {
+      response.setCode(533);
+      std::ostringstream os;
+      os << "Failure to update NFD " << "(NFD Error: " << code << " " << error << ")";
+      response.setText(os.str());
+    }
+
+  if (static_cast<bool>(request))
+    sendResponse(request->getName(), response);
+}
+
+void
+RibManager::onRegSuccess(const shared_ptr<const Interest>& request,
+                         const ControlParameters& parameters,
+                         const FaceEntry& faceEntry)
+{
+  ControlResponse response;
+
+  response.setCode(200);
+  response.setText("Success");
+  response.setBody(parameters.wireEncode());
+
+  NFD_LOG_TRACE("onRegSuccess: registered " << faceEntry);
+
+  if (static_cast<bool>(request))
+    sendResponse(request->getName(), response);
+}
+
+
+void
+RibManager::onUnRegSuccess(const shared_ptr<const Interest>& request,
+                           const ControlParameters& parameters,
+                           const FaceEntry& faceEntry)
+{
+  ControlResponse response;
+
+  response.setCode(200);
+  response.setText("Success");
+  response.setBody(parameters.wireEncode());
+
+  NFD_LOG_TRACE("onUnRegSuccess: unregistered " << faceEntry);
+
+  if (static_cast<bool>(request))
+    sendResponse(request->getName(), response);
+}
+
+void
+RibManager::sendSuccessResponse(const shared_ptr<const Interest>& request,
+                                const ControlParameters& parameters)
+{
+  if (!static_cast<bool>(request))
+    {
+      return;
+    }
+
+  ControlResponse response;
+
+  response.setCode(200);
+  response.setText("Success");
+  response.setBody(parameters.wireEncode());
+
+  if (static_cast<bool>(request))
+    sendResponse(request->getName(), response);
+}
+
+void
+RibManager::sendErrorResponse(uint32_t code, const std::string& error,
+                              const shared_ptr<const Interest>& request)
+{
+  NFD_LOG_ERROR("NFD returned an error: " << error << " (code: " << code << ")");
+
+  if (!static_cast<bool>(request))
+    {
+      return;
+    }
+
+  ControlResponse response;
+
+  if (code == 404)
+    {
+      response.setCode(code);
+      response.setText(error);
+    }
+  else
+    {
+      response.setCode(533);
+      std::ostringstream os;
+      os << "Failure to update NFD " << "(NFD Error: " << code << " " << error << ")";
+      response.setText(os.str());
+    }
+
+  if (static_cast<bool>(request))
+    sendResponse(request->getName(), response);
+}
+
+void
+RibManager::onNrdCommandPrefixAddNextHopSuccess(const Name& prefix)
+{
+  NFD_LOG_DEBUG("Successfully registered " + prefix.toUri() + " with NFD");
+}
+
+void
+RibManager::onNrdCommandPrefixAddNextHopError(const Name& name, const std::string& msg)
+{
+  throw Error("Error in setting interest filter (" + name.toUri() + "): " + msg);
+}
+
+bool
+RibManager::isTransactionComplete(const TransactionId transactionId)
+{
+  FibTransactionTable::iterator it = m_pendingFibTransactions.find(transactionId);
+
+  if (it != m_pendingFibTransactions.end())
+    {
+      int& updatesLeft = it->second;
+
+      updatesLeft--;
+
+      // All of the updates have been applied successfully
+      if (updatesLeft == 0)
+        {
+          m_pendingFibTransactions.erase(it);
+          return true;
+        }
+    }
+
+    return false;
+}
+
+void
+RibManager::invalidateTransaction(const TransactionId transactionId)
+{
+  FibTransactionTable::iterator it = m_pendingFibTransactions.find(transactionId);
+
+  if (it != m_pendingFibTransactions.end())
+    {
+      m_pendingFibTransactions.erase(it);
+    }
+}
+
+void
+RibManager::onAddNextHopSuccess(const shared_ptr<const Interest>& request,
+                                const ControlParameters& parameters,
+                                const TransactionId transactionId,
+                                const bool shouldSendResponse)
+{
+  if (isTransactionComplete(transactionId) && shouldSendResponse)
+    {
+      sendSuccessResponse(request, parameters);
+    }
+}
+
+void
+RibManager::onAddNextHopError(uint32_t code, const std::string& error,
+                              const shared_ptr<const Interest>& request,
+                              const TransactionId transactionId, const bool shouldSendResponse)
+{
+  invalidateTransaction(transactionId);
+
+  if (shouldSendResponse)
+  {
+    sendErrorResponse(code, error, request);
+  }
+
+  // Since the FIB rejected the update, clean up the invalid face
+  scheduleActiveFaceFetch(time::seconds(1));
+}
+
+void
+RibManager::onRemoveNextHopSuccess(const shared_ptr<const Interest>& request,
+                                   const ControlParameters& parameters,
+                                   const TransactionId transactionId,
+                                   const bool shouldSendResponse)
+{
+  if (isTransactionComplete(transactionId) && shouldSendResponse)
+    {
+      sendSuccessResponse(request, parameters);
+    }
+}
+
+void
+RibManager::onRemoveNextHopError(uint32_t code, const std::string& error,
+                                 const shared_ptr<const Interest>& request,
+                                 const TransactionId transactionId, const bool shouldSendResponse)
+{
+  invalidateTransaction(transactionId);
+
+  if (shouldSendResponse)
+  {
+    sendErrorResponse(code, error, request);
+  }
+}
+
+void
+RibManager::onControlHeaderSuccess()
+{
+  NFD_LOG_DEBUG("Local control header enabled");
+}
+
+void
+RibManager::onControlHeaderError(uint32_t code, const std::string& reason)
+{
+  std::ostringstream os;
+  os << "Couldn't enable local control header "
+     << "(code: " << code << ", info: " << reason << ")";
+  throw Error(os.str());
+}
+
+void
+RibManager::enableLocalControlHeader()
+{
+  m_nfdController.start<ndn::nfd::FaceEnableLocalControlCommand>(
+    ControlParameters()
+      .setLocalControlFeature(ndn::nfd::LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID),
+    bind(&RibManager::onControlHeaderSuccess, this),
+    bind(&RibManager::onControlHeaderError, this, _1, _2));
+}
+
+void
+RibManager::onNotification(const FaceEventNotification& notification)
+{
+  NFD_LOG_TRACE("onNotification: " << notification);
+
+  if (notification.getKind() == ndn::nfd::FACE_EVENT_DESTROYED)
+    {
+      NFD_LOG_DEBUG("Received notification for destroyed faceId: " << notification.getFaceId());
+
+      scheduler::schedule(time::seconds(0),
+                          bind(&RibManager::processErasureAfterNotification, this,
+                               notification.getFaceId()));
+    }
+}
+
+void
+RibManager::processErasureAfterNotification(uint64_t faceId)
+{
+  m_managedRib.erase(faceId);
+  m_registeredFaces.erase(faceId);
+
+  sendUpdatesToFibAfterFaceDestroyEvent();
+}
+
+void
+RibManager::sendUpdatesToFib(const shared_ptr<const Interest>& request,
+                             const ControlParameters& parameters)
+{
+  const Rib::FibUpdateList& updates = m_managedRib.getFibUpdates();
+
+  // If no updates were generated, consider the operation a success
+  if (updates.empty())
+    {
+      sendSuccessResponse(request, parameters);
+      return;
+    }
+
+  bool shouldWaitToRespond = false;
+
+  // An application request should wait for all FIB updates to be applied
+  // successfully before sending a response
+  if (parameters.getOrigin() == ndn::nfd::ROUTE_ORIGIN_APP)
+    {
+      shouldWaitToRespond = true;
+    }
+  else // Respond immediately
+    {
+      sendSuccessResponse(request, parameters);
+    }
+
+  std::string updateString = (updates.size() == 1) ? " update" : " updates";
+  NFD_LOG_DEBUG("Applying " << updates.size() << updateString << " to FIB");
+
+  // Assign an ID to this FIB transaction
+  TransactionId currentTransactionId = ++m_lastTransactionId;
+
+  // Add this transaction to the transaction table
+  m_pendingFibTransactions[currentTransactionId] = updates.size();
+
+  for (Rib::FibUpdateList::const_iterator it = updates.begin(); it != updates.end(); ++it)
+    {
+      shared_ptr<const FibUpdate> update(*it);
+      NFD_LOG_DEBUG("Sending FIB update: " << *update);
+
+      if (update->action == FibUpdate::ADD_NEXTHOP)
+        {
+          FaceEntry faceEntry;
+          faceEntry.faceId = update->faceId;
+          faceEntry.cost = update->cost;
+
+          m_nfdController.start<ndn::nfd::FibAddNextHopCommand>(
+            ControlParameters()
+              .setName(update->name)
+              .setFaceId(faceEntry.faceId)
+              .setCost(faceEntry.cost),
+            bind(&RibManager::onAddNextHopSuccess, this, request,
+                                                         parameters,
+                                                         currentTransactionId,
+                                                         shouldWaitToRespond),
+            bind(&RibManager::onAddNextHopError, this, _1, _2, request, currentTransactionId,
+                                                                        shouldWaitToRespond));
+        }
+      else if (update->action == FibUpdate::REMOVE_NEXTHOP)
+        {
+          FaceEntry faceEntry;
+          faceEntry.faceId = update->faceId;
+
+          m_nfdController.start<ndn::nfd::FibRemoveNextHopCommand>(
+            ControlParameters()
+              .setName(update->name)
+              .setFaceId(faceEntry.faceId),
+            bind(&RibManager::onRemoveNextHopSuccess, this, request,
+                                                            parameters,
+                                                            currentTransactionId,
+                                                            shouldWaitToRespond),
+            bind(&RibManager::onRemoveNextHopError, this, _1, _2, request, currentTransactionId,
+                                                                           shouldWaitToRespond));
+        }
+    }
+
+  m_managedRib.clearFibUpdates();
+}
+
+void
+RibManager::sendUpdatesToFibAfterFaceDestroyEvent()
+{
+  ControlParameters parameters;
+  parameters.setOrigin(ndn::nfd::ROUTE_ORIGIN_STATIC);
+
+  sendUpdatesToFib(shared_ptr<const Interest>(), parameters);
+}
+
+void
+RibManager::listEntries(const Interest& request)
+{
+  const Name& command = request.getName();
+  const size_t commandNComps = command.size();
+
+  if (commandNComps < LIST_COMMAND_NCOMPS ||
+      !LIST_COMMAND_PREFIX.isPrefixOf(command))
+    {
+      NFD_LOG_DEBUG("command result: malformed");
+      sendResponse(command, 400, "Malformed command");
+      return;
+    }
+
+  m_ribStatusPublisher.publish();
+}
+
+void
+RibManager::scheduleActiveFaceFetch(const time::seconds& timeToWait)
+{
+  scheduler::cancel(m_activeFaceFetchEvent);
+
+  m_activeFaceFetchEvent = scheduler::schedule(timeToWait,
+                                               bind(&RibManager::fetchActiveFaces, this));
+}
+
+void
+RibManager::fetchActiveFaces()
+{
+  NFD_LOG_DEBUG("Fetching active faces");
+
+  Interest interest(FACES_LIST_DATASET_PREFIX);
+  interest.setChildSelector(1);
+  interest.setMustBeFresh(true);
+
+  shared_ptr<ndn::OBufferStream> buffer = make_shared<ndn::OBufferStream>();
+
+  m_face.expressInterest(interest,
+                         bind(&RibManager::fetchSegments, this, _2, buffer),
+                         bind(&RibManager::onFetchFaceStatusTimeout, this));
+}
+
+void
+RibManager::fetchSegments(const Data& data, shared_ptr<ndn::OBufferStream> buffer)
+{
+  buffer->write(reinterpret_cast<const char*>(data.getContent().value()),
+                data.getContent().value_size());
+
+  uint64_t currentSegment = data.getName().get(-1).toSegment();
+
+  const name::Component& finalBlockId = data.getMetaInfo().getFinalBlockId();
+  if (finalBlockId.empty() || finalBlockId.toSegment() > currentSegment)
+    {
+      m_face.expressInterest(data.getName().getPrefix(-1).appendSegment(currentSegment+1),
+                             bind(&RibManager::fetchSegments, this, _2, buffer),
+                             bind(&RibManager::onFetchFaceStatusTimeout, this));
+    }
+  else
+    {
+      removeInvalidFaces(buffer);
+    }
+}
+
+void
+RibManager::removeInvalidFaces(shared_ptr<ndn::OBufferStream> buffer)
+{
+  NFD_LOG_DEBUG("Checking for invalid face registrations");
+
+  ndn::ConstBufferPtr buf = buffer->buf();
+
+  Block block;
+  size_t offset = 0;
+  FaceIdSet activeFaces;
+
+  while (offset < buf->size())
+    {
+      if (!Block::fromBuffer(buf, offset, block))
+        {
+          std::cerr << "ERROR: cannot decode FaceStatus TLV" << std::endl;
+          break;
+        }
+
+      offset += block.size();
+
+      ndn::nfd::FaceStatus status(block);
+      activeFaces.insert(status.getFaceId());
+    }
+
+  // Look for face IDs that were registered but not active to find missed
+  // face destroyed events
+  for (FaceIdSet::iterator it = m_registeredFaces.begin(); it != m_registeredFaces.end(); ++it)
+    {
+      if (activeFaces.find(*it) == activeFaces.end())
+        {
+          NFD_LOG_DEBUG("Removing invalid face ID: " << *it);
+          scheduler::schedule(time::seconds(0),
+                              bind(&RibManager::processErasureAfterNotification, this, *it));
+        }
+    }
+
+  // Reschedule the check for future clean up
+  scheduleActiveFaceFetch(ACTIVE_FACE_FETCH_INTERVAL);
+}
+
+void
+RibManager::onFetchFaceStatusTimeout()
+{
+  std::cerr << "Face Status Dataset request timed out" << std::endl;
+  scheduleActiveFaceFetch(ACTIVE_FACE_FETCH_INTERVAL);
+}
+
+} // namespace rib
+} // namespace nfd
diff --git a/NFD/rib/rib-manager.hpp b/NFD/rib/rib-manager.hpp
new file mode 100644
index 0000000..181d210
--- /dev/null
+++ b/NFD/rib/rib-manager.hpp
@@ -0,0 +1,311 @@
+/* -*- 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_RIB_RIB_MANAGER_HPP
+#define NFD_RIB_RIB_MANAGER_HPP
+
+#include "rib.hpp"
+#include "core/config-file.hpp"
+#include "rib-status-publisher.hpp"
+#include "remote-registrator.hpp"
+
+#include <ndn-cxx/security/validator-config.hpp>
+#include <ndn-cxx/management/nfd-face-monitor.hpp>
+#include <ndn-cxx/management/nfd-controller.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>
+
+namespace nfd {
+namespace rib {
+
+using ndn::nfd::ControlCommand;
+using ndn::nfd::ControlResponse;
+using ndn::nfd::ControlParameters;
+
+using ndn::nfd::FaceEventNotification;
+
+class RibManager : noncopyable
+{
+public:
+  class Error : public std::runtime_error
+  {
+  public:
+    explicit
+    Error(const std::string& what)
+      : std::runtime_error(what)
+    {
+    }
+  };
+
+  explicit
+  RibManager(ndn::Face& face);
+
+  ~RibManager();
+
+  void
+  registerWithNfd();
+
+  void
+  enableLocalControlHeader();
+
+  void
+  setConfigFile(ConfigFile& configFile);
+
+private:
+  typedef uint32_t TransactionId;
+
+  void
+  onConfig(const ConfigSection& configSection,
+           bool isDryRun,
+           const std::string& filename);
+
+  void
+  startListening(const Name& commandPrefix, const ndn::OnInterest& onRequest);
+
+  void
+  onLocalhopRequest(const Interest& request);
+
+  void
+  onLocalhostRequest(const Interest& request);
+
+  void
+  sendResponse(const Name& name,
+               const ControlResponse& response);
+
+  void
+  sendResponse(const Name& name,
+               uint32_t code,
+               const std::string& text);
+
+  void
+  sendSuccessResponse(const shared_ptr<const Interest>& request,
+                      const ControlParameters& parameters);
+
+  void
+  sendErrorResponse(uint32_t code, const std::string& error,
+                    const shared_ptr<const Interest>& request);
+
+  void
+  registerEntry(const shared_ptr<const Interest>& request,
+                ControlParameters& parameters);
+
+  void
+  unregisterEntry(const shared_ptr<const Interest>& request,
+                  ControlParameters& parameters);
+
+  void
+  expireEntry(const shared_ptr<const Interest>& request, ControlParameters& params);
+
+  void
+  onCommandValidated(const shared_ptr<const Interest>& request);
+
+  void
+  onCommandValidationFailed(const shared_ptr<const Interest>& request,
+                            const std::string& failureInfo);
+
+
+  void
+  onCommandError(uint32_t code, const std::string& error,
+                 const shared_ptr<const Interest>& request,
+                 const FaceEntry& faceEntry);
+
+  void
+  onRegSuccess(const shared_ptr<const Interest>& request,
+               const ControlParameters& parameters,
+               const FaceEntry& faceEntry);
+
+  void
+  onUnRegSuccess(const shared_ptr<const Interest>& request,
+                 const ControlParameters& parameters,
+                 const FaceEntry& faceEntry);
+
+  void
+  onNrdCommandPrefixAddNextHopSuccess(const Name& prefix);
+
+  void
+  onNrdCommandPrefixAddNextHopError(const Name& name, const std::string& msg);
+
+  void
+  onAddNextHopSuccess(const shared_ptr<const Interest>& request,
+                      const ControlParameters& parameters,
+                      const TransactionId transactionId,
+                      const bool shouldSendResponse);
+
+  void
+  onAddNextHopError(uint32_t code, const std::string& error,
+                    const shared_ptr<const Interest>& request,
+                    const TransactionId transactionId, const bool shouldSendResponse);
+
+  void
+  onRemoveNextHopSuccess(const shared_ptr<const Interest>& request,
+                         const ControlParameters& parameters,
+                         const TransactionId transactionId,
+                         const bool shouldSendResponse);
+
+  void
+  onRemoveNextHopError(uint32_t code, const std::string& error,
+                       const shared_ptr<const Interest>& request,
+                       const TransactionId transactionId, const bool shouldSendResponse);
+
+  void
+  onControlHeaderSuccess();
+
+  void
+  onControlHeaderError(uint32_t code, const std::string& reason);
+
+  static bool
+  extractParameters(const Name::Component& parameterComponent,
+                    ControlParameters& extractedParameters);
+
+  bool
+  validateParameters(const ControlCommand& command,
+                     ControlParameters& parameters);
+
+  void
+  onNotification(const FaceEventNotification& notification);
+
+  void
+  processErasureAfterNotification(uint64_t faceId);
+
+  void
+  sendUpdatesToFib(const shared_ptr<const Interest>& request,
+                   const ControlParameters& parameters);
+
+  void
+  sendUpdatesToFibAfterFaceDestroyEvent();
+
+  /** \brief Checks if the transaction has received all of the expected responses
+   *         from the FIB.
+   *  \return{ True if the transaction with the passed transactionId has applied
+   *           all of its FIB updates successfully; otherwise, false }
+   */
+  bool
+  isTransactionComplete(const TransactionId transactionId);
+
+  void
+  invalidateTransaction(const TransactionId transactionId);
+
+  void
+  listEntries(const Interest& request);
+
+  void
+  scheduleActiveFaceFetch(const time::seconds& timeToWait);
+
+  void
+  fetchActiveFaces();
+
+  void
+  fetchSegments(const Data& data, shared_ptr<ndn::OBufferStream> buffer);
+
+  void
+  onFetchFaceStatusTimeout();
+
+PUBLIC_WITH_TESTS_ELSE_PRIVATE:
+  /** \param buffer Face dataset contents
+  */
+  void
+  removeInvalidFaces(shared_ptr<ndn::OBufferStream> buffer);
+
+PUBLIC_WITH_TESTS_ELSE_PRIVATE:
+  Rib m_managedRib;
+
+private:
+  ndn::Face& m_face;
+  ndn::nfd::Controller m_nfdController;
+  ndn::KeyChain m_keyChain;
+  ndn::ValidatorConfig m_localhostValidator;
+  ndn::ValidatorConfig m_localhopValidator;
+  ndn::nfd::FaceMonitor m_faceMonitor;
+  bool m_isLocalhopEnabled;
+  RemoteRegistrator m_remoteRegistrator;
+
+  RibStatusPublisher m_ribStatusPublisher;
+
+  /** \brief The last transaction ID for FIB update response messages.
+   *         Each group of FIB updates applied to the FIB is assigned an incrementing
+   *         ID that is used to track the number of successfully applied updates.
+   */
+  TransactionId m_lastTransactionId;
+
+  /// table of FIB update transactions => count of pending FIB updates
+  typedef std::map<TransactionId, int> FibTransactionTable;
+
+  /** \brief Table used to track the number of FIB updates that have not yet received
+   *         a response from the FIB.
+   *         The table maps a transaction ID to the number of updates remaining for that
+   *         specific transaction.
+   */
+  FibTransactionTable m_pendingFibTransactions;
+
+  typedef function<void(RibManager*,
+                        const shared_ptr<const Interest>& request,
+                        ControlParameters& parameters)> SignedVerbProcessor;
+
+  typedef std::map<name::Component, SignedVerbProcessor> SignedVerbDispatchTable;
+
+  typedef std::pair<name::Component, SignedVerbProcessor> SignedVerbAndProcessor;
+
+
+  const SignedVerbDispatchTable m_signedVerbDispatch;
+
+  static const Name COMMAND_PREFIX; // /localhost/nrd
+  static const Name REMOTE_COMMAND_PREFIX; // /localhop/nrd
+
+  // number of components in an invalid, but not malformed, unsigned command.
+  // (/localhost/nrd + verb + options) = 4
+  static const size_t COMMAND_UNSIGNED_NCOMPS;
+
+  // number of components in a valid signed Interest.
+  // 8 with signed Interest support.
+  static const size_t COMMAND_SIGNED_NCOMPS;
+
+  static const SignedVerbAndProcessor SIGNED_COMMAND_VERBS[];
+
+  typedef function<void(RibManager*, const Interest&)> UnsignedVerbProcessor;
+  typedef std::map<Name::Component, UnsignedVerbProcessor> UnsignedVerbDispatchTable;
+  typedef std::pair<Name::Component, UnsignedVerbProcessor> UnsignedVerbAndProcessor;
+
+  const UnsignedVerbDispatchTable m_unsignedVerbDispatch;
+  static const UnsignedVerbAndProcessor UNSIGNED_COMMAND_VERBS[];
+
+  static const Name LIST_COMMAND_PREFIX;
+  static const size_t LIST_COMMAND_NCOMPS;
+
+  static const Name FACES_LIST_DATASET_PREFIX;
+
+  static const time::seconds ACTIVE_FACE_FETCH_INTERVAL;
+  EventId m_activeFaceFetchEvent;
+
+  typedef std::set<uint64_t> FaceIdSet;
+  /** \brief contains FaceIds with one or more Routes in the RIB
+  */
+  FaceIdSet m_registeredFaces;
+};
+
+} // namespace rib
+} // namespace nfd
+
+#endif // NFD_RIB_RIB_MANAGER_HPP
diff --git a/NFD/rib/rib-status-publisher.cpp b/NFD/rib/rib-status-publisher.cpp
new file mode 100644
index 0000000..76a164c
--- /dev/null
+++ b/NFD/rib/rib-status-publisher.cpp
@@ -0,0 +1,97 @@
+/* -*- 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/>.
+ */
+
+#include "rib/rib-status-publisher.hpp"
+#include "rib/rib.hpp"
+#include "core/logger.hpp"
+
+#include <ndn-cxx/management/nfd-rib-entry.hpp>
+#include <ndn-cxx/face.hpp>
+
+namespace nfd {
+namespace rib {
+
+NFD_LOG_INIT("RibStatusPublisher");
+
+RibStatusPublisher::RibStatusPublisher(const Rib& rib,
+                                       ndn::Face& face,
+                                       const Name& prefix,
+                                       ndn::KeyChain& keyChain)
+  : SegmentPublisher<ndn::Face>(face, prefix, keyChain)
+  , m_rib(rib)
+{
+}
+
+RibStatusPublisher::~RibStatusPublisher()
+{
+}
+
+size_t
+RibStatusPublisher::generate(ndn::EncodingBuffer& outBuffer)
+{
+  size_t totalLength = 0;
+
+  for (Rib::const_iterator ribIt = m_rib.begin(); ribIt != m_rib.end(); ++ribIt)
+    {
+      RibEntry& entry = *ribIt->second;
+
+      const Name& prefix = entry.getName();
+      size_t ribEntryLength = 0;
+
+      ndn::nfd::RibEntry tlvEntry;
+      const RibEntry::FaceList& faces = entry.getFaces();
+
+      for (RibEntry::FaceList::const_iterator faceIt = faces.begin();
+           faceIt != faces.end(); ++faceIt)
+        {
+          const FaceEntry& face = *faceIt;
+
+          ndn::nfd::Route route;
+          route
+            .setFaceId(face.faceId)
+            .setOrigin(face.origin)
+            .setCost(face.cost)
+            .setFlags(face.flags);
+          if (face.expires < time::steady_clock::TimePoint::max()) {
+            route.setExpirationPeriod(time::duration_cast<time::milliseconds>
+                                      (face.expires - time::steady_clock::now()));
+          }
+          tlvEntry.addRoute(route);
+        }
+
+      tlvEntry.setName(prefix);
+      ribEntryLength += tlvEntry.wireEncode(outBuffer);
+
+      NFD_LOG_DEBUG("generate: rib entry length = " << ribEntryLength);
+
+      totalLength += ribEntryLength;
+    }
+  NFD_LOG_DEBUG("generate: Total length = " << totalLength);
+  return totalLength;
+}
+
+
+} // namespace rib
+} // namespace nfd
diff --git a/NFD/rib/rib-status-publisher.hpp b/NFD/rib/rib-status-publisher.hpp
new file mode 100644
index 0000000..8673983
--- /dev/null
+++ b/NFD/rib/rib-status-publisher.hpp
@@ -0,0 +1,60 @@
+/* -*- 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_RIB_RIB_STATUS_PUBLISHER_HPP
+#define NFD_RIB_RIB_STATUS_PUBLISHER_HPP
+
+#include "core/segment-publisher.hpp"
+#include <ndn-cxx/face.hpp>
+
+namespace nfd {
+namespace rib {
+
+class Rib;
+
+class RibStatusPublisher : public SegmentPublisher<ndn::Face>
+{
+public:
+  RibStatusPublisher(const Rib& rib,
+                     ndn::Face& face,
+                     const Name& prefix,
+                     ndn::KeyChain& keyChain);
+
+  virtual
+  ~RibStatusPublisher();
+
+protected:
+  virtual size_t
+  generate(ndn::EncodingBuffer& outBuffer);
+
+private:
+  const Rib& m_rib;
+};
+
+
+} // namespace rib
+} // namespace nfd
+
+#endif
diff --git a/NFD/rib/rib.cpp b/NFD/rib/rib.cpp
new file mode 100644
index 0000000..02df45e
--- /dev/null
+++ b/NFD/rib/rib.cpp
@@ -0,0 +1,866 @@
+/* -*- 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/>.
+ */
+
+#include "rib.hpp"
+
+#include "core/logger.hpp"
+
+NFD_LOG_INIT("Rib");
+
+namespace nfd {
+namespace rib {
+
+static inline bool
+sortFace(const FaceEntry& entry1, const FaceEntry& entry2)
+{
+  return entry1.faceId < entry2.faceId;
+}
+
+static inline bool
+isChildInheritFlagSet(uint64_t flags)
+{
+  return flags & ndn::nfd::ROUTE_FLAG_CHILD_INHERIT;
+}
+
+static inline bool
+isCaptureFlagSet(uint64_t flags)
+{
+  return flags & ndn::nfd::ROUTE_FLAG_CAPTURE;
+}
+
+static inline bool
+isAnyFlagSet(uint64_t flags)
+{
+  return isChildInheritFlagSet(flags) || isCaptureFlagSet(flags);
+}
+
+static inline bool
+areBothFlagsSet(uint64_t flags)
+{
+  return isChildInheritFlagSet(flags) && isCaptureFlagSet(flags);
+}
+
+Rib::Rib()
+  : m_nItems(0)
+{
+}
+
+Rib::const_iterator
+Rib::find(const Name& prefix) const
+{
+  return m_rib.find(prefix);
+}
+
+FaceEntry*
+Rib::find(const Name& prefix, const FaceEntry& face) const
+{
+  RibTable::const_iterator ribIt = m_rib.find(prefix);
+
+  // Name prefix exists
+  if (ribIt != m_rib.end())
+    {
+      shared_ptr<RibEntry> entry(ribIt->second);
+
+      RibEntry::iterator faceIt = std::find_if(entry->begin(), entry->end(),
+                                                     bind(&compareFaceIdAndOrigin, _1, face));
+
+      if (faceIt != entry->end())
+        {
+          return &((*faceIt));
+        }
+    }
+  return 0;
+}
+
+void
+Rib::insert(const Name& prefix, const FaceEntry& face)
+{
+  RibTable::iterator ribIt = m_rib.find(prefix);
+
+  // Name prefix exists
+  if (ribIt != m_rib.end())
+    {
+      shared_ptr<RibEntry> entry(ribIt->second);
+
+      RibEntry::iterator faceIt = std::find_if(entry->getFaces().begin(),
+                                               entry->getFaces().end(),
+                                               bind(&compareFaceIdAndOrigin, _1, face));
+
+      if (faceIt == entry->end())
+        {
+          // Will the new face change the namespace's capture flag?
+          bool captureWasTurnedOn = (entry->hasCapture() == false && isCaptureFlagSet(face.flags));
+
+          // New face
+          entry->insertFace(face);
+          m_nItems++;
+
+          // Register with face lookup table
+          m_faceMap[face.faceId].push_back(entry);
+
+          createFibUpdatesForNewFaceEntry(*entry, face, captureWasTurnedOn);
+        }
+      else // Entry exists, update fields
+        {
+          // First cancel old scheduled event, if any, then set the EventId to new one
+          if (static_cast<bool>(faceIt->getExpirationEvent()))
+            {
+              NFD_LOG_TRACE("Cancelling expiration event for " << entry->getName() << " "
+                                                               << *faceIt);
+              scheduler::cancel(faceIt->getExpirationEvent());
+            }
+
+          // No checks are required here as the iterator needs to be updated in all cases.
+          faceIt->setExpirationEvent(face.getExpirationEvent());
+
+          // Save flags for update processing
+          uint64_t previousFlags = faceIt->flags;
+
+          // If the entry's cost didn't change and child inherit is not set,
+          // no need to traverse subtree.
+          uint64_t previousCost = faceIt->cost;
+
+          faceIt->flags = face.flags;
+          faceIt->cost = face.cost;
+          faceIt->expires = face.expires;
+
+          createFibUpdatesForUpdatedEntry(*entry, face, previousFlags, previousCost);
+        }
+    }
+  else // New name prefix
+    {
+      shared_ptr<RibEntry> entry(make_shared<RibEntry>(RibEntry()));
+
+      m_rib[prefix] = entry;
+      m_nItems++;
+
+      entry->setName(prefix);
+      entry->insertFace(face);
+
+      // Find prefix's parent
+      shared_ptr<RibEntry> parent = findParent(prefix);
+
+      // Add self to parent's children
+      if (static_cast<bool>(parent))
+        {
+          parent->addChild(entry);
+        }
+
+      RibEntryList children = findDescendants(prefix);
+
+      for (std::list<shared_ptr<RibEntry> >::iterator child = children.begin();
+           child != children.end(); ++child)
+        {
+          if ((*child)->getParent() == parent)
+            {
+              // Remove child from parent and inherit parent's child
+              if (static_cast<bool>(parent))
+                {
+                  parent->removeChild((*child));
+                }
+              entry->addChild((*child));
+            }
+        }
+
+      // Register with face lookup table
+      m_faceMap[face.faceId].push_back(entry);
+
+      createFibUpdatesForNewRibEntry(*entry, face);
+
+      // do something after inserting an entry
+      afterInsertEntry(prefix);
+    }
+}
+
+void
+Rib::erase(const Name& prefix, const FaceEntry& face)
+{
+  RibTable::iterator ribIt = m_rib.find(prefix);
+
+  // Name prefix exists
+  if (ribIt != m_rib.end())
+    {
+      shared_ptr<RibEntry> entry(ribIt->second);
+
+      const bool hadCapture = entry->hasCapture();
+
+      // Need to copy face to do FIB updates with correct cost and flags since nfdc does not
+      // pass flags or cost
+      RibEntry::iterator faceIt = entry->findFace(face);
+
+      if (faceIt != entry->end())
+        {
+          FaceEntry faceToErase = *faceIt;
+          faceToErase.flags = faceIt->flags;
+          faceToErase.cost = faceIt->cost;
+
+          entry->eraseFace(faceIt);
+
+          m_nItems--;
+
+          const bool captureWasTurnedOff = (hadCapture && !entry->hasCapture());
+
+          createFibUpdatesForErasedFaceEntry(*entry, faceToErase, captureWasTurnedOff);
+
+          // If this RibEntry no longer has this faceId, unregister from face lookup table
+          if (!entry->hasFaceId(face.faceId))
+            {
+              m_faceMap[face.faceId].remove(entry);
+            }
+          else
+            {
+              // The RibEntry still has the face ID; need to update FIB
+              // with lowest cost for the same face instead of removing the face from the FIB
+              shared_ptr<FaceEntry> lowCostFace = entry->getFaceWithLowestCostByFaceId(face.faceId);
+
+              BOOST_ASSERT(static_cast<bool>(lowCostFace));
+
+              createFibUpdatesForNewFaceEntry(*entry, *lowCostFace, false);
+            }
+
+          // If a RibEntry's facelist is empty, remove it from the tree
+          if (entry->getFaces().size() == 0)
+            {
+              eraseEntry(ribIt);
+            }
+        }
+    }
+}
+
+void
+Rib::erase(const uint64_t faceId)
+{
+  FaceLookupTable::iterator lookupIt = m_faceMap.find(faceId);
+
+  // No RIB entries have this face
+  if (lookupIt == m_faceMap.end())
+    {
+      return;
+    }
+
+  RibEntryList& ribEntries = lookupIt->second;
+
+  // For each RIB entry that has faceId, remove the face from the entry
+  for (RibEntryList::iterator entryIt = ribEntries.begin(); entryIt != ribEntries.end(); ++entryIt)
+    {
+      shared_ptr<RibEntry> entry = *entryIt;
+
+      const bool hadCapture = entry->hasCapture();
+
+      // Find the faces in the entry
+      for (RibEntry::iterator faceIt = entry->begin(); faceIt != entry->end(); ++faceIt)
+        {
+          if (faceIt->faceId == faceId)
+            {
+              FaceEntry copy = *faceIt;
+
+              faceIt = entry->eraseFace(faceIt);
+              m_nItems--;
+
+              const bool captureWasTurnedOff = (hadCapture && !entry->hasCapture());
+              createFibUpdatesForErasedFaceEntry(*entry, copy, captureWasTurnedOff);
+            }
+        }
+
+        // If a RibEntry's facelist is empty, remove it from the tree
+        if (entry->getFaces().size() == 0)
+          {
+            eraseEntry(m_rib.find(entry->getName()));
+          }
+    }
+
+  // Face no longer exists, remove from face lookup table
+  FaceLookupTable::iterator entryToDelete = m_faceMap.find(faceId);
+
+  if (entryToDelete != m_faceMap.end())
+    {
+      m_faceMap.erase(entryToDelete);
+    }
+}
+
+shared_ptr<RibEntry>
+Rib::findParent(const Name& prefix) const
+{
+  for (int i = prefix.size() - 1; i >= 0; i--)
+    {
+      RibTable::const_iterator it = m_rib.find(prefix.getPrefix(i));
+      if (it != m_rib.end())
+        {
+          return (it->second);
+        }
+    }
+
+  return shared_ptr<RibEntry>();
+}
+
+std::list<shared_ptr<RibEntry> >
+Rib::findDescendants(const Name& prefix) const
+{
+  std::list<shared_ptr<RibEntry> > children;
+
+  RibTable::const_iterator it = m_rib.find(prefix);
+
+  if (it != m_rib.end())
+    {
+      ++it;
+      for (; it != m_rib.end(); ++it)
+        {
+          if (prefix.isPrefixOf(it->first))
+            {
+              children.push_back((it->second));
+            }
+          else
+            {
+              break;
+            }
+        }
+    }
+
+  return children;
+}
+
+Rib::RibTable::iterator
+Rib::eraseEntry(RibTable::iterator it)
+{
+  // Entry does not exist
+  if (it == m_rib.end())
+    {
+      return m_rib.end();
+    }
+
+  shared_ptr<RibEntry> entry(it->second);
+
+  // Remove inherited routes from namespace
+  createFibUpdatesForErasedRibEntry(*entry);
+
+  shared_ptr<RibEntry> parent = entry->getParent();
+
+  // Remove self from parent's children
+  if (static_cast<bool>(parent))
+    {
+      parent->removeChild(entry);
+    }
+
+  std::list<shared_ptr<RibEntry> > children = entry->getChildren();
+
+  for (RibEntryList::iterator child = children.begin(); child != children.end(); ++child)
+    {
+      // Remove children from self
+      entry->removeChild(*child);
+
+      // Update parent's children
+      if (static_cast<bool>(parent))
+        {
+          parent->addChild(*child);
+        }
+    }
+
+  // Must save and advance iterator to return a valid iterator
+  RibTable::iterator nextIt = it;
+  nextIt++;
+
+  m_rib.erase(it);
+
+  // do something after erasing an entry.
+  afterEraseEntry(entry->getName());
+
+  return nextIt;
+}
+
+bool
+compareFibUpdates(const shared_ptr<const FibUpdate> lhs, const shared_ptr<const FibUpdate> rhs)
+{
+  return ((lhs->name == rhs->name) &&
+          (lhs->faceId == rhs->faceId));
+}
+
+void
+Rib::insertFibUpdate(shared_ptr<FibUpdate> update)
+{
+  // If an update with the same name and face already exists,
+  // replace it
+  FibUpdateList::iterator it = std::find_if(m_fibUpdateList.begin(), m_fibUpdateList.end(),
+                                            bind(&compareFibUpdates, _1, update));
+
+  if (it != m_fibUpdateList.end())
+    {
+      // Get rid of the const to alter the action, prevents copying or removal and
+      // insertion
+      FibUpdate& entry = const_cast<FibUpdate&>(*(*it));
+      entry.action = update->action;
+      entry.cost = update->cost;
+    }
+  else
+    {
+      m_fibUpdateList.push_back(update);
+    }
+}
+
+void
+Rib::createFibUpdatesForNewRibEntry(RibEntry& entry, const FaceEntry& face)
+{
+  // Create FIB update for new entry
+  insertFibUpdate(FibUpdate::createAddUpdate(entry.getName(), face.faceId, face.cost));
+
+  // No flags are set
+  if (!isAnyFlagSet(face.flags))
+    {
+      // Add ancestor faces to self
+      addInheritedFacesToEntry(entry, getAncestorFaces(entry));
+    }
+  else if (areBothFlagsSet(face.flags))
+    {
+      // Add face to children
+      FaceSet facesToAdd;
+      facesToAdd.insert(face);
+
+      // Remove faces blocked by capture and add self to children
+      modifyChildrensInheritedFaces(entry, facesToAdd, getAncestorFaces(entry));
+    }
+  else if (isChildInheritFlagSet(face.flags))
+    {
+      FaceSet ancestorFaces = getAncestorFaces(entry);
+
+      // Add ancestor faces to self
+      addInheritedFacesToEntry(entry, ancestorFaces);
+
+      // If there is an ancestor face which is the same as the new face, replace it
+      // with the new face
+      FaceSet::iterator it = ancestorFaces.find(face);
+
+      // There is a face that needs to be overwritten, erase and then replace
+      if (it != ancestorFaces.end())
+        {
+          ancestorFaces.erase(it);
+        }
+
+      // Add new face to ancestor list so it can be added to children
+      ancestorFaces.insert(face);
+
+      // Add ancestor faces to children
+      modifyChildrensInheritedFaces(entry, ancestorFaces, FaceSet());
+    }
+  else if (isCaptureFlagSet(face.flags))
+    {
+      // Remove faces blocked by capture
+      modifyChildrensInheritedFaces(entry, FaceSet(), getAncestorFaces(entry));
+    }
+}
+
+void
+Rib::createFibUpdatesForNewFaceEntry(RibEntry& entry, const FaceEntry& face,
+                                     bool captureWasTurnedOn)
+{
+  // Only update if the new face has a lower cost than a previously installed face
+  shared_ptr<FaceEntry> prevFace = entry.getFaceWithLowestCostAndChildInheritByFaceId(face.faceId);
+
+  FaceSet facesToAdd;
+  if (isChildInheritFlagSet(face.flags))
+    {
+      // Add to children if this new face doesn't override a previous lower cost, or
+      // add to children if this new is lower cost than a previous face.
+      // Less than equal, since entry may find this face
+      if (!static_cast<bool>(prevFace) || face.cost <= prevFace->cost)
+        {
+          // Add self to children
+          facesToAdd.insert(face);
+        }
+    }
+
+  FaceSet facesToRemove;
+  if (captureWasTurnedOn)
+    {
+      // Capture flag on
+      facesToRemove = getAncestorFaces(entry);
+
+      // Remove ancestor faces from self
+      removeInheritedFacesFromEntry(entry, facesToRemove);
+    }
+
+  modifyChildrensInheritedFaces(entry, facesToAdd, facesToRemove);
+
+  // If another face with same faceId and lower cost, don't update.
+  // Must be done last so that add updates replace removal updates
+  // Create FIB update for new entry
+  if (face.cost <= entry.getFaceWithLowestCostByFaceId(face.faceId)->cost)
+    {
+      insertFibUpdate(FibUpdate::createAddUpdate(entry.getName(), face.faceId, face.cost));
+    }
+}
+
+void
+Rib::createFibUpdatesForUpdatedEntry(RibEntry& entry, const FaceEntry& face,
+                                     const uint64_t previousFlags, const uint64_t previousCost)
+{
+  const bool costDidChange = (face.cost != previousCost);
+
+  // Look for an installed face with the lowest cost and child inherit set
+  shared_ptr<FaceEntry> prevFace = entry.getFaceWithLowestCostAndChildInheritByFaceId(face.faceId);
+
+  // No flags changed and cost didn't change, no change in FIB
+  if (face.flags == previousFlags && !costDidChange)
+    {
+      return;
+    }
+
+  // Cost changed so create update for the entry itself
+  if (costDidChange)
+    {
+      // Create update if this face's cost is lower than other faces
+       if (face.cost <= entry.getFaceWithLowestCostByFaceId(face.faceId)->cost)
+        {
+          // Create FIB update for the updated entry
+         insertFibUpdate(FibUpdate::createAddUpdate(entry.getName(), face.faceId, face.cost));
+        }
+      else if (previousCost < entry.getFaceWithLowestCostByFaceId(face.faceId)->cost)
+        {
+          // Create update if this face used to be the lowest face but is no longer
+          // the lowest cost face.
+          insertFibUpdate(FibUpdate::createAddUpdate(entry.getName(), prevFace->faceId,
+                                                                          prevFace->cost));
+        }
+
+      // If another face with same faceId and lower cost and ChildInherit exists,
+      // don't update children.
+      if (!static_cast<bool>(prevFace) || face.cost <= prevFace->cost)
+        {
+          // If no flags changed but child inheritance is set, need to update children
+          // with new cost
+          if ((face.flags == previousFlags) && isChildInheritFlagSet(face.flags))
+          {
+            // Add self to children
+            FaceSet facesToAdd;
+            facesToAdd.insert(face);
+            modifyChildrensInheritedFaces(entry, facesToAdd, FaceSet());
+
+            return;
+          }
+        }
+    }
+
+  // Child inherit was turned on
+  if (!isChildInheritFlagSet(previousFlags) && isChildInheritFlagSet(face.flags))
+    {
+      // If another face with same faceId and lower cost and ChildInherit exists,
+      // don't update children.
+      if (!static_cast<bool>(prevFace) || face.cost <= prevFace->cost)
+        {
+          // Add self to children
+          FaceSet facesToAdd;
+          facesToAdd.insert(face);
+          modifyChildrensInheritedFaces(entry, facesToAdd, FaceSet());
+        }
+
+    } // Child inherit was turned off
+  else if (isChildInheritFlagSet(previousFlags) && !isChildInheritFlagSet(face.flags))
+    {
+      // Remove self from children
+      FaceSet facesToRemove;
+      facesToRemove.insert(face);
+
+      FaceSet facesToAdd;
+      // If another face with same faceId and ChildInherit exists, update children with this face.
+      if (static_cast<bool>(prevFace))
+        {
+          facesToAdd.insert(*prevFace);
+        }
+      else
+        {
+          // Look for an ancestor that was blocked previously
+          const FaceSet ancestorFaces = getAncestorFaces(entry);
+          FaceSet::iterator it = ancestorFaces.find(face);
+
+          // If an ancestor is found, add it to children
+          if (it != ancestorFaces.end())
+            {
+              facesToAdd.insert(*it);
+            }
+        }
+
+      modifyChildrensInheritedFaces(entry, facesToAdd, facesToRemove);
+    }
+
+  // Capture was turned on
+  if (!isCaptureFlagSet(previousFlags) && isCaptureFlagSet(face.flags))
+    {
+      FaceSet ancestorFaces = getAncestorFaces(entry);
+
+      // Remove ancestor faces from self
+      removeInheritedFacesFromEntry(entry, ancestorFaces);
+
+      // Remove ancestor faces from children
+      modifyChildrensInheritedFaces(entry, FaceSet(), ancestorFaces);
+    }  // Capture was turned off
+  else if (isCaptureFlagSet(previousFlags) && !isCaptureFlagSet(face.flags))
+    {
+      FaceSet ancestorFaces = getAncestorFaces(entry);
+
+      // Add ancestor faces to self
+      addInheritedFacesToEntry(entry, ancestorFaces);
+
+      // Add ancestor faces to children
+      modifyChildrensInheritedFaces(entry, ancestorFaces, FaceSet());
+    }
+}
+
+void
+Rib::createFibUpdatesForErasedFaceEntry(RibEntry& entry, const FaceEntry& face,
+                                        const bool captureWasTurnedOff)
+{
+  insertFibUpdate(FibUpdate::createRemoveUpdate(entry.getName(), face.faceId));
+
+  if (areBothFlagsSet(face.flags))
+    {
+      // Remove self from children
+      FaceSet facesToRemove;
+      facesToRemove.insert(face);
+
+      // If capture is turned off for the route, need to add ancestors
+      // to self and children
+      FaceSet facesToAdd;
+      if (captureWasTurnedOff)
+        {
+          // Look for an ancestors that were blocked previously
+          facesToAdd = getAncestorFaces(entry);
+
+          // Add ancestor faces to self
+          addInheritedFacesToEntry(entry, facesToAdd);
+        }
+
+      modifyChildrensInheritedFaces(entry, facesToAdd, facesToRemove);
+    }
+  else if (isChildInheritFlagSet(face.flags))
+    {
+      // If not blocked by capture, add inherited routes to children
+      FaceSet facesToAdd;
+      if (!entry.hasCapture())
+        {
+          facesToAdd = getAncestorFaces(entry);
+        }
+
+      FaceSet facesToRemove;
+      facesToRemove.insert(face);
+
+      // Add ancestor faces to children
+      modifyChildrensInheritedFaces(entry, facesToAdd, facesToRemove);
+    }
+  else if (isCaptureFlagSet(face.flags))
+    {
+      // If capture is turned off for the route, need to add ancestors
+      // to self and children
+      FaceSet facesToAdd;
+      if (captureWasTurnedOff)
+        {
+          // Look for an ancestors that were blocked previously
+          facesToAdd = getAncestorFaces(entry);
+
+          // Add ancestor faces to self
+          addInheritedFacesToEntry(entry, facesToAdd);
+        }
+
+      modifyChildrensInheritedFaces(entry, facesToAdd, FaceSet());
+    }
+
+  // Need to check if the removed face was blocking an inherited route
+  FaceSet ancestorFaces = getAncestorFaces(entry);
+
+  if (!entry.hasCapture())
+  {
+    // If there is an ancestor face which is the same as the erased face, add that face
+    // to the current entry
+    FaceSet::iterator it = ancestorFaces.find(face);
+
+    if (it != ancestorFaces.end())
+      {
+        entry.addInheritedFace(*it);
+        insertFibUpdate(FibUpdate::createAddUpdate(entry.getName(), it->faceId, it->cost));
+      }
+  }
+}
+
+void
+Rib::createFibUpdatesForErasedRibEntry(RibEntry& entry)
+{
+  for (RibEntry::FaceList::iterator it = entry.getInheritedFaces().begin();
+       it != entry.getInheritedFaces().end(); ++it)
+    {
+      insertFibUpdate(FibUpdate::createRemoveUpdate(entry.getName(), it->faceId));
+    }
+}
+
+Rib::FaceSet
+Rib::getAncestorFaces(const RibEntry& entry) const
+{
+  FaceSet ancestorFaces(&sortFace);
+
+  shared_ptr<RibEntry> parent = entry.getParent();
+
+  while (static_cast<bool>(parent))
+    {
+      for (RibEntry::iterator it = parent->getFaces().begin();
+           it != parent->getFaces().end(); ++it)
+        {
+          if (isChildInheritFlagSet(it->flags))
+            {
+              ancestorFaces.insert(*it);
+            }
+        }
+
+      if (parent->hasCapture())
+        {
+          break;
+        }
+
+      parent = parent->getParent();
+    }
+
+    return ancestorFaces;
+}
+
+void
+Rib::addInheritedFacesToEntry(RibEntry& entry, const Rib::FaceSet& facesToAdd)
+{
+  for (FaceSet::const_iterator it = facesToAdd.begin(); it != facesToAdd.end(); ++it)
+    {
+      // Don't add an ancestor faceId if the namespace has an entry for that faceId
+      if (!entry.hasFaceId(it->faceId))
+        {
+          entry.addInheritedFace(*it);
+          insertFibUpdate(FibUpdate::createAddUpdate(entry.getName(), it->faceId, it->cost));
+        }
+    }
+}
+
+void
+Rib::removeInheritedFacesFromEntry(RibEntry& entry, const Rib::FaceSet& facesToRemove)
+{
+  for (FaceSet::const_iterator it = facesToRemove.begin(); it != facesToRemove.end(); ++it)
+    {
+      // Only remove if the face has been inherited
+      if (entry.hasInheritedFace(*it))
+        {
+          entry.removeInheritedFace(*it);
+          insertFibUpdate(FibUpdate::createRemoveUpdate(entry.getName(), it->faceId));
+        }
+    }
+}
+
+void
+Rib::modifyChildrensInheritedFaces(RibEntry& entry, const Rib::FaceSet& facesToAdd,
+                                                    const Rib::FaceSet& facesToRemove)
+{
+  RibEntryList children = entry.getChildren();
+
+  for (RibEntryList::iterator child = children.begin(); child != children.end(); ++child)
+    {
+      traverseSubTree(*(*child), facesToAdd, facesToRemove);
+    }
+}
+
+void
+Rib::traverseSubTree(RibEntry& entry, Rib::FaceSet facesToAdd,
+                                      Rib::FaceSet facesToRemove)
+{
+  // If a route on the namespace has the capture flag set, ignore self and children
+  if (entry.hasCapture())
+    {
+      return;
+    }
+
+  // Remove inherited faces from current namespace
+  for (Rib::FaceSet::const_iterator removeIt = facesToRemove.begin();
+       removeIt != facesToRemove.end(); )
+    {
+      // If a route on the namespace has the same face and child inheritance set, ignore this face
+      if (entry.hasChildInheritOnFaceId(removeIt->faceId))
+        {
+          facesToRemove.erase(removeIt++);
+          continue;
+        }
+
+      // Only remove face if it removes an existing inherited route
+      if (entry.hasInheritedFace(*removeIt))
+        {
+          entry.removeInheritedFace(*removeIt);
+          insertFibUpdate(FibUpdate::createRemoveUpdate(entry.getName(), removeIt->faceId));
+        }
+
+      ++removeIt;
+    }
+
+  // Add inherited faces to current namespace
+  for (Rib::FaceSet::const_iterator addIt = facesToAdd.begin();
+       addIt != facesToAdd.end(); )
+    {
+      // If a route on the namespace has the same face and child inherit set, ignore this face
+      if (entry.hasChildInheritOnFaceId(addIt->faceId))
+      {
+        facesToAdd.erase(addIt++);
+        continue;
+      }
+
+      // Only add face if it does not override an existing route
+      if (!entry.hasFaceId(addIt->faceId))
+        {
+          RibEntry::FaceList::iterator faceIt = entry.findInheritedFace(*addIt);
+
+          // If the entry already has the inherited face, just update the face
+          if (faceIt != entry.getInheritedFaces().end())
+            {
+              faceIt->cost = addIt->cost;
+            }
+          else // Otherwise, this is a newly inherited face
+            {
+              entry.addInheritedFace(*addIt);
+            }
+
+          insertFibUpdate(FibUpdate::createAddUpdate(entry.getName(), addIt->faceId, addIt->cost));
+        }
+
+      ++addIt;
+    }
+
+  Rib::RibEntryList children = entry.getChildren();
+
+  // Apply face operations to current namespace's children
+  for (Rib::RibEntryList::iterator child = children.begin(); child != children.end(); ++child)
+    {
+      traverseSubTree(*(*child), facesToAdd, facesToRemove);
+    }
+}
+
+std::ostream&
+operator<<(std::ostream& os, const Rib& rib)
+{
+  for (Rib::RibTable::const_iterator it = rib.begin(); it != rib.end(); ++it)
+    {
+      os << *(it->second) << "\n";
+    }
+
+  return os;
+}
+
+} // namespace rib
+} // namespace nfd
diff --git a/NFD/rib/rib.hpp b/NFD/rib/rib.hpp
new file mode 100644
index 0000000..2568b1a
--- /dev/null
+++ b/NFD/rib/rib.hpp
@@ -0,0 +1,194 @@
+/* -*- 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_RIB_RIB_HPP
+#define NFD_RIB_RIB_HPP
+
+#include "rib-entry.hpp"
+#include "fib-update.hpp"
+#include "common.hpp"
+#include <ndn-cxx/management/nfd-control-command.hpp>
+#include <ndn-cxx/util/signal.hpp>
+
+namespace nfd {
+namespace rib {
+
+/** \brief represents the RIB
+ */
+class Rib : noncopyable
+{
+public:
+  typedef std::list<shared_ptr<RibEntry> > RibEntryList;
+  typedef std::map<Name, shared_ptr<RibEntry> > RibTable;
+  typedef RibTable::const_iterator const_iterator;
+  typedef std::map<uint64_t, std::list<shared_ptr<RibEntry> > > FaceLookupTable;
+  typedef bool (*FaceComparePredicate)(const FaceEntry&, const FaceEntry&);
+  typedef std::set<FaceEntry, FaceComparePredicate> FaceSet;
+  typedef std::list<shared_ptr<const FibUpdate> > FibUpdateList;
+
+  Rib();
+
+  const_iterator
+  find(const Name& prefix) const;
+
+  FaceEntry*
+  find(const Name& prefix, const FaceEntry& face) const;
+
+  void
+  insert(const Name& prefix, const FaceEntry& face);
+
+  void
+  erase(const Name& prefix, const FaceEntry& face);
+
+  void
+  erase(const uint64_t faceId);
+
+  const_iterator
+  begin() const;
+
+  const_iterator
+  end() const;
+
+  size_t
+  size() const;
+
+  bool
+  empty() const;
+
+  shared_ptr<RibEntry>
+  findParent(const Name& prefix) const;
+
+  /** \brief finds namespaces under the passed prefix
+   *  \return{ a list of entries which are under the passed prefix }
+   */
+  std::list<shared_ptr<RibEntry> >
+  findDescendants(const Name& prefix) const;
+
+  const std::list<shared_ptr<const FibUpdate> >&
+  getFibUpdates() const;
+
+  void
+  clearFibUpdates();
+
+private:
+  RibTable::iterator
+  eraseEntry(RibTable::iterator it);
+
+  void
+  insertFibUpdate(shared_ptr<FibUpdate> update);
+
+  void
+  createFibUpdatesForNewRibEntry(RibEntry& entry, const FaceEntry& face);
+
+  void
+  createFibUpdatesForNewFaceEntry(RibEntry& entry, const FaceEntry& face,
+                                  const bool captureWasTurnedOn);
+
+  void
+  createFibUpdatesForUpdatedEntry(RibEntry& entry, const FaceEntry& face,
+                                  const uint64_t previousFlags, const uint64_t previousCost);
+  void
+  createFibUpdatesForErasedFaceEntry(RibEntry& entry, const FaceEntry& face,
+                                     const bool captureWasTurnedOff);
+
+  void
+  createFibUpdatesForErasedRibEntry(RibEntry& entry);
+
+  FaceSet
+  getAncestorFaces(const RibEntry& entry) const;
+
+  void
+  modifyChildrensInheritedFaces(RibEntry& entry, const Rib::FaceSet& facesToAdd,
+                                                 const Rib::FaceSet& facesToRemove);
+
+  void
+  traverseSubTree(RibEntry& entry, Rib::FaceSet facesToAdd,
+                                   Rib::FaceSet facesToRemove);
+
+  /** \brief Adds passed faces to the entry's inherited faces list
+   */
+  void
+  addInheritedFacesToEntry(RibEntry& entry, const Rib::FaceSet& facesToAdd);
+
+  /** \brief Removes passed faces from the entry's inherited faces list
+   */
+  void
+  removeInheritedFacesFromEntry(RibEntry& entry, const Rib::FaceSet& facesToRemove);
+
+public:
+  ndn::util::signal::Signal<Rib, Name> afterInsertEntry;
+  ndn::util::signal::Signal<Rib, Name> afterEraseEntry;
+
+private:
+  RibTable m_rib;
+  FaceLookupTable m_faceMap;
+  FibUpdateList m_fibUpdateList;
+
+  size_t m_nItems;
+};
+
+inline Rib::const_iterator
+Rib::begin() const
+{
+  return m_rib.begin();
+}
+
+inline Rib::const_iterator
+Rib::end() const
+{
+  return m_rib.end();
+}
+
+inline size_t
+Rib::size() const
+{
+  return m_nItems;
+}
+
+inline bool
+Rib::empty() const
+{
+  return m_rib.empty();
+}
+
+inline const Rib::FibUpdateList&
+Rib::getFibUpdates() const
+{
+  return m_fibUpdateList;
+}
+
+inline void
+Rib::clearFibUpdates()
+{
+  m_fibUpdateList.clear();
+}
+
+std::ostream&
+operator<<(std::ostream& os, const Rib& rib);
+
+} // namespace rib
+} // namespace nfd
+
+#endif // NFD_RIB_RIB_HPP
diff --git a/NFD/tests/boost-test.hpp b/NFD/tests/boost-test.hpp
new file mode 100644
index 0000000..a3b8579
--- /dev/null
+++ b/NFD/tests/boost-test.hpp
@@ -0,0 +1,37 @@
+/* -*- 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_TESTS_BOOST_TEST_HPP
+#define NFD_TESTS_BOOST_TEST_HPP
+
+// suppress warnings from Boost.Test
+#pragma GCC system_header
+#pragma clang system_header
+
+#include <boost/test/unit_test.hpp>
+#include <boost/concept_check.hpp>
+#include <boost/test/output_test_stream.hpp>
+
+#endif // NFD_TESTS_BOOST_TEST_HPP
diff --git a/NFD/tests/core/config-file.cpp b/NFD/tests/core/config-file.cpp
new file mode 100644
index 0000000..29689dd
--- /dev/null
+++ b/NFD/tests/core/config-file.cpp
@@ -0,0 +1,399 @@
+/* -*- 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
+ *
+ * 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 "core/config-file.hpp"
+
+#include "tests/test-common.hpp"
+
+#include <fstream>
+
+namespace nfd {
+namespace tests {
+
+NFD_LOG_INIT("ConfigFileTest");
+
+BOOST_FIXTURE_TEST_SUITE(MgmtConfigFile, BaseFixture)
+
+// a
+// {
+//    akey "avalue"
+// }
+// b
+// {
+//   bkey "bvalue"
+// }
+
+const std::string CONFIG =
+"a\n"
+"{\n"
+"        akey \"avalue\"\n"
+"}\n"
+"b\n"
+"{\n"
+"        bkey \"bvalue\"\n"
+"}\n";
+
+
+// a
+// {
+//    akey "avalue"
+// }
+// b
+//
+//   bkey "bvalue"
+// }
+
+const std::string MALFORMED_CONFIG =
+"a\n"
+"{\n"
+"        akey \"avalue\"\n"
+"}\n"
+"b\n"
+"\n"
+"        bkey \"bvalue\"\n"
+"}\n";
+
+// counts of the respective section counts in config_example.info
+
+const int CONFIG_N_A_SECTIONS = 1;
+const int CONFIG_N_B_SECTIONS = 1;
+
+class DummySubscriber
+{
+public:
+
+  DummySubscriber(ConfigFile& config,
+                  int nASections,
+                  int nBSections,
+                  bool expectDryRun)
+    : m_nASections(nASections),
+      m_nBSections(nBSections),
+      m_nRemainingACallbacks(nASections),
+      m_nRemainingBCallbacks(nBSections),
+      m_expectDryRun(expectDryRun)
+  {
+
+  }
+
+  void
+  onA(const ConfigSection& section, bool isDryRun)
+  {
+    // NFD_LOG_DEBUG("a");
+    BOOST_CHECK_EQUAL(isDryRun, m_expectDryRun);
+    --m_nRemainingACallbacks;
+  }
+
+
+  void
+  onB(const ConfigSection& section, bool isDryRun)
+  {
+    // NFD_LOG_DEBUG("b");
+    BOOST_CHECK_EQUAL(isDryRun, m_expectDryRun);
+    --m_nRemainingBCallbacks;
+  }
+
+  bool
+  allCallbacksFired() const
+  {
+    return m_nRemainingACallbacks == 0 &&
+      m_nRemainingBCallbacks == 0;
+  }
+
+  bool
+  noCallbacksFired() const
+  {
+    return m_nRemainingACallbacks == m_nASections &&
+      m_nRemainingBCallbacks == m_nBSections;
+  }
+
+  virtual
+  ~DummySubscriber()
+  {
+
+  }
+
+private:
+  int m_nASections;
+  int m_nBSections;
+  int m_nRemainingACallbacks;
+  int m_nRemainingBCallbacks;
+  bool m_expectDryRun;
+};
+
+class DummyAllSubscriber : public DummySubscriber
+{
+public:
+  DummyAllSubscriber(ConfigFile& config, bool expectDryRun=false)
+    : DummySubscriber(config,
+                      CONFIG_N_A_SECTIONS,
+                      CONFIG_N_B_SECTIONS,
+                      expectDryRun)
+  {
+    config.addSectionHandler("a", bind(&DummySubscriber::onA, this, _1, _2));
+    config.addSectionHandler("b", bind(&DummySubscriber::onB, this, _1, _2));
+  }
+
+  virtual
+  ~DummyAllSubscriber()
+  {
+
+  }
+};
+
+class DummyOneSubscriber : public DummySubscriber
+{
+public:
+  DummyOneSubscriber(ConfigFile& config,
+                     const std::string& sectionName,
+                     bool expectDryRun=false)
+    : DummySubscriber(config,
+                      (sectionName == "a"),
+                      (sectionName == "b"),
+                      expectDryRun)
+  {
+    if (sectionName == "a")
+      {
+        config.addSectionHandler(sectionName, bind(&DummySubscriber::onA, this, _1, _2));
+      }
+    else if (sectionName == "b")
+      {
+        config.addSectionHandler(sectionName, bind(&DummySubscriber::onB, this, _1, _2));
+      }
+    else
+      {
+        BOOST_FAIL("Test setup error: "
+                   << "Unexpected section name "
+                   <<"\"" << sectionName << "\"");
+      }
+
+  }
+
+  virtual
+  ~DummyOneSubscriber()
+  {
+
+  }
+};
+
+class DummyNoSubscriber : public DummySubscriber
+{
+public:
+  DummyNoSubscriber(ConfigFile& config, bool expectDryRun)
+    : DummySubscriber(config, 0, 0, expectDryRun)
+  {
+
+  }
+
+  virtual
+  ~DummyNoSubscriber()
+  {
+
+  }
+};
+
+BOOST_AUTO_TEST_CASE(OnConfigStream)
+{
+  ConfigFile file;
+  DummyAllSubscriber sub(file);
+  std::ifstream input;
+
+  input.open("tests/core/config_example.info");
+  BOOST_REQUIRE(input.is_open());
+
+  file.parse(input, false, "config_example.info");
+  BOOST_CHECK(sub.allCallbacksFired());
+}
+
+BOOST_AUTO_TEST_CASE(OnConfigStreamEmptyStream)
+{
+  ConfigFile file;
+  DummyAllSubscriber sub(file);
+
+  std::ifstream input;
+
+  BOOST_CHECK_THROW(file.parse(input, false, "unknown"), ConfigFile::Error);
+  BOOST_CHECK(sub.noCallbacksFired());
+}
+
+
+BOOST_AUTO_TEST_CASE(OnConfigString)
+{
+  ConfigFile file;
+  DummyAllSubscriber sub(file);
+
+  file.parse(CONFIG, false, "dummy-config");
+
+  BOOST_CHECK(sub.allCallbacksFired());
+}
+
+BOOST_AUTO_TEST_CASE(OnConfigStringEmpty)
+{
+  ConfigFile file;
+  DummyAllSubscriber sub(file);
+
+  BOOST_CHECK_THROW(file.parse(std::string(), false, "dummy-config"), ConfigFile::Error);
+  BOOST_CHECK(sub.noCallbacksFired());
+}
+
+BOOST_AUTO_TEST_CASE(OnConfigStringMalformed)
+{
+  ConfigFile file;
+  DummyAllSubscriber sub(file);
+
+  BOOST_CHECK_THROW(file.parse(MALFORMED_CONFIG, false, "dummy-config"), ConfigFile::Error);
+  BOOST_CHECK(sub.noCallbacksFired());
+}
+
+BOOST_AUTO_TEST_CASE(OnConfigStringDryRun)
+{
+  ConfigFile file;
+  DummyAllSubscriber sub(file, true);
+
+  file.parse(CONFIG, true, "dummy-config");
+
+  BOOST_CHECK(sub.allCallbacksFired());
+}
+
+BOOST_AUTO_TEST_CASE(OnConfigFilename)
+{
+  ConfigFile file;
+  DummyAllSubscriber sub(file);
+
+  file.parse("tests/core/config_example.info", false);
+
+  BOOST_CHECK(sub.allCallbacksFired());
+}
+
+BOOST_AUTO_TEST_CASE(OnConfigFilenameNoFile)
+{
+  ConfigFile file;
+  DummyAllSubscriber sub(file);
+
+  BOOST_CHECK_THROW(file.parse("i_made_this_up.info", false), ConfigFile::Error);
+
+  BOOST_CHECK(sub.noCallbacksFired());
+}
+
+BOOST_AUTO_TEST_CASE(OnConfigFilenameMalformed)
+{
+  ConfigFile file;
+  DummyAllSubscriber sub(file);
+
+  BOOST_CHECK_THROW(file.parse("tests/core/config_malformed.info", false), ConfigFile::Error);
+
+  BOOST_CHECK(sub.noCallbacksFired());
+}
+
+BOOST_AUTO_TEST_CASE(OnConfigStreamDryRun)
+{
+  ConfigFile file;
+  DummyAllSubscriber sub(file, true);
+  std::ifstream input;
+
+  input.open("tests/core/config_example.info");
+  BOOST_REQUIRE(input.is_open());
+
+  file.parse(input, true, "tests/core/config_example.info");
+
+  BOOST_CHECK(sub.allCallbacksFired());
+
+  input.close();
+}
+
+BOOST_AUTO_TEST_CASE(OnConfigFilenameDryRun)
+{
+  ConfigFile file;
+  DummyAllSubscriber sub(file, true);
+
+  file.parse("tests/core/config_example.info", true);
+  BOOST_CHECK(sub.allCallbacksFired());
+}
+
+BOOST_AUTO_TEST_CASE(OnConfigReplaceSubscriber)
+{
+  ConfigFile file;
+  DummyAllSubscriber sub1(file);
+  DummyAllSubscriber sub2(file);
+
+  file.parse(CONFIG, false, "dummy-config");
+
+  BOOST_CHECK(sub1.noCallbacksFired());
+  BOOST_CHECK(sub2.allCallbacksFired());
+}
+
+class MissingCallbackFixture : public BaseFixture
+{
+public:
+  MissingCallbackFixture()
+    : m_missingFired(false)
+  {
+  }
+
+  void
+  checkMissingHandler(const std::string& filename,
+                      const std::string& sectionName,
+                      const ConfigSection& section,
+                      bool isDryRun)
+  {
+    m_missingFired = true;
+  }
+
+protected:
+  bool m_missingFired;
+};
+
+
+
+BOOST_FIXTURE_TEST_CASE(OnConfigUncoveredSections, MissingCallbackFixture)
+{
+  ConfigFile file;
+
+  BOOST_REQUIRE_THROW(file.parse(CONFIG, false, "dummy-config"), ConfigFile::Error);
+
+  ConfigFile permissiveFile(bind(&MissingCallbackFixture::checkMissingHandler,
+                                 this, _1, _2, _3, _4));
+
+  DummyOneSubscriber subA(permissiveFile, "a");
+
+  BOOST_REQUIRE_NO_THROW(permissiveFile.parse(CONFIG, false, "dummy-config"));
+  BOOST_CHECK(subA.allCallbacksFired());
+  BOOST_CHECK(m_missingFired);
+}
+
+BOOST_AUTO_TEST_CASE(OnConfigCoveredByPartialSubscribers)
+{
+  ConfigFile file;
+  DummyOneSubscriber subA(file, "a");
+  DummyOneSubscriber subB(file, "b");
+
+  file.parse(CONFIG, false, "dummy-config");
+
+  BOOST_CHECK(subA.allCallbacksFired());
+  BOOST_CHECK(subB.allCallbacksFired());
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/NFD/tests/core/config_example.info b/NFD/tests/core/config_example.info
new file mode 100644
index 0000000..d61691f
--- /dev/null
+++ b/NFD/tests/core/config_example.info
@@ -0,0 +1,9 @@
+a
+{
+        akey "avalue"
+}
+
+b
+{
+        bkey "bvalue"
+}
\ No newline at end of file
diff --git a/NFD/tests/core/config_malformed.info b/NFD/tests/core/config_malformed.info
new file mode 100644
index 0000000..d3a1f9e
--- /dev/null
+++ b/NFD/tests/core/config_malformed.info
@@ -0,0 +1,9 @@
+a
+{
+        akey "avalue"
+}
+
+b
+
+        bkey "bvalue"
+}
\ No newline at end of file
diff --git a/NFD/tests/core/logger.cpp b/NFD/tests/core/logger.cpp
new file mode 100644
index 0000000..9ae7fa4
--- /dev/null
+++ b/NFD/tests/core/logger.cpp
@@ -0,0 +1,870 @@
+/* -*- 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
+ *
+ * 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 "core/logger.hpp"
+
+#include "tests/test-common.hpp"
+
+#include <boost/algorithm/string.hpp>
+#include <boost/algorithm/string/classification.hpp>
+
+namespace nfd {
+namespace tests {
+
+BOOST_FIXTURE_TEST_SUITE(CoreLogger, BaseFixture)
+
+class LoggerFixture : protected BaseFixture
+{
+public:
+  LoggerFixture()
+    : m_savedBuf(std::clog.rdbuf())
+    , m_savedLevel(LoggerFactory::getInstance().getDefaultLevel())
+  {
+    std::clog.rdbuf(m_buffer.rdbuf());
+  }
+
+  ~LoggerFixture()
+  {
+    std::clog.rdbuf(m_savedBuf);
+    LoggerFactory::getInstance().setDefaultLevel(m_savedLevel);
+  }
+
+  std::stringstream m_buffer;
+  std::streambuf* m_savedBuf;
+  LogLevel m_savedLevel;
+
+};
+
+BOOST_FIXTURE_TEST_CASE(Basic, LoggerFixture)
+{
+  using namespace ndn::time;
+  using std::string;
+
+  const ndn::time::microseconds::rep ONE_SECOND = 1000000;
+
+  NFD_LOG_INIT("BasicTests");
+  g_logger.setLogLevel(LOG_ALL);
+
+  const string EXPECTED[] =
+    {
+      "TRACE:",   "[BasicTests]", "trace-message-JHGFDSR^1\n",
+      "DEBUG:",   "[BasicTests]", "debug-message-IGg2474fdksd-fo-151617\n",
+      "WARNING:", "[BasicTests]", "warning-message-XXXhdhd111x\n",
+      "INFO:",    "[BasicTests]", "info-message-Jjxjshj13\n",
+      "ERROR:",   "[BasicTests]", "error-message-!#$&^%$#@\n",
+      "FATAL:",   "[BasicTests]", "fatal-message-JJSjaamcng\n",
+    };
+
+  const size_t N_EXPECTED = sizeof(EXPECTED) / sizeof(string);
+
+  microseconds::rep before =
+    duration_cast<microseconds>(system_clock::now().time_since_epoch()).count();
+
+  NFD_LOG_TRACE("trace-message-JHGFDSR^1");
+  NFD_LOG_DEBUG("debug-message-IGg2474fdksd-fo-" << 15 << 16 << 17);
+  NFD_LOG_WARN("warning-message-XXXhdhd11" << 1 <<"x");
+  NFD_LOG_INFO("info-message-Jjxjshj13");
+  NFD_LOG_ERROR("error-message-!#$&^%$#@");
+  NFD_LOG_FATAL("fatal-message-JJSjaamcng");
+
+  microseconds::rep after =
+    duration_cast<microseconds>(system_clock::now().time_since_epoch()).count();
+
+  const string buffer = m_buffer.str();
+
+  std::vector<string> components;
+  boost::split(components, buffer, boost::is_any_of(" ,\n"));
+
+  // std::cout << components.size() << " for " << moduleName  << std::endl;
+  // for (size_t i = 0; i < components.size(); ++i)
+  //   {
+  //     std::cout << "-> " << components[i] << std::endl;
+  //   }
+
+  // expected + number of timestamps (one per log statement) + trailing newline of last statement
+  BOOST_REQUIRE_EQUAL(components.size(), N_EXPECTED + 6 + 1);
+
+  std::vector<std::string>::const_iterator componentIter = components.begin();
+  for (size_t i = 0; i < N_EXPECTED; ++i)
+    {
+      // timestamp LOG_LEVEL: [ModuleName] message\n
+
+      const string& timestamp = *componentIter;
+      // std::cout << "timestamp = " << timestamp << std::endl;
+      ++componentIter;
+
+      size_t timeDelimiterPosition = timestamp.find(".");
+
+      BOOST_REQUIRE_NE(string::npos, timeDelimiterPosition);
+
+      string secondsString = timestamp.substr(0, timeDelimiterPosition);
+      string usecondsString = timestamp.substr(timeDelimiterPosition + 1);
+
+      microseconds::rep extractedTime =
+        ONE_SECOND * boost::lexical_cast<microseconds::rep>(secondsString) +
+        boost::lexical_cast<microseconds::rep>(usecondsString);
+
+      // std::cout << "before=" << before
+      //           << " extracted=" << extractedTime
+      //           << " after=" << after << std::endl;
+
+
+      BOOST_CHECK_LE(before, extractedTime);
+      BOOST_CHECK_LE(extractedTime, after);
+
+      // LOG_LEVEL:
+      BOOST_CHECK_EQUAL(*componentIter, EXPECTED[i]);
+      ++componentIter;
+      ++i;
+
+      // [ModuleName]
+      BOOST_CHECK_EQUAL(*componentIter, EXPECTED[i]);
+      ++componentIter;
+      ++i;
+
+      const string& message = *componentIter;
+
+      // std::cout << "message = " << message << std::endl;
+
+      // add back the newline that we split on
+      BOOST_CHECK_EQUAL(message + "\n", EXPECTED[i]);
+      ++componentIter;
+    }
+
+}
+
+
+BOOST_FIXTURE_TEST_CASE(ConfigureFactory, LoggerFixture)
+{
+  using namespace ndn::time;
+  using std::string;
+
+  const ndn::time::microseconds::rep ONE_SECOND = 1000000;
+
+  NFD_LOG_INIT("ConfigureFactoryTests");
+
+  const string LOG_CONFIG =
+    "log\n"
+    "{\n"
+    "  default_level INFO\n"
+    "}\n";
+
+  LoggerFactory::getInstance().setDefaultLevel(LOG_ALL);
+
+  ConfigFile config;
+  LoggerFactory::getInstance().setConfigFile(config);
+
+  config.parse(LOG_CONFIG, false, "LOG_CONFIG");
+
+  BOOST_REQUIRE_EQUAL(LoggerFactory::getInstance().getDefaultLevel(), LOG_INFO);
+
+  const std::string EXPECTED[] =
+    {
+      "WARNING:", "[ConfigureFactoryTests]", "warning-message-XXXhdhd111x\n",
+      "INFO:",    "[ConfigureFactoryTests]", "info-message-Jjxjshj13\n",
+      "ERROR:",   "[ConfigureFactoryTests]", "error-message-!#$&^%$#@\n",
+      "FATAL:",   "[ConfigureFactoryTests]", "fatal-message-JJSjaamcng\n",
+    };
+
+  const size_t N_EXPECTED = sizeof(EXPECTED) / sizeof(std::string);
+
+  microseconds::rep before =
+    duration_cast<microseconds>(system_clock::now().time_since_epoch()).count();
+
+  NFD_LOG_TRACE("trace-message-JHGFDSR^1");
+  NFD_LOG_DEBUG("debug-message-IGg2474fdksd-fo-" << 15 << 16 << 17);
+  NFD_LOG_WARN("warning-message-XXXhdhd11" << 1 <<"x");
+  NFD_LOG_INFO("info-message-Jjxjshj13");
+  NFD_LOG_ERROR("error-message-!#$&^%$#@");
+  NFD_LOG_FATAL("fatal-message-JJSjaamcng");
+
+  microseconds::rep after =
+    duration_cast<microseconds>(system_clock::now().time_since_epoch()).count();
+
+  const string buffer = m_buffer.str();
+
+  std::vector<string> components;
+  boost::split(components, buffer, boost::is_any_of(" ,\n"));
+
+  // std::cout << components.size() << " for " << moduleName  << std::endl;
+  // for (size_t i = 0; i < components.size(); ++i)
+  //   {
+  //     std::cout << "-> " << components[i] << std::endl;
+  //   }
+
+  // expected + number of timestamps (one per log statement) + trailing newline of last statement
+  BOOST_REQUIRE_EQUAL(components.size(), N_EXPECTED + 4 + 1);
+
+  std::vector<std::string>::const_iterator componentIter = components.begin();
+  for (size_t i = 0; i < N_EXPECTED; ++i)
+    {
+      // timestamp LOG_LEVEL: [ModuleName] message\n
+
+      const string& timestamp = *componentIter;
+      // std::cout << "timestamp = " << timestamp << std::endl;
+      ++componentIter;
+
+      size_t timeDelimiterPosition = timestamp.find(".");
+
+      BOOST_REQUIRE_NE(string::npos, timeDelimiterPosition);
+
+      string secondsString = timestamp.substr(0, timeDelimiterPosition);
+      string usecondsString = timestamp.substr(timeDelimiterPosition + 1);
+
+      microseconds::rep extractedTime =
+        ONE_SECOND * boost::lexical_cast<microseconds::rep>(secondsString) +
+        boost::lexical_cast<microseconds::rep>(usecondsString);
+
+      // std::cout << "before=" << before
+      //           << " extracted=" << extractedTime
+      //           << " after=" << after << std::endl;
+
+      BOOST_CHECK_LE(before, extractedTime);
+      BOOST_CHECK_LE(extractedTime, after);
+
+      // LOG_LEVEL:
+      BOOST_CHECK_EQUAL(*componentIter, EXPECTED[i]);
+      ++componentIter;
+      ++i;
+
+      // [ModuleName]
+      BOOST_CHECK_EQUAL(*componentIter, EXPECTED[i]);
+      ++componentIter;
+      ++i;
+
+      const string& message = *componentIter;
+
+      // std::cout << "message = " << message << std::endl;
+
+      // add back the newline that we split on
+      BOOST_CHECK_EQUAL(message + "\n", EXPECTED[i]);
+      ++componentIter;
+    }
+}
+
+BOOST_FIXTURE_TEST_CASE(TestNumberLevel, LoggerFixture)
+{
+  const std::string LOG_CONFIG =
+    "log\n"
+    "{\n"
+    "  default_level 2\n" // equivalent of WARN
+    "}\n";
+
+  LoggerFactory::getInstance().setDefaultLevel(LOG_ALL);
+
+  ConfigFile config;
+  LoggerFactory::getInstance().setConfigFile(config);
+
+  config.parse(LOG_CONFIG, false, "LOG_CONFIG");
+
+  BOOST_REQUIRE_EQUAL(LoggerFactory::getInstance().getDefaultLevel(), LOG_WARN);
+}
+
+static void
+testModuleBPrint()
+{
+  NFD_LOG_INIT("TestModuleB");
+  NFD_LOG_DEBUG("debug-message-IGg2474fdksd-fo-" << 15 << 16 << 17);
+}
+
+BOOST_FIXTURE_TEST_CASE(LimitModules, LoggerFixture)
+{
+  using namespace ndn::time;
+  using std::string;
+
+  NFD_LOG_INIT("TestModuleA");
+
+  const ndn::time::microseconds::rep ONE_SECOND = 1000000;
+
+  const std::string EXPECTED[] =
+    {
+      "WARNING:", "[TestModuleA]", "warning-message-XXXhdhd111x\n",
+    };
+
+  const size_t N_EXPECTED = sizeof(EXPECTED) / sizeof(std::string);
+
+  const std::string LOG_CONFIG =
+    "log\n"
+    "{\n"
+    "  default_level WARN\n"
+    "}\n";
+
+  ConfigFile config;
+  LoggerFactory::getInstance().setConfigFile(config);
+
+  config.parse(LOG_CONFIG, false, "LOG_CONFIG");
+
+  microseconds::rep before =
+    duration_cast<microseconds>(system_clock::now().time_since_epoch()).count();
+
+  // this should print
+  NFD_LOG_WARN("warning-message-XXXhdhd11" << 1 << "x");
+
+  // this should not because it's level is < WARN
+  testModuleBPrint();
+
+  microseconds::rep after =
+    duration_cast<microseconds>(system_clock::now().time_since_epoch()).count();
+
+  const string buffer = m_buffer.str();
+
+  std::vector<string> components;
+  boost::split(components, buffer, boost::is_any_of(" ,\n"));
+
+  // expected + number of timestamps (one per log statement) + trailing newline of last statement
+  BOOST_REQUIRE_EQUAL(components.size(), N_EXPECTED + 1 + 1);
+
+  std::vector<std::string>::const_iterator componentIter = components.begin();
+  for (size_t i = 0; i < N_EXPECTED; ++i)
+    {
+      // timestamp LOG_LEVEL: [ModuleName] message\n
+
+      const string& timestamp = *componentIter;
+      // std::cout << "timestamp = " << timestamp << std::endl;
+      ++componentIter;
+
+      size_t timeDelimiterPosition = timestamp.find(".");
+
+      BOOST_REQUIRE_NE(string::npos, timeDelimiterPosition);
+
+      string secondsString = timestamp.substr(0, timeDelimiterPosition);
+      string usecondsString = timestamp.substr(timeDelimiterPosition + 1);
+
+      microseconds::rep extractedTime =
+        ONE_SECOND * boost::lexical_cast<microseconds::rep>(secondsString) +
+        boost::lexical_cast<microseconds::rep>(usecondsString);
+
+      // std::cout << "before=" << before
+      //           << " extracted=" << extractedTime
+      //           << " after=" << after << std::endl;
+
+      BOOST_CHECK_LE(before, extractedTime);
+      BOOST_CHECK_LE(extractedTime, after);
+
+      // LOG_LEVEL:
+      BOOST_CHECK_EQUAL(*componentIter, EXPECTED[i]);
+      ++componentIter;
+      ++i;
+
+      // [ModuleName]
+      BOOST_CHECK_EQUAL(*componentIter, EXPECTED[i]);
+      ++componentIter;
+      ++i;
+
+      const string& message = *componentIter;
+
+      // std::cout << "message = " << message << std::endl;
+
+      // add back the newline that we split on
+      BOOST_CHECK_EQUAL(message + "\n", EXPECTED[i]);
+      ++componentIter;
+    }
+}
+
+BOOST_FIXTURE_TEST_CASE(ExplicitlySetModule, LoggerFixture)
+{
+  using namespace ndn::time;
+  using std::string;
+
+  NFD_LOG_INIT("TestModuleA");
+
+  const ndn::time::microseconds::rep ONE_SECOND = 1000000;
+
+  const std::string LOG_CONFIG =
+    "log\n"
+    "{\n"
+    "  default_level WARN\n"
+    "  TestModuleB DEBUG\n"
+    "}\n";
+
+  ConfigFile config;
+  LoggerFactory::getInstance().setConfigFile(config);
+
+  config.parse(LOG_CONFIG, false, "LOG_CONFIG");
+
+  microseconds::rep before =
+    duration_cast<microseconds>(system_clock::now().time_since_epoch()).count();
+
+ // this should print
+  NFD_LOG_WARN("warning-message-XXXhdhd11" << 1 << "x");
+
+  // this too because its level is explicitly set to DEBUG
+  testModuleBPrint();
+
+  microseconds::rep after =
+    duration_cast<microseconds>(system_clock::now().time_since_epoch()).count();
+
+  const std::string EXPECTED[] =
+    {
+      "WARNING:", "[TestModuleA]", "warning-message-XXXhdhd111x\n",
+      "DEBUG:",   "[TestModuleB]", "debug-message-IGg2474fdksd-fo-151617\n",
+    };
+
+  const size_t N_EXPECTED = sizeof(EXPECTED) / sizeof(std::string);
+
+  const string buffer = m_buffer.str();
+
+  std::vector<string> components;
+  boost::split(components, buffer, boost::is_any_of(" ,\n"));
+
+  // for (size_t i = 0; i < components.size(); ++i)
+  //   {
+  //     std::cout << "-> " << components[i] << std::endl;
+  //   }
+
+  // expected + number of timestamps (one per log statement) + trailing newline of last statement
+  BOOST_REQUIRE_EQUAL(components.size(), N_EXPECTED + 2 + 1);
+
+  std::vector<std::string>::const_iterator componentIter = components.begin();
+  for (size_t i = 0; i < N_EXPECTED; ++i)
+    {
+      // timestamp LOG_LEVEL: [ModuleName] message\n
+
+      const string& timestamp = *componentIter;
+      // std::cout << "timestamp = " << timestamp << std::endl;
+      ++componentIter;
+
+      size_t timeDelimiterPosition = timestamp.find(".");
+
+      BOOST_REQUIRE_NE(string::npos, timeDelimiterPosition);
+
+      string secondsString = timestamp.substr(0, timeDelimiterPosition);
+      string usecondsString = timestamp.substr(timeDelimiterPosition + 1);
+
+      microseconds::rep extractedTime =
+        ONE_SECOND * boost::lexical_cast<microseconds::rep>(secondsString) +
+        boost::lexical_cast<microseconds::rep>(usecondsString);
+
+      // std::cout << "before=" << before
+      //           << " extracted=" << extractedTime
+      //           << " after=" << after << std::endl;
+
+      BOOST_CHECK_LE(before, extractedTime);
+      BOOST_CHECK_LE(extractedTime, after);
+
+      // LOG_LEVEL:
+      BOOST_CHECK_EQUAL(*componentIter, EXPECTED[i]);
+      ++componentIter;
+      ++i;
+
+      // [ModuleName]
+      BOOST_CHECK_EQUAL(*componentIter, EXPECTED[i]);
+      ++componentIter;
+      ++i;
+
+      const string& message = *componentIter;
+
+      // std::cout << "message = " << message << std::endl;
+
+      // add back the newline that we split on
+      BOOST_CHECK_EQUAL(message + "\n", EXPECTED[i]);
+      ++componentIter;
+    }
+}
+
+BOOST_FIXTURE_TEST_CASE(UnknownModule, LoggerFixture)
+{
+  using namespace ndn::time;
+  using std::string;
+
+  const ndn::time::microseconds::rep ONE_SECOND = 1000000;
+
+  const std::string LOG_CONFIG =
+    "log\n"
+    "{\n"
+    "  default_level DEBUG\n"
+    "  TestMadeUpModule INFO\n"
+    "}\n";
+
+  ConfigFile config;
+  LoggerFactory::getInstance().setDefaultLevel(LOG_ALL);
+  LoggerFactory::getInstance().setConfigFile(config);
+
+  const std::string EXPECTED = "DEBUG: [LoggerFactory] "
+    "Failed to configure logging level for module \"TestMadeUpModule\" (module not found)\n";
+
+  microseconds::rep before =
+    duration_cast<microseconds>(system_clock::now().time_since_epoch()).count();
+
+  config.parse(LOG_CONFIG, false, "LOG_CONFIG");
+
+  microseconds::rep after =
+    duration_cast<microseconds>(system_clock::now().time_since_epoch()).count();
+
+  const string buffer = m_buffer.str();
+
+  const size_t firstSpace = buffer.find(" ");
+  BOOST_REQUIRE(firstSpace != string::npos);
+
+  const string timestamp = buffer.substr(0, firstSpace);
+  const string message = buffer.substr(firstSpace + 1);
+
+  size_t timeDelimiterPosition = timestamp.find(".");
+
+  BOOST_REQUIRE_NE(string::npos, timeDelimiterPosition);
+
+  string secondsString = timestamp.substr(0, timeDelimiterPosition);
+  string usecondsString = timestamp.substr(timeDelimiterPosition + 1);
+
+  microseconds::rep extractedTime =
+    ONE_SECOND * boost::lexical_cast<microseconds::rep>(secondsString) +
+    boost::lexical_cast<microseconds::rep>(usecondsString);
+
+  // std::cout << "before=" << before
+  //           << " extracted=" << extractedTime
+  //           << " after=" << after << std::endl;
+
+  BOOST_CHECK_LE(before, extractedTime);
+  BOOST_CHECK_LE(extractedTime, after);
+
+  BOOST_CHECK_EQUAL(message, EXPECTED);
+}
+
+static bool
+checkError(const LoggerFactory::Error& error, const std::string& expected)
+{
+  return error.what() == expected;
+}
+
+BOOST_FIXTURE_TEST_CASE(UnknownLevelString, LoggerFixture)
+{
+  const std::string LOG_CONFIG =
+    "log\n"
+    "{\n"
+    "  default_level TestMadeUpLevel\n"
+    "}\n";
+
+  ConfigFile config;
+  LoggerFactory::getInstance().setConfigFile(config);
+
+  BOOST_REQUIRE_EXCEPTION(config.parse(LOG_CONFIG, false, "LOG_CONFIG"),
+                          LoggerFactory::Error,
+                          bind(&checkError,
+                               _1,
+                               "Unsupported logging level \"TestMadeUpLevel\""));
+}
+
+class InClassLogger : public LoggerFixture
+{
+public:
+
+  InClassLogger()
+  {
+    g_logger.setLogLevel(LOG_ALL);
+  }
+
+  void
+  writeLogs()
+  {
+    NFD_LOG_TRACE("trace-message-JHGFDSR^1");
+    NFD_LOG_DEBUG("debug-message-IGg2474fdksd-fo-" << 15 << 16 << 17);
+    NFD_LOG_WARN("warning-message-XXXhdhd11" << 1 <<"x");
+    NFD_LOG_INFO("info-message-Jjxjshj13");
+    NFD_LOG_ERROR("error-message-!#$&^%$#@");
+    NFD_LOG_FATAL("fatal-message-JJSjaamcng");
+  }
+
+private:
+  NFD_LOG_INCLASS_DECLARE();
+};
+
+NFD_LOG_INCLASS_DEFINE(InClassLogger, "InClassLogger");
+
+BOOST_FIXTURE_TEST_CASE(InClass, InClassLogger)
+{
+  using namespace ndn::time;
+  using std::string;
+
+  const ndn::time::microseconds::rep ONE_SECOND = 1000000;
+
+  microseconds::rep before =
+    duration_cast<microseconds>(system_clock::now().time_since_epoch()).count();
+
+  writeLogs();
+
+  microseconds::rep after =
+    duration_cast<microseconds>(system_clock::now().time_since_epoch()).count();
+
+  const string EXPECTED[] =
+    {
+      "TRACE:",   "[InClassLogger]", "trace-message-JHGFDSR^1\n",
+      "DEBUG:",   "[InClassLogger]", "debug-message-IGg2474fdksd-fo-151617\n",
+      "WARNING:", "[InClassLogger]", "warning-message-XXXhdhd111x\n",
+      "INFO:",    "[InClassLogger]", "info-message-Jjxjshj13\n",
+      "ERROR:",   "[InClassLogger]", "error-message-!#$&^%$#@\n",
+      "FATAL:",   "[InClassLogger]", "fatal-message-JJSjaamcng\n",
+    };
+
+  const size_t N_EXPECTED = sizeof(EXPECTED) / sizeof(string);
+
+  const string buffer = m_buffer.str();
+
+  std::vector<string> components;
+  boost::split(components, buffer, boost::is_any_of(" ,\n"));
+
+  // expected + number of timestamps (one per log statement) + trailing newline of last statement
+  BOOST_REQUIRE_EQUAL(components.size(), N_EXPECTED + 6 + 1);
+
+  std::vector<std::string>::const_iterator componentIter = components.begin();
+  for (size_t i = 0; i < N_EXPECTED; ++i)
+    {
+      // timestamp LOG_LEVEL: [ModuleName] message\n
+
+      const string& timestamp = *componentIter;
+      // std::cout << "timestamp = " << timestamp << std::endl;
+      ++componentIter;
+
+      size_t timeDelimiterPosition = timestamp.find(".");
+
+      BOOST_REQUIRE_NE(string::npos, timeDelimiterPosition);
+
+      string secondsString = timestamp.substr(0, timeDelimiterPosition);
+      string usecondsString = timestamp.substr(timeDelimiterPosition + 1);
+
+      microseconds::rep extractedTime =
+        ONE_SECOND * boost::lexical_cast<microseconds::rep>(secondsString) +
+        boost::lexical_cast<microseconds::rep>(usecondsString);
+
+      // std::cout << "before=" << before
+      //           << " extracted=" << extractedTime
+      //           << " after=" << after << std::endl;
+
+      BOOST_CHECK_LE(before, extractedTime);
+      BOOST_CHECK_LE(extractedTime, after);
+
+      // LOG_LEVEL:
+      BOOST_CHECK_EQUAL(*componentIter, EXPECTED[i]);
+      ++componentIter;
+      ++i;
+
+      // [ModuleName]
+      BOOST_CHECK_EQUAL(*componentIter, EXPECTED[i]);
+      ++componentIter;
+      ++i;
+
+      const string& message = *componentIter;
+
+      // std::cout << "message = " << message << std::endl;
+
+      // add back the newline that we split on
+      BOOST_CHECK_EQUAL(message + "\n", EXPECTED[i]);
+      ++componentIter;
+    }
+}
+
+
+template<class T>
+class InClassTemplateLogger : public LoggerFixture
+{
+public:
+  InClassTemplateLogger()
+  {
+    g_logger.setLogLevel(LOG_ALL);
+  }
+
+  void
+  writeLogs()
+  {
+    NFD_LOG_TRACE("trace-message-JHGFDSR^1");
+    NFD_LOG_DEBUG("debug-message-IGg2474fdksd-fo-" << 15 << 16 << 17);
+    NFD_LOG_WARN("warning-message-XXXhdhd11" << 1 <<"x");
+    NFD_LOG_INFO("info-message-Jjxjshj13");
+    NFD_LOG_ERROR("error-message-!#$&^%$#@");
+    NFD_LOG_FATAL("fatal-message-JJSjaamcng");
+  }
+
+private:
+  NFD_LOG_INCLASS_DECLARE();
+};
+
+NFD_LOG_INCLASS_TEMPLATE_DEFINE(InClassTemplateLogger, "GenericInClassTemplateLogger");
+NFD_LOG_INCLASS_TEMPLATE_SPECIALIZATION_DEFINE(InClassTemplateLogger, int, "IntInClassLogger");
+
+BOOST_FIXTURE_TEST_CASE(GenericInTemplatedClass, InClassTemplateLogger<bool>)
+{
+  using namespace ndn::time;
+  using std::string;
+
+  const ndn::time::microseconds::rep ONE_SECOND = 1000000;
+
+  microseconds::rep before =
+    duration_cast<microseconds>(system_clock::now().time_since_epoch()).count();
+
+  writeLogs();
+
+  microseconds::rep after =
+    duration_cast<microseconds>(system_clock::now().time_since_epoch()).count();
+
+  const string EXPECTED[] =
+    {
+      "TRACE:",   "[GenericInClassTemplateLogger]", "trace-message-JHGFDSR^1\n",
+      "DEBUG:",   "[GenericInClassTemplateLogger]", "debug-message-IGg2474fdksd-fo-151617\n",
+      "WARNING:", "[GenericInClassTemplateLogger]", "warning-message-XXXhdhd111x\n",
+      "INFO:",    "[GenericInClassTemplateLogger]", "info-message-Jjxjshj13\n",
+      "ERROR:",   "[GenericInClassTemplateLogger]", "error-message-!#$&^%$#@\n",
+      "FATAL:",   "[GenericInClassTemplateLogger]", "fatal-message-JJSjaamcng\n",
+    };
+
+  const size_t N_EXPECTED = sizeof(EXPECTED) / sizeof(string);
+
+  const string buffer = m_buffer.str();
+
+  std::vector<string> components;
+  boost::split(components, buffer, boost::is_any_of(" ,\n"));
+
+  // expected + number of timestamps (one per log statement) + trailing newline of last statement
+  BOOST_REQUIRE_EQUAL(components.size(), N_EXPECTED + 6 + 1);
+
+  std::vector<std::string>::const_iterator componentIter = components.begin();
+  for (size_t i = 0; i < N_EXPECTED; ++i)
+    {
+      // timestamp LOG_LEVEL: [ModuleName] message\n
+
+      const string& timestamp = *componentIter;
+      // std::cout << "timestamp = " << timestamp << std::endl;
+      ++componentIter;
+
+      size_t timeDelimiterPosition = timestamp.find(".");
+
+      BOOST_REQUIRE_NE(string::npos, timeDelimiterPosition);
+
+      string secondsString = timestamp.substr(0, timeDelimiterPosition);
+      string usecondsString = timestamp.substr(timeDelimiterPosition + 1);
+
+      microseconds::rep extractedTime =
+        ONE_SECOND * boost::lexical_cast<microseconds::rep>(secondsString) +
+        boost::lexical_cast<microseconds::rep>(usecondsString);
+
+      // std::cout << "before=" << before
+      //           << " extracted=" << extractedTime
+      //           << " after=" << after << std::endl;
+
+      BOOST_CHECK_LE(before, extractedTime);
+      BOOST_CHECK_LE(extractedTime, after);
+
+      // LOG_LEVEL:
+      BOOST_CHECK_EQUAL(*componentIter, EXPECTED[i]);
+      ++componentIter;
+      ++i;
+
+      // [ModuleName]
+      BOOST_CHECK_EQUAL(*componentIter, EXPECTED[i]);
+      ++componentIter;
+      ++i;
+
+      const string& message = *componentIter;
+
+      // std::cout << "message = " << message << std::endl;
+
+      // add back the newline that we split on
+      BOOST_CHECK_EQUAL(message + "\n", EXPECTED[i]);
+      ++componentIter;
+    }
+}
+
+
+BOOST_FIXTURE_TEST_CASE(SpecializedInTemplatedClass, InClassTemplateLogger<int>)
+{
+  using namespace ndn::time;
+  using std::string;
+
+  const ndn::time::microseconds::rep ONE_SECOND = 1000000;
+
+  microseconds::rep before =
+    duration_cast<microseconds>(system_clock::now().time_since_epoch()).count();
+
+  writeLogs();
+
+  microseconds::rep after =
+    duration_cast<microseconds>(system_clock::now().time_since_epoch()).count();
+
+  const string EXPECTED[] =
+    {
+      "TRACE:",   "[IntInClassLogger]", "trace-message-JHGFDSR^1\n",
+      "DEBUG:",   "[IntInClassLogger]", "debug-message-IGg2474fdksd-fo-151617\n",
+      "WARNING:", "[IntInClassLogger]", "warning-message-XXXhdhd111x\n",
+      "INFO:",    "[IntInClassLogger]", "info-message-Jjxjshj13\n",
+      "ERROR:",   "[IntInClassLogger]", "error-message-!#$&^%$#@\n",
+      "FATAL:",   "[IntInClassLogger]", "fatal-message-JJSjaamcng\n",
+    };
+
+  const size_t N_EXPECTED = sizeof(EXPECTED) / sizeof(string);
+
+  const string buffer = m_buffer.str();
+
+  std::vector<string> components;
+  boost::split(components, buffer, boost::is_any_of(" ,\n"));
+
+  // expected + number of timestamps (one per log statement) + trailing newline of last statement
+  BOOST_REQUIRE_EQUAL(components.size(), N_EXPECTED + 6 + 1);
+
+  std::vector<std::string>::const_iterator componentIter = components.begin();
+  for (size_t i = 0; i < N_EXPECTED; ++i)
+    {
+      // timestamp LOG_LEVEL: [ModuleName] message\n
+
+      const string& timestamp = *componentIter;
+      // std::cout << "timestamp = " << timestamp << std::endl;
+      ++componentIter;
+
+      size_t timeDelimiterPosition = timestamp.find(".");
+
+      BOOST_REQUIRE_NE(string::npos, timeDelimiterPosition);
+
+      string secondsString = timestamp.substr(0, timeDelimiterPosition);
+      string usecondsString = timestamp.substr(timeDelimiterPosition + 1);
+
+      microseconds::rep extractedTime =
+        ONE_SECOND * boost::lexical_cast<microseconds::rep>(secondsString) +
+        boost::lexical_cast<microseconds::rep>(usecondsString);
+
+      // std::cout << "before=" << before << " extracted=" << extractedTime << " after=" << after << std::endl;
+
+      BOOST_CHECK_LE(before, extractedTime);
+      BOOST_CHECK_LE(extractedTime, after);
+
+      // LOG_LEVEL:
+      BOOST_CHECK_EQUAL(*componentIter, EXPECTED[i]);
+      ++componentIter;
+      ++i;
+
+      // [ModuleName]
+      BOOST_CHECK_EQUAL(*componentIter, EXPECTED[i]);
+      ++componentIter;
+      ++i;
+
+      const string& message = *componentIter;
+
+      // std::cout << "message = " << message << std::endl;
+
+      // add back the newline that we split on
+      BOOST_CHECK_EQUAL(message + "\n", EXPECTED[i]);
+      ++componentIter;
+    }
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/NFD/tests/core/ndebug.cpp b/NFD/tests/core/ndebug.cpp
new file mode 100644
index 0000000..75f69c9
--- /dev/null
+++ b/NFD/tests/core/ndebug.cpp
@@ -0,0 +1,57 @@
+/* -*- 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/>.
+ */
+
+#include "common.hpp"
+
+#include "tests/test-common.hpp"
+
+namespace ndn {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(CoreNdebug)
+
+BOOST_AUTO_TEST_CASE(AssertFalse)
+{
+#ifndef _DEBUG
+  // in release builds, assertion shouldn't execute
+  BOOST_ASSERT(false);
+#endif
+}
+
+BOOST_AUTO_TEST_CASE(SideEffect)
+{
+  int a = 1;
+  BOOST_ASSERT((a = 2) > 0);
+#ifdef _DEBUG
+  BOOST_CHECK_EQUAL(a, 2);
+#else
+  BOOST_CHECK_EQUAL(a, 1);
+#endif
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace ndn
diff --git a/NFD/tests/core/network-interface.cpp b/NFD/tests/core/network-interface.cpp
new file mode 100644
index 0000000..c26d46a
--- /dev/null
+++ b/NFD/tests/core/network-interface.cpp
@@ -0,0 +1,112 @@
+/* -*- 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
+ *
+ * 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 "core/network-interface.hpp"
+#include "tests/test-common.hpp"
+
+namespace nfd {
+namespace tests {
+
+BOOST_FIXTURE_TEST_SUITE(CoreNetworkInterface, BaseFixture)
+
+BOOST_AUTO_TEST_CASE(ListRealNetworkInterfaces)
+{
+  std::vector<NetworkInterfaceInfo> netifs;
+  BOOST_CHECK_NO_THROW(netifs = listNetworkInterfaces());
+
+  for (const auto& netif : netifs) {
+    BOOST_TEST_MESSAGE(netif.index << ": " << netif.name);
+    BOOST_TEST_MESSAGE("\tether " << netif.etherAddress);
+    for (const auto& address : netif.ipv4Addresses)
+      BOOST_TEST_MESSAGE("\tinet  " << address);
+    for (const auto& address : netif.ipv6Addresses)
+      BOOST_TEST_MESSAGE("\tinet6 " << address);
+    BOOST_TEST_MESSAGE("\tloopback  : " << netif.isLoopback());
+    BOOST_TEST_MESSAGE("\tmulticast : " << netif.isMulticastCapable());
+    BOOST_TEST_MESSAGE("\tup        : " << netif.isUp());
+  }
+}
+
+class FakeNetworkInterfaceFixture : public BaseFixture
+{
+public:
+  FakeNetworkInterfaceFixture()
+  {
+    using namespace boost::asio::ip;
+
+    auto fakeInterfaces = make_shared<std::vector<NetworkInterfaceInfo>>();
+
+    fakeInterfaces->push_back(
+      NetworkInterfaceInfo {0, "lo0",
+        ethernet::Address(),
+        {address_v4::from_string("127.0.0.1")},
+        {address_v6::from_string("fe80::1")},
+        address_v4::from_string("127.255.255.255"),
+        IFF_LOOPBACK | IFF_UP});
+    fakeInterfaces->push_back(
+      NetworkInterfaceInfo {1, "eth0",
+        ethernet::Address::fromString("3e:15:c2:8b:65:00"),
+        {address_v4::from_string("192.168.2.1")},
+        {},
+        address_v4::from_string("192.168.2.255"),
+        0});
+    fakeInterfaces->push_back(
+      NetworkInterfaceInfo {2, "eth1",
+        ethernet::Address::fromString("3e:15:c2:8b:65:00"),
+        {address_v4::from_string("198.51.100.1")},
+        {address_v6::from_string("2001:db8::1")},
+        address_v4::from_string("198.51.100.255"),
+        IFF_MULTICAST | IFF_BROADCAST | IFF_UP});
+
+    setDebugNetworkInterfaces(fakeInterfaces);
+  }
+
+  ~FakeNetworkInterfaceFixture()
+  {
+    setDebugNetworkInterfaces(nullptr);
+  }
+};
+
+BOOST_FIXTURE_TEST_CASE(ListFakeNetworkInterfaces, FakeNetworkInterfaceFixture)
+{
+  std::vector<NetworkInterfaceInfo> netifs;
+  BOOST_CHECK_NO_THROW(netifs = listNetworkInterfaces());
+
+  BOOST_REQUIRE_EQUAL(netifs.size(), 3);
+
+  BOOST_CHECK_EQUAL(netifs[0].index, 0);
+  BOOST_CHECK_EQUAL(netifs[1].index, 1);
+  BOOST_CHECK_EQUAL(netifs[2].index, 2);
+
+  BOOST_CHECK_EQUAL(netifs[0].name, "lo0");
+  BOOST_CHECK_EQUAL(netifs[1].name, "eth0");
+  BOOST_CHECK_EQUAL(netifs[2].name, "eth1");
+
+  // no real value of testing other parameters
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/NFD/tests/core/network.cpp b/NFD/tests/core/network.cpp
new file mode 100644
index 0000000..866d0b7
--- /dev/null
+++ b/NFD/tests/core/network.cpp
@@ -0,0 +1,150 @@
+/* -*- 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/>.
+ */
+
+#include "core/network.hpp"
+
+#include "tests/test-common.hpp"
+
+namespace nfd {
+namespace tests {
+
+BOOST_FIXTURE_TEST_SUITE(CoreNetwork, BaseFixture)
+
+using boost::asio::ip::address;
+
+BOOST_AUTO_TEST_CASE(Empty)
+{
+  Network n;
+  BOOST_CHECK_EQUAL(n.doesContain(address::from_string("192.0.2.1")), false);
+  BOOST_CHECK_EQUAL(n.doesContain(address::from_string("2001:db8:3f9:0:3025:ccc5:eeeb:86d3")),
+                    false);
+
+  BOOST_CHECK_EQUAL(n.doesContain(address::from_string("0.0.0.1")), false);
+  BOOST_CHECK_EQUAL(n.doesContain(address::from_string("255.255.255.255")), false);
+
+  BOOST_CHECK_EQUAL(n.doesContain(address::from_string("::")), false);
+  BOOST_CHECK_EQUAL(n.doesContain(address::from_string("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")),
+                    false);
+}
+
+BOOST_AUTO_TEST_CASE(MaxRangeV4)
+{
+  Network n = Network::getMaxRangeV4();
+  BOOST_CHECK_EQUAL(n.doesContain(address::from_string("192.0.2.1")), true);
+  BOOST_CHECK_EQUAL(n.doesContain(address::from_string("2001:db8:3f9:1:3025:ccc5:eeeb:86d3")),
+                    false);
+
+  BOOST_CHECK_EQUAL(n.doesContain(address::from_string("0.0.0.1")), true);
+  BOOST_CHECK_EQUAL(n.doesContain(address::from_string("255.255.255.255")), true);
+
+  BOOST_CHECK_EQUAL(n.doesContain(address::from_string("::")), false);
+  BOOST_CHECK_EQUAL(n.doesContain(address::from_string("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")),
+                    false);
+}
+
+BOOST_AUTO_TEST_CASE(RangeV4)
+{
+  Network n = boost::lexical_cast<Network>("192.0.2.0/24");
+  BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(n), "192.0.2.0 <-> 192.0.2.255");
+
+  BOOST_CHECK_EQUAL(n.doesContain(address::from_string("192.0.2.1")), true);
+  BOOST_CHECK_EQUAL(n.doesContain(address::from_string("192.0.2.254")), true);
+  BOOST_CHECK_EQUAL(n.doesContain(address::from_string("192.0.1.255")), false);
+  BOOST_CHECK_EQUAL(n.doesContain(address::from_string("192.0.3.0")), false);
+
+  BOOST_CHECK_EQUAL(n.doesContain(address::from_string("2001:db8:3f9:1:3025:ccc5:eeeb:86d3")),
+                    false);
+
+  BOOST_CHECK_EQUAL(n.doesContain(address::from_string("0.0.0.1")), false);
+  BOOST_CHECK_EQUAL(n.doesContain(address::from_string("255.255.255.255")), false);
+
+  BOOST_CHECK_EQUAL(n.doesContain(address::from_string("::")), false);
+  BOOST_CHECK_EQUAL(n.doesContain(address::from_string("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")),
+                    false);
+}
+
+BOOST_AUTO_TEST_CASE(MaxRangeV6)
+{
+  Network n = Network::getMaxRangeV6();
+  BOOST_CHECK_EQUAL(n.doesContain(address::from_string("192.0.2.1")), false);
+  BOOST_CHECK_EQUAL(n.doesContain(address::from_string("2001:db8:3f9:1:3025:ccc5:eeeb:86d3")),
+                    true);
+
+  BOOST_CHECK_EQUAL(n.doesContain(address::from_string("0.0.0.1")), false);
+  BOOST_CHECK_EQUAL(n.doesContain(address::from_string("255.255.255.255")), false);
+
+  BOOST_CHECK_EQUAL(n.doesContain(address::from_string("::")), true);
+  BOOST_CHECK_EQUAL(n.doesContain(address::from_string("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")),
+                    true);
+}
+
+BOOST_AUTO_TEST_CASE(RangeV6)
+{
+  Network n = boost::lexical_cast<Network>("2001:db8:3f9:1::/64");
+  BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(n),
+                    "2001:db8:3f9:1:: <-> 2001:db8:3f9:1:ffff:ffff:ffff:ffff");
+
+  BOOST_CHECK_EQUAL(n.doesContain(address::from_string("192.0.2.1")), false);
+  BOOST_CHECK_EQUAL(n.doesContain(address::from_string("2001:db8:3f9:1:3025:ccc5:eeeb:86d3")),
+                    true);
+  BOOST_CHECK_EQUAL(n.doesContain(address::from_string("2001:db8:3f9:1::")),
+                    true);
+  BOOST_CHECK_EQUAL(n.doesContain(address::from_string("2001:db8:3f9:1:ffff:ffff:ffff:ffff")),
+                    true);
+  BOOST_CHECK_EQUAL(n.doesContain(address::from_string("2001:db8:3f9:0:ffff:ffff:ffff:ffff")),
+                    false);
+  BOOST_CHECK_EQUAL(n.doesContain(address::from_string("2001:db8:3f9:2::")),
+                    false);
+
+  BOOST_CHECK_EQUAL(n.doesContain(address::from_string("0.0.0.1")), false);
+  BOOST_CHECK_EQUAL(n.doesContain(address::from_string("255.255.255.255")), false);
+
+  BOOST_CHECK_EQUAL(n.doesContain(address::from_string("::")), false);
+  BOOST_CHECK_EQUAL(n.doesContain(address::from_string("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")),
+                    false);
+}
+
+BOOST_AUTO_TEST_CASE(Comparisons)
+{
+  BOOST_CHECK_EQUAL(boost::lexical_cast<Network>("192.0.2.0/24"),
+                    boost::lexical_cast<Network>("192.0.2.127/24"));
+
+  BOOST_CHECK_EQUAL(boost::lexical_cast<Network>("2001:db8:3f9:0::/64"),
+                    boost::lexical_cast<Network>("2001:db8:3f9:0:ffff::/64"));
+
+  BOOST_CHECK_NE(boost::lexical_cast<Network>("192.0.2.0/24"),
+                 boost::lexical_cast<Network>("192.0.3.127/24"));
+
+  BOOST_CHECK_NE(boost::lexical_cast<Network>("2001:db8:3f9:0::/64"),
+                 boost::lexical_cast<Network>("2001:db8:3f9:1::/64"));
+
+  BOOST_CHECK_NE(boost::lexical_cast<Network>("192.0.2.0/24"),
+                 boost::lexical_cast<Network>("2001:db8:3f9:0::/64"));
+}
+
+BOOST_AUTO_TEST_SUITE_END() // CoreNetwork
+
+} // namespace tests
+} // namespace nfd
diff --git a/NFD/tests/core/notification-stream.cpp b/NFD/tests/core/notification-stream.cpp
new file mode 100644
index 0000000..e231427
--- /dev/null
+++ b/NFD/tests/core/notification-stream.cpp
@@ -0,0 +1,68 @@
+/* -*- 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/>.
+ */
+
+#include "core/notification-stream.hpp"
+#include "simple-notification.hpp"
+
+#include "tests/test-common.hpp"
+#include <ndn-cxx/util/dummy-client-face.hpp>
+
+namespace nfd {
+namespace tests {
+
+BOOST_FIXTURE_TEST_SUITE(CoreNotificationStream, BaseFixture)
+
+BOOST_AUTO_TEST_CASE(Post)
+{
+  shared_ptr<ndn::util::DummyClientFace> face = ndn::util::makeDummyClientFace();
+  ndn::KeyChain keyChain;
+  NotificationStream<ndn::util::DummyClientFace> notificationStream(*face,
+    "/localhost/nfd/NotificationStreamTest", keyChain);
+
+  SimpleNotification event1("msg1");
+  notificationStream.postNotification(event1);
+  face->processEvents();
+  BOOST_REQUIRE_EQUAL(face->sentDatas.size(), 1);
+  BOOST_CHECK_EQUAL(face->sentDatas[0].getName(),
+                    "/localhost/nfd/NotificationStreamTest/%FE%00");
+  SimpleNotification decoded1;
+  BOOST_CHECK_NO_THROW(decoded1.wireDecode(face->sentDatas[0].getContent().blockFromValue()));
+  BOOST_CHECK_EQUAL(decoded1.getMessage(), "msg1");
+
+  SimpleNotification event2("msg2");
+  notificationStream.postNotification(event2);
+  face->processEvents();
+  BOOST_REQUIRE_EQUAL(face->sentDatas.size(), 2);
+  BOOST_CHECK_EQUAL(face->sentDatas[1].getName(),
+                    "/localhost/nfd/NotificationStreamTest/%FE%01");
+  SimpleNotification decoded2;
+  BOOST_CHECK_NO_THROW(decoded2.wireDecode(face->sentDatas[1].getContent().blockFromValue()));
+  BOOST_CHECK_EQUAL(decoded2.getMessage(), "msg2");
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/NFD/tests/core/resolver.cpp b/NFD/tests/core/resolver.cpp
new file mode 100644
index 0000000..18d35b7
--- /dev/null
+++ b/NFD/tests/core/resolver.cpp
@@ -0,0 +1,246 @@
+/* -*- 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
+ *
+ * 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 "core/resolver.hpp"
+#include "core/logger.hpp"
+
+#include "tests/test-common.hpp"
+
+namespace nfd {
+namespace tests {
+
+NFD_LOG_INIT("tests.CoreResolver");
+
+using boost::asio::ip::address_v4;
+using boost::asio::ip::address_v6;
+using boost::asio::ip::tcp;
+using boost::asio::ip::udp;
+
+BOOST_FIXTURE_TEST_SUITE(CoreResolver, BaseFixture)
+
+template<class Protocol>
+class ResolverFixture : protected BaseFixture
+{
+public:
+  ResolverFixture()
+    : m_nFailures(0)
+    , m_nSuccesses(0)
+  {
+  }
+
+  void
+  onSuccess(const typename Protocol::endpoint& resolvedEndpoint,
+            const typename Protocol::endpoint& expectedEndpoint,
+            bool isValid, bool wantCheckAddress = false)
+  {
+    NFD_LOG_DEBUG("Resolved to: " << resolvedEndpoint);
+    ++m_nSuccesses;
+
+    if (!isValid)
+      {
+        BOOST_FAIL("Resolved to " + boost::lexical_cast<std::string>(resolvedEndpoint)
+                   + ", but it should have failed");
+      }
+
+    BOOST_CHECK_EQUAL(resolvedEndpoint.port(), expectedEndpoint.port());
+
+    BOOST_CHECK_EQUAL(resolvedEndpoint.address().is_v4(), expectedEndpoint.address().is_v4());
+
+    // checking address is not deterministic and should be enabled only
+    // if only one IP address will be returned by resolution
+    if (wantCheckAddress)
+      {
+        BOOST_CHECK_EQUAL(resolvedEndpoint.address(), expectedEndpoint.address());
+      }
+  }
+
+  void
+  onFailure(bool isValid)
+  {
+    ++m_nFailures;
+
+    if (!isValid)
+      BOOST_FAIL("Resolution should not have failed");
+
+    BOOST_CHECK_MESSAGE(true, "Resolution failed as expected");
+  }
+
+
+  uint32_t m_nFailures;
+  uint32_t m_nSuccesses;
+};
+
+BOOST_FIXTURE_TEST_CASE(Tcp, ResolverFixture<tcp>)
+{
+  TcpResolver::asyncResolve("www.named-data.net", "6363",
+                            bind(&ResolverFixture<tcp>::onSuccess, this, _1,
+                                 tcp::endpoint(address_v4(), 6363), true, false),
+                            bind(&ResolverFixture<tcp>::onFailure, this, false));
+
+  TcpResolver::asyncResolve("www.named-data.net", "notport",
+                            bind(&ResolverFixture<tcp>::onSuccess, this, _1,
+                                 tcp::endpoint(address_v4(), 0), false, false),
+                            bind(&ResolverFixture<tcp>::onFailure, this, true)); // should fail
+
+
+  TcpResolver::asyncResolve("nothost.nothost.nothost.arpa", "6363",
+                            bind(&ResolverFixture<tcp>::onSuccess, this, _1,
+                                 tcp::endpoint(address_v4(), 6363), false, false),
+                            bind(&ResolverFixture<tcp>::onFailure, this, true)); // should fail
+
+  TcpResolver::asyncResolve("www.google.com", "80",
+                            bind(&ResolverFixture<tcp>::onSuccess, this, _1,
+                                 tcp::endpoint(address_v4(), 80), true, false),
+                            bind(&ResolverFixture<tcp>::onFailure, this, false),
+                            resolver::Ipv4Address()); // request IPv4 address
+
+  TcpResolver::asyncResolve("www.google.com", "80",
+                            bind(&ResolverFixture<tcp>::onSuccess, this, _1,
+                                 tcp::endpoint(address_v6(), 80), true, false),
+                            bind(&ResolverFixture<tcp>::onFailure, this, false),
+                            resolver::Ipv6Address()); // request IPv6 address
+
+  TcpResolver::asyncResolve("ipv6.google.com", "80", // only IPv6 address should be available
+                            bind(&ResolverFixture<tcp>::onSuccess, this, _1,
+                                 tcp::endpoint(address_v6(), 80), true, false),
+                            bind(&ResolverFixture<tcp>::onFailure, this, false));
+
+  TcpResolver::asyncResolve("ipv6.google.com", "80", // only IPv6 address should be available
+                            bind(&ResolverFixture<tcp>::onSuccess, this, _1,
+                                 tcp::endpoint(address_v6(), 80), true, false),
+                            bind(&ResolverFixture<tcp>::onFailure, this, false),
+                            resolver::Ipv6Address());
+
+  TcpResolver::asyncResolve("ipv6.google.com", "80", // only IPv6 address should be available
+                            bind(&ResolverFixture<tcp>::onSuccess, this, _1,
+                                 tcp::endpoint(address_v6(), 80), false, false),
+                            bind(&ResolverFixture<tcp>::onFailure, this, true), // should fail
+                            resolver::Ipv4Address());
+
+  TcpResolver::asyncResolve("192.0.2.1", "80",
+                            bind(&ResolverFixture<tcp>::onSuccess, this, _1,
+                                 tcp::endpoint(address_v4::from_string("192.0.2.1"), 80), true, true),
+                            bind(&ResolverFixture<tcp>::onFailure, this, false));
+
+  TcpResolver::asyncResolve("2001:db8:3f9:0:3025:ccc5:eeeb:86d3", "80",
+                            bind(&ResolverFixture<tcp>::onSuccess, this, _1,
+                                 tcp::endpoint(address_v6::
+                                               from_string("2001:db8:3f9:0:3025:ccc5:eeeb:86d3"),
+                                               80), true, true),
+                            bind(&ResolverFixture<tcp>::onFailure, this, false));
+
+  g_io.run();
+
+  BOOST_CHECK_EQUAL(m_nFailures, 3);
+  BOOST_CHECK_EQUAL(m_nSuccesses, 7);
+}
+
+BOOST_AUTO_TEST_CASE(SyncTcp)
+{
+  tcp::endpoint endpoint;
+  BOOST_CHECK_NO_THROW(endpoint = TcpResolver::syncResolve("www.named-data.net", "6363"));
+  NFD_LOG_DEBUG("Resolved to: " << endpoint);
+  BOOST_CHECK_EQUAL(endpoint.address().is_v4(), true);
+  BOOST_CHECK_EQUAL(endpoint.port(), 6363);
+}
+
+BOOST_FIXTURE_TEST_CASE(Udp, ResolverFixture<udp>)
+{
+  UdpResolver::asyncResolve("www.named-data.net", "6363",
+                            bind(&ResolverFixture<udp>::onSuccess, this, _1,
+                                 udp::endpoint(address_v4(), 6363), true, false),
+                            bind(&ResolverFixture<udp>::onFailure, this, false));
+
+  UdpResolver::asyncResolve("www.named-data.net", "notport",
+                            bind(&ResolverFixture<udp>::onSuccess, this, _1,
+                                 udp::endpoint(address_v4(), 0), false, false),
+                            bind(&ResolverFixture<udp>::onFailure, this, true)); // should fail
+
+
+  UdpResolver::asyncResolve("nothost.nothost.nothost.arpa", "6363",
+                            bind(&ResolverFixture<udp>::onSuccess, this, _1,
+                                 udp::endpoint(address_v4(), 6363), false, false),
+                            bind(&ResolverFixture<udp>::onFailure, this, true)); // should fail
+
+  UdpResolver::asyncResolve("www.google.com", "80",
+                            bind(&ResolverFixture<udp>::onSuccess, this, _1,
+                                 udp::endpoint(address_v4(), 80), true, false),
+                            bind(&ResolverFixture<udp>::onFailure, this, false),
+                            resolver::Ipv4Address()); // request IPv4 address
+
+  UdpResolver::asyncResolve("www.google.com", "80",
+                            bind(&ResolverFixture<udp>::onSuccess, this, _1,
+                                 udp::endpoint(address_v6(), 80), true, false),
+                            bind(&ResolverFixture<udp>::onFailure, this, false),
+                            resolver::Ipv6Address()); // request IPv6 address
+
+  UdpResolver::asyncResolve("ipv6.google.com", "80", // only IPv6 address should be available
+                            bind(&ResolverFixture<udp>::onSuccess, this, _1,
+                                 udp::endpoint(address_v6(), 80), true, false),
+                            bind(&ResolverFixture<udp>::onFailure, this, false));
+
+  UdpResolver::asyncResolve("ipv6.google.com", "80", // only IPv6 address should be available
+                            bind(&ResolverFixture<udp>::onSuccess, this, _1,
+                                 udp::endpoint(address_v6(), 80), true, false),
+                            bind(&ResolverFixture<udp>::onFailure, this, false),
+                            resolver::Ipv6Address());
+
+  UdpResolver::asyncResolve("ipv6.google.com", "80", // only IPv6 address should be available
+                            bind(&ResolverFixture<udp>::onSuccess, this, _1,
+                                 udp::endpoint(address_v6(), 80), false, false),
+                            bind(&ResolverFixture<udp>::onFailure, this, true), // should fail
+                            resolver::Ipv4Address());
+
+  UdpResolver::asyncResolve("192.0.2.1", "80",
+                            bind(&ResolverFixture<udp>::onSuccess, this, _1,
+                                 udp::endpoint(address_v4::from_string("192.0.2.1"), 80), true, true),
+                            bind(&ResolverFixture<udp>::onFailure, this, false));
+
+  UdpResolver::asyncResolve("2001:db8:3f9:0:3025:ccc5:eeeb:86d3", "80",
+                            bind(&ResolverFixture<udp>::onSuccess, this, _1,
+                                 udp::endpoint(address_v6::
+                                               from_string("2001:db8:3f9:0:3025:ccc5:eeeb:86d3"),
+                                               80), true, true),
+                            bind(&ResolverFixture<udp>::onFailure, this, false));
+
+  g_io.run();
+
+  BOOST_CHECK_EQUAL(m_nFailures, 3);
+  BOOST_CHECK_EQUAL(m_nSuccesses, 7);
+}
+
+BOOST_AUTO_TEST_CASE(SyncUdp)
+{
+  udp::endpoint endpoint;
+  BOOST_CHECK_NO_THROW(endpoint = UdpResolver::syncResolve("www.named-data.net", "6363"));
+  NFD_LOG_DEBUG("Resolved to: " << endpoint);
+  BOOST_CHECK_EQUAL(endpoint.address().is_v4(), true);
+  BOOST_CHECK_EQUAL(endpoint.port(), 6363);
+}
+
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/NFD/tests/core/scheduler.cpp b/NFD/tests/core/scheduler.cpp
new file mode 100644
index 0000000..954c31e
--- /dev/null
+++ b/NFD/tests/core/scheduler.cpp
@@ -0,0 +1,160 @@
+/* -*- 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
+ *
+ * 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 "core/scheduler.hpp"
+
+#include "tests/test-common.hpp"
+
+namespace nfd {
+namespace tests {
+
+BOOST_FIXTURE_TEST_SUITE(CoreScheduler, BaseFixture)
+
+class SchedulerFixture : protected BaseFixture
+{
+public:
+  SchedulerFixture()
+    : count1(0)
+    , count2(0)
+    , count3(0)
+  {
+  }
+
+  void
+  event1()
+  {
+    BOOST_CHECK_EQUAL(count3, 1);
+    ++count1;
+  }
+
+  void
+  event2()
+  {
+    ++count2;
+  }
+
+  void
+  event3()
+  {
+    BOOST_CHECK_EQUAL(count1, 0);
+    ++count3;
+  }
+
+  int count1;
+  int count2;
+  int count3;
+};
+
+BOOST_FIXTURE_TEST_CASE(Events, SchedulerFixture)
+{
+  scheduler::schedule(time::milliseconds(500), bind(&SchedulerFixture::event1, this));
+
+  EventId i = scheduler::schedule(time::seconds(1), bind(&SchedulerFixture::event2, this));
+  scheduler::cancel(i);
+
+  scheduler::schedule(time::milliseconds(250), bind(&SchedulerFixture::event3, this));
+
+  i = scheduler::schedule(time::milliseconds(50), bind(&SchedulerFixture::event2, this));
+  scheduler::cancel(i);
+
+  g_io.run();
+
+  BOOST_CHECK_EQUAL(count1, 1);
+  BOOST_CHECK_EQUAL(count2, 0);
+  BOOST_CHECK_EQUAL(count3, 1);
+}
+
+BOOST_AUTO_TEST_CASE(CancelEmptyEvent)
+{
+  EventId i;
+  scheduler::cancel(i);
+}
+
+class SelfCancelFixture : protected BaseFixture
+{
+public:
+  void
+  cancelSelf()
+  {
+    scheduler::cancel(m_selfEventId);
+  }
+
+  EventId m_selfEventId;
+};
+
+BOOST_FIXTURE_TEST_CASE(SelfCancel, SelfCancelFixture)
+{
+  m_selfEventId = scheduler::schedule(time::milliseconds(100),
+                                      bind(&SelfCancelFixture::cancelSelf, this));
+
+  BOOST_REQUIRE_NO_THROW(g_io.run());
+}
+
+BOOST_FIXTURE_TEST_CASE(ScopedEventIdDestruct, UnitTestTimeFixture)
+{
+  int hit = 0;
+  {
+    scheduler::ScopedEventId se = scheduler::schedule(time::milliseconds(10), [&] { ++hit; });
+  } // se goes out of scope
+  this->advanceClocks(time::milliseconds(1), 15);
+  BOOST_CHECK_EQUAL(hit, 0);
+}
+
+BOOST_FIXTURE_TEST_CASE(ScopedEventIdAssign, UnitTestTimeFixture)
+{
+  int hit1 = 0, hit2 = 0;
+  scheduler::ScopedEventId se1 = scheduler::schedule(time::milliseconds(10), [&] { ++hit1; });
+  se1 = scheduler::schedule(time::milliseconds(10), [&] { ++hit2; });
+  this->advanceClocks(time::milliseconds(1), 15);
+  BOOST_CHECK_EQUAL(hit1, 0);
+  BOOST_CHECK_EQUAL(hit2, 1);
+}
+
+BOOST_FIXTURE_TEST_CASE(ScopedEventIdRelease, UnitTestTimeFixture)
+{
+  int hit = 0;
+  {
+    scheduler::ScopedEventId se = scheduler::schedule(time::milliseconds(10), [&] { ++hit; });
+    se.release();
+  } // se goes out of scope
+  this->advanceClocks(time::milliseconds(1), 15);
+  BOOST_CHECK_EQUAL(hit, 1);
+}
+
+BOOST_FIXTURE_TEST_CASE(ScopedEventIdMove, UnitTestTimeFixture)
+{
+  int hit = 0;
+  unique_ptr<scheduler::ScopedEventId> se2;
+  {
+    scheduler::ScopedEventId se = scheduler::schedule(time::milliseconds(10), [&] { ++hit; });
+    se2.reset(new scheduler::ScopedEventId(std::move(se)));
+  } // se goes out of scope
+  this->advanceClocks(time::milliseconds(1), 15);
+  BOOST_CHECK_EQUAL(hit, 1);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/NFD/tests/core/segment-publisher.cpp b/NFD/tests/core/segment-publisher.cpp
new file mode 100644
index 0000000..565c314
--- /dev/null
+++ b/NFD/tests/core/segment-publisher.cpp
@@ -0,0 +1,165 @@
+/* -*- 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/>.
+ */
+
+#include "core/segment-publisher.hpp"
+#include <ndn-cxx/encoding/tlv.hpp>
+
+#include "tests/test-common.hpp"
+#include <ndn-cxx/util/dummy-client-face.hpp>
+
+namespace nfd {
+namespace tests {
+
+NFD_LOG_INIT("SegmentPublisherTest");
+
+template<int64_t N=10000>
+class TestSegmentPublisher : public SegmentPublisher<ndn::util::DummyClientFace>
+{
+public:
+  TestSegmentPublisher(ndn::util::DummyClientFace& face,
+                       const Name& prefix,
+                       ndn::KeyChain& keyChain)
+    : SegmentPublisher(face, prefix, keyChain)
+    , m_totalPayloadLength(0)
+  {
+
+  }
+
+  virtual
+  ~TestSegmentPublisher()
+  {
+  }
+
+  uint16_t
+  getLimit() const
+  {
+    return N;
+  }
+
+  size_t
+  getTotalPayloadLength() const
+  {
+    return m_totalPayloadLength;
+  }
+
+protected:
+
+  virtual size_t
+  generate(ndn::EncodingBuffer& outBuffer)
+  {
+    size_t totalLength = 0;
+    for (int64_t i = 0; i < N; i++)
+      {
+        totalLength += prependNonNegativeIntegerBlock(outBuffer, tlv::Content, i);
+      }
+    m_totalPayloadLength += totalLength;
+    return totalLength;
+  }
+
+protected:
+  size_t m_totalPayloadLength;
+};
+
+template<int64_t N>
+class SegmentPublisherFixture : public BaseFixture
+{
+public:
+  SegmentPublisherFixture()
+    : m_face(ndn::util::makeDummyClientFace())
+    , m_publisher(*m_face, "/localhost/nfd/SegmentPublisherFixture", m_keyChain)
+  {
+  }
+
+  void
+  validate(const Data& data)
+  {
+    Block payload = data.getContent();
+    NFD_LOG_DEBUG("payload size (w/o Content TLV): " << payload.value_size());
+
+    m_buffer.appendByteArray(payload.value(), payload.value_size());
+
+    uint64_t segmentNo = data.getName()[-1].toSegment();
+    if (data.getFinalBlockId() != data.getName()[-1])
+      {
+        return;
+      }
+
+    NFD_LOG_DEBUG("got final block: #" << segmentNo);
+
+    // wrap data in a single Content TLV for easy parsing
+    m_buffer.prependVarNumber(m_buffer.size());
+    m_buffer.prependVarNumber(tlv::Content);
+
+    BOOST_TEST_CHECKPOINT("creating parser");
+    ndn::Block parser(m_buffer.buf(), m_buffer.size());
+    BOOST_TEST_CHECKPOINT("parsing aggregated response");
+    parser.parse();
+
+    BOOST_REQUIRE_EQUAL(parser.elements_size(), m_publisher.getLimit());
+
+    uint64_t expectedNo = m_publisher.getLimit() - 1;
+    for (Block::element_const_iterator i = parser.elements_begin();
+         i != parser.elements_end();
+         ++i)
+      {
+        uint64_t number = readNonNegativeInteger(*i);
+        BOOST_REQUIRE_EQUAL(number, expectedNo);
+        --expectedNo;
+      }
+  }
+
+protected:
+  shared_ptr<ndn::util::DummyClientFace> m_face;
+  TestSegmentPublisher<N> m_publisher;
+  ndn::EncodingBuffer m_buffer;
+  ndn::KeyChain m_keyChain;
+};
+
+using boost::mpl::int_;
+typedef boost::mpl::vector<int_<10000>, int_<100>, int_<10>, int_<0> > DatasetSizes;
+
+BOOST_AUTO_TEST_SUITE(SegmentPublisher)
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(Generate, T, DatasetSizes, SegmentPublisherFixture<T::value>)
+{
+  this->m_publisher.publish();
+  this->m_face->processEvents();
+
+  size_t nSegments = this->m_publisher.getTotalPayloadLength() /
+                     this->m_publisher.getMaxSegmentSize();
+  if (this->m_publisher.getTotalPayloadLength() % this->m_publisher.getMaxSegmentSize() != 0 ||
+      nSegments == 0)
+    ++nSegments;
+
+  BOOST_CHECK_EQUAL(this->m_face->sentDatas.size(), nSegments);
+  for (const Data& data : this->m_face->sentDatas) {
+    this->validate(data);
+  }
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/NFD/tests/core/simple-notification.hpp b/NFD/tests/core/simple-notification.hpp
new file mode 100644
index 0000000..3c8fdef
--- /dev/null
+++ b/NFD/tests/core/simple-notification.hpp
@@ -0,0 +1,94 @@
+/* -*- 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_TESTS_CORE_SIMPLE_NOTIFICATION_HPP
+#define NFD_TESTS_CORE_SIMPLE_NOTIFICATION_HPP
+
+#include "common.hpp"
+
+#include "tests/test-common.hpp"
+
+namespace nfd {
+namespace tests {
+
+class SimpleNotification
+{
+public:
+  SimpleNotification()
+  {
+  }
+
+  SimpleNotification(const std::string& message)
+    : m_message(message)
+  {
+  }
+
+  ~SimpleNotification()
+  {
+  }
+
+  Block
+  wireEncode() const
+  {
+    ndn::EncodingBuffer buffer;
+    prependByteArrayBlock(buffer,
+                          0x8888,
+                          reinterpret_cast<const uint8_t*>(m_message.c_str()),
+                          m_message.size());
+    return buffer.block();
+  }
+
+  void
+  wireDecode(const Block& block)
+  {
+    m_message.assign(reinterpret_cast<const char*>(block.value()),
+                     block.value_size());
+
+    // error for testing
+    if (!m_message.empty() && m_message[0] == '\x07')
+      throw tlv::Error("0x07 error");
+  }
+
+public:
+  const std::string&
+  getMessage() const
+  {
+    return m_message;
+  }
+
+  void
+  setMessage(const std::string& message)
+  {
+    m_message = message;
+  }
+
+private:
+  std::string m_message;
+};
+
+} // namespace tests
+} // namespace nfd
+
+#endif // NFD_TESTS_CORE_SIMPLE_NOTIFICATION_HPP
diff --git a/NFD/tests/core/version.cpp b/NFD/tests/core/version.cpp
new file mode 100644
index 0000000..9fe3851
--- /dev/null
+++ b/NFD/tests/core/version.cpp
@@ -0,0 +1,63 @@
+/* -*- 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/>.
+ */
+
+#include "version.hpp"
+#include "core/logger.hpp"
+
+#include "tests/test-common.hpp"
+
+namespace nfd {
+namespace tests {
+
+BOOST_FIXTURE_TEST_SUITE(CoreVersion, BaseFixture)
+
+NFD_LOG_INIT("VersionTest");
+
+BOOST_AUTO_TEST_CASE(Version)
+{
+  NFD_LOG_INFO("NFD_VERSION " << NFD_VERSION);
+
+  BOOST_CHECK_EQUAL(NFD_VERSION, NFD_VERSION_MAJOR * 1000000 +
+                                 NFD_VERSION_MINOR * 1000 +
+                                 NFD_VERSION_PATCH);
+}
+
+BOOST_AUTO_TEST_CASE(VersionString)
+{
+  NFD_LOG_INFO("NFD_VERSION_STRING " << NFD_VERSION_STRING);
+
+  BOOST_STATIC_ASSERT(NFD_VERSION_MAJOR < 1000);
+  BOOST_STATIC_ASSERT(NFD_VERSION_MINOR < 1000);
+  BOOST_STATIC_ASSERT(NFD_VERSION_PATCH < 1000);
+  char buf[12];
+  snprintf(buf, sizeof(buf), "%d.%d.%d", NFD_VERSION_MAJOR, NFD_VERSION_MINOR, NFD_VERSION_PATCH);
+
+  BOOST_CHECK_EQUAL(std::string(NFD_VERSION_STRING), std::string(buf));
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/NFD/tests/daemon/face/dummy-face.hpp b/NFD/tests/daemon/face/dummy-face.hpp
new file mode 100644
index 0000000..ec0adbf
--- /dev/null
+++ b/NFD/tests/daemon/face/dummy-face.hpp
@@ -0,0 +1,99 @@
+/* -*- 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_TESTS_DAEMON_FACE_DUMMY_FACE_HPP
+#define NFD_TESTS_DAEMON_FACE_DUMMY_FACE_HPP
+
+#include "face/face.hpp"
+#include "face/local-face.hpp"
+
+namespace nfd {
+namespace tests {
+
+/** \class DummyFace
+ *  \brief a Face for unit testing
+ */
+template<class FaceBase>
+class DummyFaceImpl : public FaceBase
+{
+public:
+  DummyFaceImpl()
+    : FaceBase(FaceUri("dummy://"), FaceUri("dummy://"))
+  {
+  }
+
+  DummyFaceImpl(const std::string& remoteUri, const std::string& localUri)
+    : FaceBase(FaceUri(remoteUri), FaceUri(localUri))
+  {
+  }
+
+  virtual void
+  sendInterest(const Interest& interest)
+  {
+    this->onSendInterest(interest);
+    m_sentInterests.push_back(interest);
+    this->afterSend();
+  }
+
+  virtual void
+  sendData(const Data& data)
+  {
+    this->onSendData(data);
+    m_sentDatas.push_back(data);
+    this->afterSend();
+  }
+
+  virtual void
+  close()
+  {
+    this->onFail("close");
+  }
+
+  void
+  receiveInterest(const Interest& interest)
+  {
+    this->onReceiveInterest(interest);
+  }
+
+  void
+  receiveData(const Data& data)
+  {
+    this->onReceiveData(data);
+  }
+
+  EventEmitter<> afterSend;
+
+public:
+  std::vector<Interest> m_sentInterests;
+  std::vector<Data> m_sentDatas;
+};
+
+typedef DummyFaceImpl<Face> DummyFace;
+typedef DummyFaceImpl<LocalFace> DummyLocalFace;
+
+} // namespace tests
+} // namespace nfd
+
+#endif // NFD_TESTS_DAEMON_FACE_DUMMY_FACE_HPP
diff --git a/NFD/tests/daemon/face/dummy-stream-sender.hpp b/NFD/tests/daemon/face/dummy-stream-sender.hpp
new file mode 100644
index 0000000..af0e114
--- /dev/null
+++ b/NFD/tests/daemon/face/dummy-stream-sender.hpp
@@ -0,0 +1,116 @@
+/* -*- 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_TESTS_DAEMON_FACE_DUMMY_STREAM_SENDER_HPP
+#define NFD_TESTS_DAEMON_FACE_DUMMY_STREAM_SENDER_HPP
+
+#include "core/scheduler.hpp"
+#include "core/global-io.hpp"
+
+namespace nfd {
+namespace tests {
+
+
+template<class Protocol, class Dataset>
+class DummyStreamSender : public Dataset
+{
+public:
+  typedef typename Protocol::endpoint Endpoint;
+  typedef typename Protocol::socket Socket;
+
+  class Error : public std::runtime_error
+  {
+  public:
+    explicit
+    Error(const std::string& what)
+      : std::runtime_error(what)
+    {
+    }
+  };
+
+  DummyStreamSender()
+    : socket(getGlobalIoService())
+  {
+  }
+
+  void
+  start(const Endpoint& endpoint)
+  {
+    socket.async_connect(endpoint,
+                         bind(&DummyStreamSender::onSuccessfullConnect, this, _1));
+  }
+
+  void
+  onSuccessfullConnect(const boost::system::error_code& error)
+  {
+    if (error)
+      {
+        throw Error("Connection aborted");
+      }
+
+    // This value may need to be adjusted if some dataset exceeds 100k
+    socket.set_option(boost::asio::socket_base::send_buffer_size(100000));
+
+    for (typename Dataset::Container::iterator i = this->data.begin();
+         i != this->data.end(); ++i)
+      {
+        socket.async_send(boost::asio::buffer(*i),
+                          bind(&DummyStreamSender::onSendFinished, this, _1, false));
+      }
+
+    socket.async_send(boost::asio::buffer(static_cast<const uint8_t*>(0), 0),
+                      bind(&DummyStreamSender::onSendFinished, this, _1, true));
+  }
+
+  void
+  onSendFinished(const boost::system::error_code& error, bool isFinal)
+  {
+    if (error) {
+      throw Error("Connection aborted");
+    }
+
+    if (isFinal) {
+      scheduler::schedule(ndn::time::seconds(1),
+                          bind(&DummyStreamSender::stop, this));
+    }
+  }
+
+  void
+  stop()
+  {
+    // Terminate test
+    boost::system::error_code error;
+    socket.shutdown(Socket::shutdown_both, error);
+    socket.close(error);
+  }
+
+public:
+  Socket socket;
+};
+
+} // namespace tests
+} // namespace nfd
+
+#endif // NFD_TESTS_DAEMON_FACE_DUMMY_STREAM_SENDER_HPP
diff --git a/NFD/tests/daemon/face/ethernet.cpp b/NFD/tests/daemon/face/ethernet.cpp
new file mode 100644
index 0000000..ab0181c
--- /dev/null
+++ b/NFD/tests/daemon/face/ethernet.cpp
@@ -0,0 +1,263 @@
+/* -*- 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/>.
+ */
+
+#include "face/ethernet-face.hpp"
+#include "face/ethernet-factory.hpp"
+
+#include "core/network-interface.hpp"
+#include "tests/test-common.hpp"
+
+#include <pcap/pcap.h>
+
+namespace nfd {
+namespace tests {
+
+class InterfacesFixture : protected BaseFixture
+{
+protected:
+  InterfacesFixture()
+  {
+    EthernetFactory factory;
+
+    for (const auto& netif : listNetworkInterfaces()) {
+      if (!netif.isLoopback() && netif.isUp()) {
+        try {
+          factory.createMulticastFace(netif, ethernet::getBroadcastAddress());
+        }
+        catch (Face::Error&) {
+          continue;
+        }
+
+        m_interfaces.push_back(netif);
+      }
+    }
+  }
+
+protected:
+  std::vector<NetworkInterfaceInfo> m_interfaces;
+};
+
+BOOST_FIXTURE_TEST_SUITE(FaceEthernet, InterfacesFixture)
+
+BOOST_AUTO_TEST_CASE(GetChannels)
+{
+  EthernetFactory factory;
+
+  auto channels = factory.getChannels();
+  BOOST_CHECK_EQUAL(channels.empty(), true);
+}
+
+BOOST_AUTO_TEST_CASE(MulticastFacesMap)
+{
+  if (m_interfaces.empty()) {
+    BOOST_WARN_MESSAGE(false, "No interfaces available for pcap, "
+                              "cannot perform MulticastFacesMap test");
+    return;
+  }
+
+  EthernetFactory factory;
+  shared_ptr<EthernetFace> face1 = factory.createMulticastFace(m_interfaces.front(),
+                                                               ethernet::getBroadcastAddress());
+  shared_ptr<EthernetFace> face1bis = factory.createMulticastFace(m_interfaces.front(),
+                                                                  ethernet::getBroadcastAddress());
+  BOOST_CHECK_EQUAL(face1, face1bis);
+
+  if (m_interfaces.size() > 1) {
+    shared_ptr<EthernetFace> face2 = factory.createMulticastFace(m_interfaces.back(),
+                                                                 ethernet::getBroadcastAddress());
+    BOOST_CHECK_NE(face1, face2);
+  }
+  else {
+    BOOST_WARN_MESSAGE(false, "Only one interface available for pcap, "
+                              "cannot test second EthernetFace creation");
+  }
+
+  shared_ptr<EthernetFace> face3 = factory.createMulticastFace(m_interfaces.front(),
+                                     ethernet::getDefaultMulticastAddress());
+  BOOST_CHECK_NE(face1, face3);
+}
+
+BOOST_AUTO_TEST_CASE(SendPacket)
+{
+  if (m_interfaces.empty()) {
+    BOOST_WARN_MESSAGE(false, "No interfaces available for pcap, "
+                              "cannot perform SendPacket test");
+    return;
+  }
+
+  EthernetFactory factory;
+  shared_ptr<EthernetFace> face = factory.createMulticastFace(m_interfaces.front(),
+                                    ethernet::getDefaultMulticastAddress());
+
+  BOOST_REQUIRE(static_cast<bool>(face));
+  BOOST_CHECK_EQUAL(face->isLocal(), false);
+  BOOST_CHECK_EQUAL(face->isOnDemand(), false);
+  BOOST_CHECK_EQUAL(face->getRemoteUri().toString(),
+                    "ether://[" + ethernet::getDefaultMulticastAddress().toString() + "]");
+  BOOST_CHECK_EQUAL(face->getLocalUri().toString(),
+                    "dev://" + m_interfaces.front().name);
+  BOOST_CHECK_EQUAL(face->getCounters().getNInBytes(), 0);
+  BOOST_CHECK_EQUAL(face->getCounters().getNOutBytes(), 0);
+
+  face->onFail += [] (const std::string& reason) { BOOST_FAIL(reason); };
+
+  shared_ptr<Interest> interest1 = makeInterest("ndn:/TpnzGvW9R");
+  shared_ptr<Data>     data1     = makeData("ndn:/KfczhUqVix");
+  shared_ptr<Interest> interest2 = makeInterest("ndn:/QWiIMfj5sL");
+  shared_ptr<Data>     data2     = makeData("ndn:/XNBV796f");
+
+  face->sendInterest(*interest1);
+  face->sendData    (*data1    );
+  face->sendInterest(*interest2);
+  face->sendData    (*data2    );
+
+  BOOST_CHECK_EQUAL(face->getCounters().getNOutBytes(),
+                    14 * 4 + // 4 NDNLP headers
+                    interest1->wireEncode().size() +
+                    data1->wireEncode().size() +
+                    interest2->wireEncode().size() +
+                    data2->wireEncode().size());
+
+//  m_ioRemaining = 4;
+//  m_ioService.run();
+//  m_ioService.reset();
+
+//  BOOST_REQUIRE_EQUAL(m_face1_receivedInterests.size(), 1);
+//  BOOST_REQUIRE_EQUAL(m_face1_receivedDatas    .size(), 1);
+//  BOOST_REQUIRE_EQUAL(m_face2_receivedInterests.size(), 1);
+//  BOOST_REQUIRE_EQUAL(m_face2_receivedDatas    .size(), 1);
+
+//  BOOST_CHECK_EQUAL(m_face1_receivedInterests[0].getName(), interest2.getName());
+//  BOOST_CHECK_EQUAL(m_face1_receivedDatas    [0].getName(), data2.getName());
+//  BOOST_CHECK_EQUAL(m_face2_receivedInterests[0].getName(), interest1.getName());
+//  BOOST_CHECK_EQUAL(m_face2_receivedDatas    [0].getName(), data1.getName());
+}
+
+BOOST_AUTO_TEST_CASE(ProcessIncomingPacket)
+{
+  if (m_interfaces.empty()) {
+    BOOST_WARN_MESSAGE(false, "No interfaces available for pcap, "
+                              "cannot perform ProcessIncomingPacket test");
+    return;
+  }
+
+  EthernetFactory factory;
+  shared_ptr<EthernetFace> face = factory.createMulticastFace(m_interfaces.front(),
+                                    ethernet::getDefaultMulticastAddress());
+  BOOST_REQUIRE(static_cast<bool>(face));
+
+  std::vector<Interest> recInterests;
+  std::vector<Data>     recDatas;
+
+  face->onFail            += [] (const std::string& reason) { BOOST_FAIL(reason); };
+  face->onReceiveInterest += [&recInterests] (const Interest& i) { recInterests.push_back(i); };
+  face->onReceiveData     += [&recDatas]     (const Data& d)     { recDatas.push_back(d);     };
+
+  // check that packet data is not accessed if pcap didn't capture anything (caplen == 0)
+  static const pcap_pkthdr header1{};
+  face->processIncomingPacket(&header1, nullptr);
+  BOOST_CHECK_EQUAL(face->getCounters().getNInBytes(), 0);
+  BOOST_CHECK_EQUAL(recInterests.size(), 0);
+  BOOST_CHECK_EQUAL(recDatas.size(), 0);
+
+  // runt frame (too short)
+  static const pcap_pkthdr header2{{}, ethernet::HDR_LEN + 6};
+  static const uint8_t packet2[ethernet::HDR_LEN + 6]{};
+  face->processIncomingPacket(&header2, packet2);
+  BOOST_CHECK_EQUAL(face->getCounters().getNInBytes(), 0);
+  BOOST_CHECK_EQUAL(recInterests.size(), 0);
+  BOOST_CHECK_EQUAL(recDatas.size(), 0);
+
+  // valid frame, but TLV block has invalid length
+  static const pcap_pkthdr header3{{}, ethernet::HDR_LEN + ethernet::MIN_DATA_LEN};
+  static const uint8_t packet3[ethernet::HDR_LEN + ethernet::MIN_DATA_LEN]{
+    0x01, 0x00, 0x5e, 0x00, 0x17, 0xaa, // destination address
+    0x02, 0x00, 0x00, 0x00, 0x00, 0x02, // source address
+    0x86, 0x24,       // NDN ethertype
+    tlv::NdnlpData,   // TLV type
+    0xfd, 0xff, 0xff  // TLV length (invalid because greater than buffer size)
+  };
+  face->processIncomingPacket(&header3, packet3);
+  BOOST_CHECK_EQUAL(face->getCounters().getNInBytes(), 0);
+  BOOST_CHECK_EQUAL(recInterests.size(), 0);
+  BOOST_CHECK_EQUAL(recDatas.size(), 0);
+
+  // valid frame, but TLV block has invalid type
+  static const pcap_pkthdr header4{{}, ethernet::HDR_LEN + ethernet::MIN_DATA_LEN};
+  static const uint8_t packet4[ethernet::HDR_LEN + ethernet::MIN_DATA_LEN]{
+    0x01, 0x00, 0x5e, 0x00, 0x17, 0xaa, // destination address
+    0x02, 0x00, 0x00, 0x00, 0x00, 0x02, // source address
+    0x86, 0x24,       // NDN ethertype
+    0x00,             // TLV type (invalid)
+    0x00              // TLV length
+  };
+  face->processIncomingPacket(&header4, packet4);
+  BOOST_CHECK_EQUAL(face->getCounters().getNInBytes(), 2);
+  BOOST_CHECK_EQUAL(recInterests.size(), 0);
+  BOOST_CHECK_EQUAL(recDatas.size(), 0);
+
+  // valid frame and valid NDNLP header, but invalid payload
+  static const pcap_pkthdr header5{{}, ethernet::HDR_LEN + ethernet::MIN_DATA_LEN};
+  static const uint8_t packet5[ethernet::HDR_LEN + ethernet::MIN_DATA_LEN]{
+    0x01, 0x00, 0x5e, 0x00, 0x17, 0xaa, // destination address
+    0x02, 0x00, 0x00, 0x00, 0x00, 0x02, // source address
+    0x86, 0x24,                   // NDN ethertype
+    tlv::NdnlpData,     0x0e,     // NDNLP header
+    tlv::NdnlpSequence, 0x08,
+    0, 0, 0, 0, 0, 0, 0, 0,
+    tlv::NdnlpPayload,  0x02,
+    0x00,             // NDN TLV type (invalid)
+    0x00              // NDN TLV length
+  };
+  face->processIncomingPacket(&header5, packet5);
+  BOOST_CHECK_EQUAL(face->getCounters().getNInBytes(), 18);
+  BOOST_CHECK_EQUAL(recInterests.size(), 0);
+  BOOST_CHECK_EQUAL(recDatas.size(), 0);
+
+  // valid frame, valid NDNLP header, and valid NDN (interest) packet
+  static const pcap_pkthdr header6{{}, ethernet::HDR_LEN + ethernet::MIN_DATA_LEN};
+  static const uint8_t packet6[ethernet::HDR_LEN + ethernet::MIN_DATA_LEN]{
+    0x01, 0x00, 0x5e, 0x00, 0x17, 0xaa, // destination address
+    0x02, 0x00, 0x00, 0x00, 0x00, 0x02, // source address
+    0x86, 0x24,                         // NDN ethertype
+    tlv::NdnlpData, 0x24,               // NDNLP TLV type and length
+    0x51, 0x08, 0x00, 0x00, 0x00, 0x00, // rest of NDNLP header
+    0x00, 0x00, 0x00, 0x00, 0x54, 0x18,
+    tlv::Interest, 0x16,                // NDN TLV type and length
+    0x07, 0x0e, 0x08, 0x07, 0x65, 0x78, // payload
+    0x61, 0x6d, 0x70, 0x6c, 0x65, 0x08,
+    0x03, 0x66, 0x6f, 0x6f, 0x0a, 0x04,
+    0x03, 0xef, 0xe9, 0x7c
+  };
+  face->processIncomingPacket(&header6, packet6);
+  BOOST_CHECK_EQUAL(face->getCounters().getNInBytes(), 56);
+  BOOST_CHECK_EQUAL(recInterests.size(), 1);
+  BOOST_CHECK_EQUAL(recDatas.size(), 0);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/NFD/tests/daemon/face/face-counters.cpp b/NFD/tests/daemon/face/face-counters.cpp
new file mode 100644
index 0000000..3be0d88
--- /dev/null
+++ b/NFD/tests/daemon/face/face-counters.cpp
@@ -0,0 +1,79 @@
+/* -*- 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/>.
+ **/
+
+#include "face/face-counters.hpp"
+#include "dummy-face.hpp"
+
+#include "tests/test-common.hpp"
+
+namespace nfd {
+namespace tests {
+
+BOOST_FIXTURE_TEST_SUITE(FaceFaceCounters, BaseFixture)
+
+BOOST_AUTO_TEST_CASE(PacketCnt)
+{
+  PacketCounter counter;
+
+  uint64_t observation = counter;//implicit convertible
+  BOOST_CHECK_EQUAL(observation, 0);
+
+  ++counter;
+  BOOST_CHECK_EQUAL(static_cast<int>(counter), 1);
+  ++counter;
+  ++counter;
+  BOOST_CHECK_EQUAL(static_cast<int>(counter), 3);
+}
+
+BOOST_AUTO_TEST_CASE(ByteCnt)
+{
+  ByteCounter counter;
+
+  uint64_t observation = counter;//implicit convertible
+  BOOST_CHECK_EQUAL(observation, 0);
+
+  counter += 20;
+  BOOST_CHECK_EQUAL(static_cast<int>(counter), 20);
+  counter += 80;
+  counter += 90;
+  BOOST_CHECK_EQUAL(static_cast<int>(counter), 190);
+}
+
+BOOST_AUTO_TEST_CASE(Counters)
+{
+  DummyFace face;
+  const FaceCounters& counters = face.getCounters();
+  BOOST_CHECK_EQUAL(counters.getNInInterests() , 0);
+  BOOST_CHECK_EQUAL(counters.getNInDatas()     , 0);
+  BOOST_CHECK_EQUAL(counters.getNOutInterests(), 0);
+  BOOST_CHECK_EQUAL(counters.getNOutDatas()    , 0);
+  BOOST_CHECK_EQUAL(counters.getNInBytes()     , 0);
+  BOOST_CHECK_EQUAL(counters.getNOutBytes()    , 0);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/NFD/tests/daemon/face/face.cpp b/NFD/tests/daemon/face/face.cpp
new file mode 100644
index 0000000..143dfe6
--- /dev/null
+++ b/NFD/tests/daemon/face/face.cpp
@@ -0,0 +1,102 @@
+/* -*- 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/>.
+ **/
+
+#include "face/face.hpp"
+#include "face/local-face.hpp"
+#include "dummy-face.hpp"
+
+#include "tests/test-common.hpp"
+
+namespace nfd {
+namespace tests {
+
+BOOST_FIXTURE_TEST_SUITE(FaceFace, BaseFixture)
+
+BOOST_AUTO_TEST_CASE(Description)
+{
+  DummyFace face;
+  face.setDescription("3pFsKrvWr");
+  BOOST_CHECK_EQUAL(face.getDescription(), "3pFsKrvWr");
+}
+
+BOOST_AUTO_TEST_CASE(LocalControlHeaderEnabled)
+{
+  DummyLocalFace face;
+
+  BOOST_CHECK_EQUAL(face.isLocalControlHeaderEnabled(), false);
+
+  face.setLocalControlHeaderFeature(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID, true);
+  BOOST_CHECK_EQUAL(face.isLocalControlHeaderEnabled(), true);
+  BOOST_CHECK_EQUAL(face.isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID), true);
+  BOOST_CHECK_EQUAL(face.isLocalControlHeaderEnabled(
+                         LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID), false);
+
+  face.setLocalControlHeaderFeature(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID, false);
+  BOOST_CHECK_EQUAL(face.isLocalControlHeaderEnabled(), false);
+  BOOST_CHECK_EQUAL(face.isLocalControlHeaderEnabled(
+                         LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID), false);
+}
+
+class FaceFailTestFace : public DummyFace
+{
+public:
+  FaceFailTestFace()
+    : failCount(0)
+  {
+    this->onFail += bind(&FaceFailTestFace::failHandler, this, _1);
+  }
+
+  void
+  failOnce()
+  {
+    this->fail("reason");
+  }
+
+private:
+  void
+  failHandler(const std::string& reason)
+  {
+    BOOST_CHECK_EQUAL(reason, "reason");
+    ++this->failCount;
+  }
+
+public:
+  int failCount;
+};
+
+BOOST_AUTO_TEST_CASE(FailTwice)
+{
+  FaceFailTestFace face;
+  BOOST_CHECK_EQUAL(face.failCount, 0);
+  face.failOnce();
+  BOOST_CHECK_EQUAL(face.failCount, 1);
+  face.failOnce();
+  BOOST_CHECK_EQUAL(face.failCount, 1);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/NFD/tests/daemon/face/ndnlp.cpp b/NFD/tests/daemon/face/ndnlp.cpp
new file mode 100644
index 0000000..f5ba8e1
--- /dev/null
+++ b/NFD/tests/daemon/face/ndnlp.cpp
@@ -0,0 +1,232 @@
+/* -*- 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
+ *
+ * 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/ndnlp-sequence-generator.hpp"
+#include "face/ndnlp-slicer.hpp"
+#include "face/ndnlp-partial-message-store.hpp"
+
+#include "tests/test-common.hpp"
+
+#include <boost/scoped_array.hpp>
+
+namespace nfd {
+namespace tests {
+
+BOOST_FIXTURE_TEST_SUITE(FaceNdnlp, BaseFixture)
+
+BOOST_AUTO_TEST_CASE(SequenceBlock)
+{
+  ndnlp::SequenceBlock sb(0x8000, 2);
+  BOOST_CHECK_EQUAL(sb.count(), 2);
+  BOOST_CHECK_EQUAL(sb[0], 0x8000);
+  BOOST_CHECK_EQUAL(sb[1], 0x8001);
+  BOOST_CHECK_THROW(sb[2], std::out_of_range);
+}
+
+// sequence number can safely wrap around
+BOOST_AUTO_TEST_CASE(SequenceBlockWrap)
+{
+  ndnlp::SequenceBlock sb(std::numeric_limits<uint64_t>::max(), 2);
+  BOOST_CHECK_EQUAL(sb[0], std::numeric_limits<uint64_t>::max());
+  BOOST_CHECK_EQUAL(sb[1], std::numeric_limits<uint64_t>::min());
+  BOOST_CHECK_EQUAL(sb[1] - sb[0], 1);
+}
+
+BOOST_AUTO_TEST_CASE(SequenceGenerator)
+{
+  ndnlp::SequenceGenerator seqgen;
+
+  ndnlp::SequenceBlock sb1 = seqgen.nextBlock(2);
+  BOOST_CHECK_EQUAL(sb1.count(), 2);
+
+  ndnlp::SequenceBlock sb2 = seqgen.nextBlock(1);
+  BOOST_CHECK_NE(sb1[0], sb2[0]);
+  BOOST_CHECK_NE(sb1[1], sb2[0]);
+}
+
+// slice a Block to one NDNLP packet
+BOOST_AUTO_TEST_CASE(Slice1)
+{
+  uint8_t blockValue[60];
+  memset(blockValue, 0xcc, sizeof(blockValue));
+  Block block = ndn::dataBlock(0x01, blockValue, sizeof(blockValue));
+
+  ndnlp::Slicer slicer(9000);
+  ndnlp::PacketArray pa = slicer.slice(block);
+
+  BOOST_REQUIRE_EQUAL(pa->size(), 1);
+
+  const Block& pkt = pa->at(0);
+  BOOST_CHECK_EQUAL(pkt.type(), static_cast<uint32_t>(tlv::NdnlpData));
+  pkt.parse();
+
+  const Block::element_container& elements = pkt.elements();
+  BOOST_REQUIRE_EQUAL(elements.size(), 2);
+
+  const Block& sequenceElement = elements[0];
+  BOOST_CHECK_EQUAL(sequenceElement.type(), static_cast<uint32_t>(tlv::NdnlpSequence));
+  BOOST_REQUIRE_EQUAL(sequenceElement.value_size(), sizeof(uint64_t));
+
+  const Block& payloadElement = elements[1];
+  BOOST_CHECK_EQUAL(payloadElement.type(), static_cast<uint32_t>(tlv::NdnlpPayload));
+  size_t payloadSize = payloadElement.value_size();
+  BOOST_CHECK_EQUAL(payloadSize, block.size());
+
+  BOOST_CHECK_EQUAL_COLLECTIONS(payloadElement.value_begin(), payloadElement.value_end(),
+                                block.begin(),                block.end());
+}
+
+// slice a Block to four NDNLP packets
+BOOST_AUTO_TEST_CASE(Slice4)
+{
+  uint8_t blockValue[5050];
+  memset(blockValue, 0xcc, sizeof(blockValue));
+  Block block = ndn::dataBlock(0x01, blockValue, sizeof(blockValue));
+
+  ndnlp::Slicer slicer(1500);
+  ndnlp::PacketArray pa = slicer.slice(block);
+
+  BOOST_REQUIRE_EQUAL(pa->size(), 4);
+
+  uint64_t seq0 = 0xdddd;
+
+  size_t totalPayloadSize = 0;
+
+  for (size_t i = 0; i < 4; ++i) {
+    const Block& pkt = pa->at(i);
+    BOOST_CHECK_EQUAL(pkt.type(), static_cast<uint32_t>(tlv::NdnlpData));
+    pkt.parse();
+
+    const Block::element_container& elements = pkt.elements();
+    BOOST_REQUIRE_EQUAL(elements.size(), 4);
+
+    const Block& sequenceElement = elements[0];
+    BOOST_CHECK_EQUAL(sequenceElement.type(), static_cast<uint32_t>(tlv::NdnlpSequence));
+    BOOST_REQUIRE_EQUAL(sequenceElement.value_size(), sizeof(uint64_t));
+    uint64_t seq = be64toh(*reinterpret_cast<const uint64_t*>(
+                             &*sequenceElement.value_begin()));
+    if (i == 0) {
+      seq0 = seq;
+    }
+    BOOST_CHECK_EQUAL(seq, seq0 + i);
+
+    const Block& fragIndexElement = elements[1];
+    BOOST_CHECK_EQUAL(fragIndexElement.type(), static_cast<uint32_t>(tlv::NdnlpFragIndex));
+    uint64_t fragIndex = ndn::readNonNegativeInteger(fragIndexElement);
+    BOOST_CHECK_EQUAL(fragIndex, i);
+
+    const Block& fragCountElement = elements[2];
+    BOOST_CHECK_EQUAL(fragCountElement.type(), static_cast<uint32_t>(tlv::NdnlpFragCount));
+    uint64_t fragCount = ndn::readNonNegativeInteger(fragCountElement);
+    BOOST_CHECK_EQUAL(fragCount, 4);
+
+    const Block& payloadElement = elements[3];
+    BOOST_CHECK_EQUAL(payloadElement.type(), static_cast<uint32_t>(tlv::NdnlpPayload));
+    size_t payloadSize = payloadElement.value_size();
+    totalPayloadSize += payloadSize;
+  }
+
+  BOOST_CHECK_EQUAL(totalPayloadSize, block.size());
+}
+
+class ReassembleFixture : protected BaseFixture
+{
+protected:
+  ReassembleFixture()
+    : m_slicer(1500)
+  {
+    m_partialMessageStore.onReceive +=
+      // push_back in C++11 has 2 overloads, and specific version needs to be selected
+      bind(static_cast<void (std::vector<Block>::*)(const Block&)>(&std::vector<Block>::push_back),
+           &m_received, _1);
+  }
+
+  Block
+  makeBlock(size_t valueLength)
+  {
+    boost::scoped_array<uint8_t> blockValue(new uint8_t[valueLength]);
+    memset(blockValue.get(), 0xcc, valueLength);
+    return ndn::dataBlock(0x01, blockValue.get(), valueLength);
+  }
+
+protected:
+  ndnlp::Slicer m_slicer;
+  ndnlp::PartialMessageStore m_partialMessageStore;
+
+  // received network layer packets
+  std::vector<Block> m_received;
+};
+
+// reassemble one NDNLP packets into one Block
+BOOST_FIXTURE_TEST_CASE(Reassemble1, ReassembleFixture)
+{
+  Block block = makeBlock(60);
+  ndnlp::PacketArray pa = m_slicer.slice(block);
+  BOOST_REQUIRE_EQUAL(pa->size(), 1);
+
+  BOOST_CHECK_EQUAL(m_received.size(), 0);
+  m_partialMessageStore.receiveNdnlpData(pa->at(0));
+
+  BOOST_REQUIRE_EQUAL(m_received.size(), 1);
+  BOOST_CHECK_EQUAL_COLLECTIONS(m_received.at(0).begin(), m_received.at(0).end(),
+                                block.begin(),            block.end());
+}
+
+// reassemble four and two NDNLP packets into two Blocks
+BOOST_FIXTURE_TEST_CASE(Reassemble4and2, ReassembleFixture)
+{
+  Block block = makeBlock(5050);
+  ndnlp::PacketArray pa = m_slicer.slice(block);
+  BOOST_REQUIRE_EQUAL(pa->size(), 4);
+
+  Block block2 = makeBlock(2000);
+  ndnlp::PacketArray pa2 = m_slicer.slice(block2);
+  BOOST_REQUIRE_EQUAL(pa2->size(), 2);
+
+  BOOST_CHECK_EQUAL(m_received.size(), 0);
+  m_partialMessageStore.receiveNdnlpData(pa->at(0));
+  BOOST_CHECK_EQUAL(m_received.size(), 0);
+  m_partialMessageStore.receiveNdnlpData(pa->at(1));
+  BOOST_CHECK_EQUAL(m_received.size(), 0);
+  m_partialMessageStore.receiveNdnlpData(pa2->at(1));
+  BOOST_CHECK_EQUAL(m_received.size(), 0);
+  m_partialMessageStore.receiveNdnlpData(pa->at(1));
+  BOOST_CHECK_EQUAL(m_received.size(), 0);
+  m_partialMessageStore.receiveNdnlpData(pa2->at(0));
+  BOOST_CHECK_EQUAL(m_received.size(), 1);
+  m_partialMessageStore.receiveNdnlpData(pa->at(3));
+  BOOST_CHECK_EQUAL(m_received.size(), 1);
+  m_partialMessageStore.receiveNdnlpData(pa->at(2));
+
+  BOOST_REQUIRE_EQUAL(m_received.size(), 2);
+  BOOST_CHECK_EQUAL_COLLECTIONS(m_received.at(1).begin(), m_received.at(1).end(),
+                                block.begin(),            block.end());
+  BOOST_CHECK_EQUAL_COLLECTIONS(m_received.at(0).begin(), m_received.at(0).end(),
+                                block2.begin(),           block2.end());
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/NFD/tests/daemon/face/null.cpp b/NFD/tests/daemon/face/null.cpp
new file mode 100644
index 0000000..db90410
--- /dev/null
+++ b/NFD/tests/daemon/face/null.cpp
@@ -0,0 +1,51 @@
+/* -*- 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/>.
+ */
+
+#include "face/null-face.hpp"
+
+#include "tests/test-common.hpp"
+
+namespace nfd {
+namespace tests {
+
+BOOST_FIXTURE_TEST_SUITE(FaceNull, BaseFixture)
+
+BOOST_AUTO_TEST_CASE(Send)
+{
+  shared_ptr<NullFace> face = make_shared<NullFace>();
+
+  shared_ptr<Interest> interest = makeInterest("/A");
+  BOOST_CHECK_NO_THROW(face->sendInterest(*interest));
+
+  shared_ptr<Data> data = makeData("/B");
+  BOOST_CHECK_NO_THROW(face->sendData(*data));
+
+  BOOST_CHECK_NO_THROW(face->close());
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/NFD/tests/daemon/face/packet-datasets.cpp b/NFD/tests/daemon/face/packet-datasets.cpp
new file mode 100644
index 0000000..cf661db
--- /dev/null
+++ b/NFD/tests/daemon/face/packet-datasets.cpp
@@ -0,0 +1,66 @@
+/* -*- 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/>.
+ */
+
+#include "packet-datasets.hpp"
+#include "tests/test-common.hpp"
+
+#include <boost/foreach.hpp>
+
+namespace nfd {
+namespace tests {
+
+BOOST_FIXTURE_TEST_SUITE(Datasets, BaseFixture)
+
+BOOST_AUTO_TEST_CASE(Corrupted)
+{
+  {
+    typedef CorruptedInterest Dataset;
+    Dataset dataset;
+
+    BOOST_FOREACH(Dataset::Container::value_type& data, dataset.data)
+      {
+        Block block(data.buf(), data.size());
+
+        BOOST_CHECK_THROW((Interest(block)), tlv::Error);
+      }
+  }
+
+  {
+    typedef CorruptedInterestWithLocalControlHeader Dataset;
+    Dataset dataset;
+
+    BOOST_FOREACH(Dataset::Container::value_type& data, dataset.data)
+      {
+        Block block(data.buf(), data.size());
+
+        BOOST_CHECK_THROW(ndn::nfd::LocalControlHeader::getPayload(block), tlv::Error);
+      }
+  }
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/NFD/tests/daemon/face/packet-datasets.hpp b/NFD/tests/daemon/face/packet-datasets.hpp
new file mode 100644
index 0000000..373f4e6
--- /dev/null
+++ b/NFD/tests/daemon/face/packet-datasets.hpp
@@ -0,0 +1,91 @@
+/* -*- 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_TESTS_DAEMON_FACE_PACKET_DATASETS_HPP
+#define NFD_TESTS_DAEMON_FACE_PACKET_DATASETS_HPP
+
+#include "tests/test-common.hpp"
+
+namespace nfd {
+namespace tests {
+
+class CorruptedInterestWithLocalControlHeader
+{
+public:
+  typedef std::vector<ndn::Buffer> Container;
+
+  static std::string
+  getName()
+  {
+    return "CorruptedInterestWithLocalControlHeader";
+  }
+
+  CorruptedInterestWithLocalControlHeader()
+  {
+    static const uint8_t interest[] = {
+      0x50, 0x22, 0x51, 0x81, 0x0a, 0x05, 0x1d, 0x07, 0x14, 0x08, 0x05, 0x6c, 0x6f, 0x63, 0x61,
+      0x6c, 0x08, 0x03, 0x6e, 0x64, 0x6e, 0x08, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x09,
+      0x02, 0x12, 0x00, 0x0a, 0x01, 0x01
+    };
+
+    data.push_back(ndn::Buffer(interest, sizeof(interest)));
+  }
+public:
+  Container data;
+};
+
+class CorruptedInterest
+{
+public:
+  typedef std::vector<ndn::Buffer> Container;
+
+  static std::string
+  getName()
+  {
+    return "CorruptedInterest";
+  }
+
+  CorruptedInterest()
+  {
+    static const uint8_t interest[] = {
+      0x05, 0x1d, 0x07, 0x84, 0x08, 0x05, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x08, 0x03, 0x6e, 0x64,
+      0x6e, 0x08, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x09, 0x02, 0x12, 0x00, 0x0a, 0x01,
+      0x01
+    };
+
+    data.push_back(ndn::Buffer(interest, sizeof(interest)));
+  }
+public:
+  Container data;
+};
+
+
+typedef boost::mpl::vector< CorruptedInterestWithLocalControlHeader,
+                            CorruptedInterest> CorruptedPackets;
+
+} // namespace tests
+} // namespace nfd
+
+#endif // NFD_TESTS_DAEMON_FACE_PACKET_DATASETS_HPP
diff --git a/NFD/tests/daemon/face/tcp.cpp b/NFD/tests/daemon/face/tcp.cpp
new file mode 100644
index 0000000..7c8e33b
--- /dev/null
+++ b/NFD/tests/daemon/face/tcp.cpp
@@ -0,0 +1,773 @@
+/* -*- 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/>.
+ */
+
+#include "face/tcp-factory.hpp"
+#include "core/resolver.hpp"
+#include "core/network-interface.hpp"
+#include <ndn-cxx/security/key-chain.hpp>
+
+#include "tests/test-common.hpp"
+#include "tests/limited-io.hpp"
+#include "dummy-stream-sender.hpp"
+#include "packet-datasets.hpp"
+
+namespace nfd {
+namespace tests {
+
+BOOST_FIXTURE_TEST_SUITE(FaceTcp, BaseFixture)
+
+BOOST_AUTO_TEST_CASE(ChannelMap)
+{
+  TcpFactory factory;
+
+  shared_ptr<TcpChannel> channel1 = factory.createChannel("127.0.0.1", "20070");
+  shared_ptr<TcpChannel> channel1a = factory.createChannel("127.0.0.1", "20070");
+  BOOST_CHECK_EQUAL(channel1, channel1a);
+  BOOST_CHECK_EQUAL(channel1->getUri().toString(), "tcp4://127.0.0.1:20070");
+
+  shared_ptr<TcpChannel> channel2 = factory.createChannel("127.0.0.1", "20071");
+  BOOST_CHECK_NE(channel1, channel2);
+
+  shared_ptr<TcpChannel> channel3 = factory.createChannel("::1", "20071");
+  BOOST_CHECK_NE(channel2, channel3);
+  BOOST_CHECK_EQUAL(channel3->getUri().toString(), "tcp6://[::1]:20071");
+}
+
+BOOST_AUTO_TEST_CASE(GetChannels)
+{
+  TcpFactory factory;
+  BOOST_REQUIRE_EQUAL(factory.getChannels().empty(), true);
+
+  std::vector<shared_ptr<const Channel>> expectedChannels;
+  expectedChannels.push_back(factory.createChannel("127.0.0.1", "20070"));
+  expectedChannels.push_back(factory.createChannel("127.0.0.1", "20071"));
+  expectedChannels.push_back(factory.createChannel("::1", "20071"));
+
+  for (const auto& ch : factory.getChannels()) {
+    auto pos = std::find(expectedChannels.begin(), expectedChannels.end(), ch);
+    BOOST_REQUIRE(pos != expectedChannels.end());
+    expectedChannels.erase(pos);
+  }
+  BOOST_CHECK_EQUAL(expectedChannels.size(), 0);
+}
+
+class FaceCreateFixture : protected BaseFixture
+{
+public:
+  void
+  ignore()
+  {
+  }
+
+  void
+  checkError(const std::string& errorActual, const std::string& errorExpected)
+  {
+    BOOST_CHECK_EQUAL(errorActual, errorExpected);
+  }
+
+  void
+  failIfError(const std::string& errorActual)
+  {
+    BOOST_FAIL("No error expected, but got: [" << errorActual << "]");
+  }
+};
+
+BOOST_FIXTURE_TEST_CASE(FaceCreate, FaceCreateFixture)
+{
+  TcpFactory factory = TcpFactory();
+
+  factory.createFace(FaceUri("tcp4://127.0.0.1"),
+                     bind(&FaceCreateFixture::ignore, this),
+                     bind(&FaceCreateFixture::failIfError, this, _1));
+
+  factory.createFace(FaceUri("tcp4://127.0.0.1/"),
+                     bind(&FaceCreateFixture::ignore, this),
+                     bind(&FaceCreateFixture::failIfError, this, _1));
+
+  factory.createFace(FaceUri("tcp4://127.0.0.1/path"),
+                     bind(&FaceCreateFixture::ignore, this),
+                     bind(&FaceCreateFixture::checkError, this, _1, "Invalid URI"));
+}
+
+class EndToEndFixture : protected BaseFixture
+{
+public:
+  void
+  channel1_onFaceCreated(const shared_ptr<Face>& newFace)
+  {
+    BOOST_CHECK(!static_cast<bool>(face1));
+    face1 = newFace;
+    face1->onReceiveInterest +=
+      bind(&EndToEndFixture::face1_onReceiveInterest, this, _1);
+    face1->onReceiveData +=
+      bind(&EndToEndFixture::face1_onReceiveData, this, _1);
+    face1->onFail +=
+      bind(&EndToEndFixture::face1_onFail, this);
+
+    limitedIo.afterOp();
+  }
+
+  void
+  channel1_onConnectFailed(const std::string& reason)
+  {
+    BOOST_CHECK_MESSAGE(false, reason);
+
+    limitedIo.afterOp();
+  }
+
+  void
+  face1_onReceiveInterest(const Interest& interest)
+  {
+    face1_receivedInterests.push_back(interest);
+
+    limitedIo.afterOp();
+  }
+
+  void
+  face1_onReceiveData(const Data& data)
+  {
+    face1_receivedDatas.push_back(data);
+
+    limitedIo.afterOp();
+  }
+
+  void
+  face1_onFail()
+  {
+    face1.reset();
+    limitedIo.afterOp();
+  }
+
+  void
+  channel2_onFaceCreated(const shared_ptr<Face>& newFace)
+  {
+    BOOST_CHECK(!static_cast<bool>(face2));
+    face2 = newFace;
+    face2->onReceiveInterest +=
+      bind(&EndToEndFixture::face2_onReceiveInterest, this, _1);
+    face2->onReceiveData +=
+      bind(&EndToEndFixture::face2_onReceiveData, this, _1);
+    face2->onFail +=
+      bind(&EndToEndFixture::face2_onFail, this);
+
+    limitedIo.afterOp();
+  }
+
+  void
+  channel2_onConnectFailed(const std::string& reason)
+  {
+    BOOST_CHECK_MESSAGE(false, reason);
+
+    limitedIo.afterOp();
+  }
+
+  void
+  face2_onReceiveInterest(const Interest& interest)
+  {
+    face2_receivedInterests.push_back(interest);
+
+    limitedIo.afterOp();
+  }
+
+  void
+  face2_onReceiveData(const Data& data)
+  {
+    face2_receivedDatas.push_back(data);
+
+    limitedIo.afterOp();
+  }
+
+  void
+  face2_onFail()
+  {
+    face2.reset();
+    limitedIo.afterOp();
+  }
+
+  void
+  channel_onFaceCreated(const shared_ptr<Face>& newFace)
+  {
+    faces.push_back(newFace);
+    limitedIo.afterOp();
+  }
+
+  void
+  channel_onConnectFailed(const std::string& reason)
+  {
+    BOOST_CHECK_MESSAGE(false, reason);
+
+    limitedIo.afterOp();
+  }
+
+  void
+  checkFaceList(size_t shouldBe)
+  {
+    BOOST_CHECK_EQUAL(faces.size(), shouldBe);
+  }
+
+  void
+  connect(const shared_ptr<TcpChannel>& channel,
+          const std::string& remoteHost,
+          const std::string& remotePort)
+  {
+    channel->connect(remoteHost, remotePort,
+                     bind(&EndToEndFixture::channel_onFaceCreated, this, _1),
+                     bind(&EndToEndFixture::channel_onConnectFailed, this, _1));
+  }
+
+public:
+  LimitedIo limitedIo;
+
+  shared_ptr<Face> face1;
+  std::vector<Interest> face1_receivedInterests;
+  std::vector<Data> face1_receivedDatas;
+  shared_ptr<Face> face2;
+  std::vector<Interest> face2_receivedInterests;
+  std::vector<Data> face2_receivedDatas;
+
+  std::list<shared_ptr<Face>> faces;
+};
+
+BOOST_FIXTURE_TEST_CASE(EndToEnd4, EndToEndFixture)
+{
+  TcpFactory factory1;
+
+  shared_ptr<TcpChannel> channel1 = factory1.createChannel("127.0.0.1", "20070");
+  factory1.createChannel("127.0.0.1", "20071");
+
+  BOOST_CHECK_EQUAL(channel1->isListening(), false);
+
+  channel1->listen(bind(&EndToEndFixture::channel1_onFaceCreated,   this, _1),
+                   bind(&EndToEndFixture::channel1_onConnectFailed, this, _1));
+
+  BOOST_CHECK_EQUAL(channel1->isListening(), true);
+
+  TcpFactory factory2;
+
+  shared_ptr<TcpChannel> channel2 = factory2.createChannel("127.0.0.2", "20070");
+  factory2.createChannel("127.0.0.2", "20071");
+
+  factory2.createFace(FaceUri("tcp://127.0.0.1:20070"),
+                      bind(&EndToEndFixture::channel2_onFaceCreated, this, _1),
+                      bind(&EndToEndFixture::channel2_onConnectFailed, this, _1));
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(2, time::seconds(10)) == LimitedIo::EXCEED_OPS,
+                      "TcpChannel error: cannot connect or cannot accept connection");
+
+  BOOST_REQUIRE(static_cast<bool>(face1));
+  BOOST_REQUIRE(static_cast<bool>(face2));
+
+  BOOST_CHECK(face1->isOnDemand());
+  BOOST_CHECK(!face2->isOnDemand());
+
+  BOOST_CHECK_EQUAL(face2->getRemoteUri().toString(), "tcp4://127.0.0.1:20070");
+  BOOST_CHECK_EQUAL(face1->getLocalUri().toString(), "tcp4://127.0.0.1:20070");
+  // face1 has an unknown remoteUri, since the source port is automatically chosen by OS
+
+  BOOST_CHECK_EQUAL(face1->isLocal(), true);
+  BOOST_CHECK_EQUAL(face2->isLocal(), true);
+
+  BOOST_CHECK_EQUAL(static_cast<bool>(dynamic_pointer_cast<LocalFace>(face1)), true);
+  BOOST_CHECK_EQUAL(static_cast<bool>(dynamic_pointer_cast<LocalFace>(face2)), true);
+
+  // integrated tests needs to check that TcpFace for non-loopback fails these tests...
+
+  shared_ptr<Interest> interest1 = makeInterest("ndn:/TpnzGvW9R");
+  shared_ptr<Data>     data1     = makeData("ndn:/KfczhUqVix");
+  shared_ptr<Interest> interest2 = makeInterest("ndn:/QWiIMfj5sL");
+  shared_ptr<Data>     data2     = makeData("ndn:/XNBV796f");
+
+  face1->sendInterest(*interest1);
+  face1->sendInterest(*interest1);
+  face1->sendInterest(*interest1);
+  face1->sendData    (*data1    );
+  size_t nBytesSent1 = interest1->wireEncode().size() * 3 + data1->wireEncode().size();
+  face2->sendInterest(*interest2);
+  face2->sendData    (*data2    );
+  face2->sendData    (*data2    );
+  face2->sendData    (*data2    );
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(8, time::seconds(10)) == LimitedIo::EXCEED_OPS,
+                      "TcpChannel error: cannot send or receive Interest/Data packets");
+
+  BOOST_REQUIRE_EQUAL(face1_receivedInterests.size(), 1);
+  BOOST_REQUIRE_EQUAL(face1_receivedDatas    .size(), 3);
+  BOOST_REQUIRE_EQUAL(face2_receivedInterests.size(), 3);
+  BOOST_REQUIRE_EQUAL(face2_receivedDatas    .size(), 1);
+
+  BOOST_CHECK_EQUAL(face1_receivedInterests[0].getName(), interest2->getName());
+  BOOST_CHECK_EQUAL(face1_receivedDatas    [0].getName(), data2->getName());
+  BOOST_CHECK_EQUAL(face2_receivedInterests[0].getName(), interest1->getName());
+  BOOST_CHECK_EQUAL(face2_receivedDatas    [0].getName(), data1->getName());
+
+  // needed to ensure NOutBytes counters are accurate
+  limitedIo.run(LimitedIo::UNLIMITED_OPS, time::seconds(1));
+
+  const FaceCounters& counters1 = face1->getCounters();
+  BOOST_CHECK_EQUAL(counters1.getNInInterests() , 1);
+  BOOST_CHECK_EQUAL(counters1.getNInDatas()     , 3);
+  BOOST_CHECK_EQUAL(counters1.getNOutInterests(), 3);
+  BOOST_CHECK_EQUAL(counters1.getNOutDatas()    , 1);
+  BOOST_CHECK_EQUAL(counters1.getNOutBytes(), nBytesSent1);
+
+  const FaceCounters& counters2 = face2->getCounters();
+  BOOST_CHECK_EQUAL(counters2.getNInInterests() , 3);
+  BOOST_CHECK_EQUAL(counters2.getNInDatas()     , 1);
+  BOOST_CHECK_EQUAL(counters2.getNOutInterests(), 1);
+  BOOST_CHECK_EQUAL(counters2.getNOutDatas()    , 3);
+  BOOST_CHECK_EQUAL(counters2.getNInBytes(), nBytesSent1);
+}
+
+BOOST_FIXTURE_TEST_CASE(EndToEnd6, EndToEndFixture)
+{
+  TcpFactory factory1;
+
+  shared_ptr<TcpChannel> channel1 = factory1.createChannel("::1", "20070");
+  shared_ptr<TcpChannel> channel2 = factory1.createChannel("::1", "20071");
+
+  channel1->listen(bind(&EndToEndFixture::channel1_onFaceCreated,   this, _1),
+                   bind(&EndToEndFixture::channel1_onConnectFailed, this, _1));
+
+  TcpFactory factory2;
+
+  factory2.createChannel("::2", "20070");
+
+  factory2.createFace(FaceUri("tcp://[::1]:20070"),
+                      bind(&EndToEndFixture::channel2_onFaceCreated, this, _1),
+                      bind(&EndToEndFixture::channel2_onConnectFailed, this, _1));
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(2, time::seconds(10)) == LimitedIo::EXCEED_OPS,
+                      "TcpChannel error: cannot connect or cannot accept connection");
+
+  BOOST_REQUIRE(static_cast<bool>(face1));
+  BOOST_REQUIRE(static_cast<bool>(face2));
+
+  BOOST_CHECK_EQUAL(face2->getRemoteUri().toString(), "tcp6://[::1]:20070");
+  BOOST_CHECK_EQUAL(face1->getLocalUri().toString(), "tcp6://[::1]:20070");
+  // face1 has an unknown remoteUri, since the source port is automatically chosen by OS
+
+  BOOST_CHECK_EQUAL(face1->isLocal(), true);
+  BOOST_CHECK_EQUAL(face2->isLocal(), true);
+
+  BOOST_CHECK_EQUAL(static_cast<bool>(dynamic_pointer_cast<LocalFace>(face1)), true);
+  BOOST_CHECK_EQUAL(static_cast<bool>(dynamic_pointer_cast<LocalFace>(face2)), true);
+
+  // integrated tests needs to check that TcpFace for non-loopback fails these tests...
+
+  shared_ptr<Interest> interest1 = makeInterest("ndn:/TpnzGvW9R");
+  shared_ptr<Data>     data1     = makeData("ndn:/KfczhUqVix");
+  shared_ptr<Interest> interest2 = makeInterest("ndn:/QWiIMfj5sL");
+  shared_ptr<Data>     data2     = makeData("ndn:/XNBV796f");
+
+  face1->sendInterest(*interest1);
+  face1->sendData    (*data1    );
+  face2->sendInterest(*interest2);
+  face2->sendData    (*data2    );
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(4, time::seconds(10)) == LimitedIo::EXCEED_OPS,
+                      "TcpChannel error: cannot send or receive Interest/Data packets");
+
+
+  BOOST_REQUIRE_EQUAL(face1_receivedInterests.size(), 1);
+  BOOST_REQUIRE_EQUAL(face1_receivedDatas    .size(), 1);
+  BOOST_REQUIRE_EQUAL(face2_receivedInterests.size(), 1);
+  BOOST_REQUIRE_EQUAL(face2_receivedDatas    .size(), 1);
+
+  BOOST_CHECK_EQUAL(face1_receivedInterests[0].getName(), interest2->getName());
+  BOOST_CHECK_EQUAL(face1_receivedDatas    [0].getName(), data2->getName());
+  BOOST_CHECK_EQUAL(face2_receivedInterests[0].getName(), interest1->getName());
+  BOOST_CHECK_EQUAL(face2_receivedDatas    [0].getName(), data1->getName());
+}
+
+BOOST_FIXTURE_TEST_CASE(MultipleAccepts, EndToEndFixture)
+{
+  TcpFactory factory;
+
+  shared_ptr<TcpChannel> channel1 = factory.createChannel("127.0.0.1", "20070");
+  shared_ptr<TcpChannel> channel2 = factory.createChannel("127.0.0.1", "20071");
+
+  channel1->listen(bind(&EndToEndFixture::channel_onFaceCreated,   this, _1),
+                   bind(&EndToEndFixture::channel_onConnectFailed, this, _1));
+
+  channel2->connect("127.0.0.1", "20070",
+                    bind(&EndToEndFixture::channel_onFaceCreated, this, _1),
+                    bind(&EndToEndFixture::channel_onConnectFailed, this, _1),
+                    time::seconds(4)); // very short timeout
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(2, time::seconds(10)) == LimitedIo::EXCEED_OPS,
+                      "TcpChannel error: cannot connect or cannot accept connection");
+
+
+  BOOST_CHECK_EQUAL(faces.size(), 2);
+
+  shared_ptr<TcpChannel> channel3 = factory.createChannel("127.0.0.1", "20072");
+  channel3->connect("127.0.0.1", "20070",
+                    bind(&EndToEndFixture::channel_onFaceCreated, this, _1),
+                    bind(&EndToEndFixture::channel_onConnectFailed, this, _1),
+                    time::seconds(4)); // very short timeout
+
+
+  shared_ptr<TcpChannel> channel4 = factory.createChannel("127.0.0.1", "20073");
+
+  BOOST_CHECK_NE(channel3, channel4);
+
+  scheduler::schedule(time::seconds(1),
+                      bind(&EndToEndFixture::connect, this, channel4, "127.0.0.1", "20070"));
+
+  scheduler::schedule(time::milliseconds(500),
+                      bind(&EndToEndFixture::checkFaceList, this, 4));
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(4,// 2 connects and 2 accepts
+                      time::seconds(10)) == LimitedIo::EXCEED_OPS,
+                      "TcpChannel error: cannot connect or cannot accept multiple connections");
+
+  BOOST_CHECK_EQUAL(faces.size(), 6);
+}
+
+
+BOOST_FIXTURE_TEST_CASE(FaceClosing, EndToEndFixture)
+{
+  TcpFactory factory;
+
+  shared_ptr<TcpChannel> channel1 = factory.createChannel("127.0.0.1", "20070");
+  shared_ptr<TcpChannel> channel2 = factory.createChannel("127.0.0.1", "20071");
+
+  channel1->listen(bind(&EndToEndFixture::channel1_onFaceCreated,   this, _1),
+                   bind(&EndToEndFixture::channel1_onConnectFailed, this, _1));
+
+  channel2->connect("127.0.0.1", "20070",
+                    bind(&EndToEndFixture::channel2_onFaceCreated, this, _1),
+                    bind(&EndToEndFixture::channel2_onConnectFailed, this, _1),
+                    time::seconds(4)); // very short timeout
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(2, time::seconds(10)) == LimitedIo::EXCEED_OPS,
+                      "TcpChannel error: cannot connect or cannot accept connection");
+
+  BOOST_CHECK_EQUAL(channel1->size(), 1);
+  BOOST_CHECK_EQUAL(channel2->size(), 1);
+
+  BOOST_REQUIRE(static_cast<bool>(face1));
+  BOOST_CHECK(static_cast<bool>(face2));
+
+  // Face::close must be invoked during io run to be counted as an op
+  scheduler::schedule(time::milliseconds(100), bind(&Face::close, face1));
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(2, time::seconds(10)) == LimitedIo::EXCEED_OPS,
+                      "FaceClosing error: cannot properly close faces");
+
+  // both faces should get closed
+  BOOST_CHECK(!static_cast<bool>(face1));
+  BOOST_CHECK(!static_cast<bool>(face2));
+
+  BOOST_CHECK_EQUAL(channel1->size(), 0);
+  BOOST_CHECK_EQUAL(channel2->size(), 0);
+}
+
+
+class SimpleEndToEndFixture : protected BaseFixture
+{
+public:
+  void
+  onFaceCreated(const shared_ptr<Face>& face)
+  {
+    face->onReceiveInterest +=
+      bind(&SimpleEndToEndFixture::onReceiveInterest, this, _1);
+    face->onReceiveData +=
+      bind(&SimpleEndToEndFixture::onReceiveData, this, _1);
+    face->onFail +=
+      bind(&SimpleEndToEndFixture::onFail, this, face);
+
+    if (static_cast<bool>(dynamic_pointer_cast<LocalFace>(face))) {
+      static_pointer_cast<LocalFace>(face)->setLocalControlHeaderFeature(
+        LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID);
+
+      static_pointer_cast<LocalFace>(face)->setLocalControlHeaderFeature(
+        LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID);
+    }
+
+    limitedIo.afterOp();
+  }
+
+  void
+  onConnectFailed(const std::string& reason)
+  {
+    BOOST_CHECK_MESSAGE(false, reason);
+
+    limitedIo.afterOp();
+  }
+
+  void
+  onReceiveInterest(const Interest& interest)
+  {
+    receivedInterests.push_back(interest);
+
+    limitedIo.afterOp();
+  }
+
+  void
+  onReceiveData(const Data& data)
+  {
+    receivedDatas.push_back(data);
+
+    limitedIo.afterOp();
+  }
+
+  void
+  onFail(const shared_ptr<Face>& face)
+  {
+    limitedIo.afterOp();
+  }
+
+public:
+  LimitedIo limitedIo;
+
+  std::vector<Interest> receivedInterests;
+  std::vector<Data> receivedDatas;
+};
+
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(LocalFaceCorruptedInput, Dataset,
+                                 CorruptedPackets, SimpleEndToEndFixture)
+{
+  TcpFactory factory;
+
+  shared_ptr<TcpChannel> channel = factory.createChannel("127.0.0.1", "20070");
+  channel->listen(bind(&SimpleEndToEndFixture::onFaceCreated,   this, _1),
+                  bind(&SimpleEndToEndFixture::onConnectFailed, this, _1));
+  BOOST_REQUIRE_EQUAL(channel->isListening(), true);
+
+  DummyStreamSender<boost::asio::ip::tcp, Dataset> sender;
+  sender.start(Resolver<boost::asio::ip::tcp>::syncResolve("127.0.0.1", "20070"));
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(LimitedIo::UNLIMITED_OPS,
+                                    time::seconds(1)) == LimitedIo::EXCEED_TIME,
+                      "Exception thrown for " + Dataset::getName());
+}
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(FaceCorruptedInput, Dataset,
+                                 CorruptedPackets, SimpleEndToEndFixture)
+{
+  // tests with non-local Face
+  std::string someIpv4Address;
+  for (const auto& netif : listNetworkInterfaces()) {
+    if (!netif.isLoopback() && netif.isUp() && !netif.ipv4Addresses.empty()) {
+      someIpv4Address = netif.ipv4Addresses[0].to_string();
+      break;
+    }
+  }
+  if (someIpv4Address.empty()) {
+    BOOST_TEST_MESSAGE("Test with non-local Face cannot be run "
+                       "(no non-local interface with IPv4 address available)");
+    return;
+  }
+
+  TcpFactory factory;
+
+  shared_ptr<TcpChannel> channel = factory.createChannel(someIpv4Address, "20070");
+  channel->listen(bind(&SimpleEndToEndFixture::onFaceCreated,   this, _1),
+                  bind(&SimpleEndToEndFixture::onConnectFailed, this, _1));
+  BOOST_REQUIRE_EQUAL(channel->isListening(), true);
+
+  DummyStreamSender<boost::asio::ip::tcp, Dataset> sender;
+  sender.start(Resolver<boost::asio::ip::tcp>::syncResolve(someIpv4Address, "20070"));
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(LimitedIo::UNLIMITED_OPS,
+                                    time::seconds(1)) == LimitedIo::EXCEED_TIME,
+                      "Exception thrown for " + Dataset::getName());
+}
+
+class FaceCreateTimeoutFixture : protected BaseFixture
+{
+public:
+  void
+  onFaceCreated(const shared_ptr<Face>& newFace)
+  {
+    BOOST_CHECK_MESSAGE(false, "Timeout expected");
+    BOOST_CHECK(!static_cast<bool>(face1));
+    face1 = newFace;
+
+    limitedIo.afterOp();
+  }
+
+  void
+  onConnectFailed(const std::string& reason)
+  {
+    BOOST_CHECK_MESSAGE(true, reason);
+
+    limitedIo.afterOp();
+  }
+
+public:
+  LimitedIo limitedIo;
+
+  shared_ptr<Face> face1;
+};
+
+BOOST_FIXTURE_TEST_CASE(FaceCreateTimeout, FaceCreateTimeoutFixture)
+{
+  TcpFactory factory;
+  shared_ptr<TcpChannel> channel = factory.createChannel("0.0.0.0", "20070");
+
+  factory.createFace(FaceUri("tcp://192.0.2.1:20070"),
+                     bind(&FaceCreateTimeoutFixture::onFaceCreated, this, _1),
+                     bind(&FaceCreateTimeoutFixture::onConnectFailed, this, _1));
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(1, time::seconds(10)) == LimitedIo::EXCEED_OPS,
+                      "TcpChannel error: cannot connect or cannot accept connection");
+
+  BOOST_CHECK_EQUAL(static_cast<bool>(face1), false);
+}
+
+BOOST_FIXTURE_TEST_CASE(Bug1856, EndToEndFixture)
+{
+  TcpFactory factory1;
+
+  shared_ptr<TcpChannel> channel1 = factory1.createChannel("127.0.0.1", "20070");
+  factory1.createChannel("127.0.0.1", "20071");
+
+  BOOST_CHECK_EQUAL(channel1->isListening(), false);
+
+  channel1->listen(bind(&EndToEndFixture::channel1_onFaceCreated,   this, _1),
+                   bind(&EndToEndFixture::channel1_onConnectFailed, this, _1));
+
+  BOOST_CHECK_EQUAL(channel1->isListening(), true);
+
+  TcpFactory factory2;
+
+  shared_ptr<TcpChannel> channel2 = factory2.createChannel("127.0.0.2", "20070");
+  factory2.createChannel("127.0.0.2", "20071");
+
+  factory2.createFace(FaceUri("tcp://127.0.0.1:20070"),
+                      bind(&EndToEndFixture::channel2_onFaceCreated, this, _1),
+                      bind(&EndToEndFixture::channel2_onConnectFailed, this, _1));
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(2, time::seconds(10)) == LimitedIo::EXCEED_OPS,
+                      "TcpChannel error: cannot connect or cannot accept connection");
+
+  BOOST_REQUIRE(static_cast<bool>(face1));
+  BOOST_REQUIRE(static_cast<bool>(face2));
+
+  std::ostringstream hugeName;
+  hugeName << "/huge-name/";
+  for (size_t i = 0; i < ndn::MAX_NDN_PACKET_SIZE; i++)
+    hugeName << 'a';
+
+  shared_ptr<Interest> interest = makeInterest("ndn:/KfczhUqVix");
+  shared_ptr<Interest> hugeInterest = makeInterest(hugeName.str());
+
+  face1->sendInterest(*hugeInterest);
+  face2->sendInterest(*interest);
+  face2->sendInterest(*interest);
+
+  limitedIo.run(LimitedIo::UNLIMITED_OPS, time::seconds(1));
+  BOOST_TEST_MESSAGE("Unexpected assertion test passed");
+}
+
+class FakeNetworkInterfaceFixture : public BaseFixture
+{
+public:
+  FakeNetworkInterfaceFixture()
+  {
+    using namespace boost::asio::ip;
+
+    auto fakeInterfaces = make_shared<std::vector<NetworkInterfaceInfo>>();
+
+    fakeInterfaces->push_back(
+      NetworkInterfaceInfo {0, "eth0",
+        ethernet::Address::fromString("3e:15:c2:8b:65:00"),
+        {address_v4::from_string("0.0.0.0")},
+        {address_v6::from_string("::")},
+        address_v4(),
+        IFF_UP});
+    fakeInterfaces->push_back(
+      NetworkInterfaceInfo {1, "eth0",
+        ethernet::Address::fromString("3e:15:c2:8b:65:00"),
+        {address_v4::from_string("192.168.2.1"), address_v4::from_string("192.168.2.2")},
+        {},
+        address_v4::from_string("192.168.2.255"),
+        0});
+    fakeInterfaces->push_back(
+      NetworkInterfaceInfo {2, "eth1",
+        ethernet::Address::fromString("3e:15:c2:8b:65:00"),
+        {address_v4::from_string("198.51.100.1")},
+        {address_v6::from_string("2001:db8::2"), address_v6::from_string("2001:db8::3")},
+        address_v4::from_string("198.51.100.255"),
+        IFF_MULTICAST | IFF_BROADCAST | IFF_UP});
+
+    setDebugNetworkInterfaces(fakeInterfaces);
+  }
+
+  ~FakeNetworkInterfaceFixture()
+  {
+    setDebugNetworkInterfaces(nullptr);
+  }
+};
+
+BOOST_FIXTURE_TEST_CASE(Bug2292, FakeNetworkInterfaceFixture)
+{
+  using namespace boost::asio::ip;
+
+  TcpFactory factory;
+  factory.prohibitEndpoint(tcp::Endpoint(address_v4::from_string("192.168.2.1"), 1024));
+  BOOST_REQUIRE_EQUAL(factory.m_prohibitedEndpoints.size(), 1);
+  BOOST_CHECK((factory.m_prohibitedEndpoints ==
+               std::set<tcp::Endpoint> {
+                 tcp::Endpoint(address_v4::from_string("192.168.2.1"), 1024),
+               }));
+
+  factory.m_prohibitedEndpoints.clear();
+  factory.prohibitEndpoint(tcp::Endpoint(address_v6::from_string("2001:db8::1"), 2048));
+  BOOST_REQUIRE_EQUAL(factory.m_prohibitedEndpoints.size(), 1);
+  BOOST_CHECK((factory.m_prohibitedEndpoints ==
+               std::set<tcp::Endpoint> {
+                 tcp::Endpoint(address_v6::from_string("2001:db8::1"), 2048)
+               }));
+
+  factory.m_prohibitedEndpoints.clear();
+  factory.prohibitEndpoint(tcp::Endpoint(address_v4(), 1024));
+  BOOST_REQUIRE_EQUAL(factory.m_prohibitedEndpoints.size(), 4);
+  BOOST_CHECK((factory.m_prohibitedEndpoints ==
+               std::set<tcp::Endpoint> {
+                 tcp::Endpoint(address_v4::from_string("192.168.2.1"), 1024),
+                 tcp::Endpoint(address_v4::from_string("192.168.2.2"), 1024),
+                 tcp::Endpoint(address_v4::from_string("198.51.100.1"), 1024),
+                 tcp::Endpoint(address_v4::from_string("0.0.0.0"), 1024)
+               }));
+
+  factory.m_prohibitedEndpoints.clear();
+  factory.prohibitEndpoint(tcp::Endpoint(address_v6(), 2048));
+  BOOST_REQUIRE_EQUAL(factory.m_prohibitedEndpoints.size(), 3);
+  BOOST_CHECK((factory.m_prohibitedEndpoints ==
+               std::set<tcp::Endpoint> {
+                 tcp::Endpoint(address_v6::from_string("2001:db8::2"), 2048),
+                 tcp::Endpoint(address_v6::from_string("2001:db8::3"), 2048),
+                 tcp::Endpoint(address_v6::from_string("::"), 2048)
+               }));
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/NFD/tests/daemon/face/udp.cpp b/NFD/tests/daemon/face/udp.cpp
new file mode 100644
index 0000000..abab320
--- /dev/null
+++ b/NFD/tests/daemon/face/udp.cpp
@@ -0,0 +1,1079 @@
+/* -*- 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/>.
+ */
+
+#include "face/udp-factory.hpp"
+#include "core/network-interface.hpp"
+
+#include "tests/test-common.hpp"
+#include "tests/limited-io.hpp"
+
+namespace nfd {
+namespace tests {
+
+BOOST_FIXTURE_TEST_SUITE(FaceUdp, BaseFixture)
+
+BOOST_AUTO_TEST_CASE(GetChannels)
+{
+  UdpFactory factory;
+  BOOST_REQUIRE_EQUAL(factory.getChannels().empty(), true);
+
+  std::vector<shared_ptr<const Channel> > expectedChannels;
+
+  expectedChannels.push_back(factory.createChannel("127.0.0.1", "20070"));
+  expectedChannels.push_back(factory.createChannel("127.0.0.1", "20071"));
+  expectedChannels.push_back(factory.createChannel("::1", "20071"));
+
+  std::list<shared_ptr<const Channel> > channels = factory.getChannels();
+  for (std::list<shared_ptr<const Channel> >::const_iterator i = channels.begin();
+       i != channels.end(); ++i)
+    {
+      std::vector<shared_ptr<const Channel> >::iterator pos =
+        std::find(expectedChannels.begin(), expectedChannels.end(), *i);
+
+      BOOST_REQUIRE(pos != expectedChannels.end());
+      expectedChannels.erase(pos);
+    }
+
+  BOOST_CHECK_EQUAL(expectedChannels.size(), 0);
+}
+
+class FactoryErrorCheck : protected BaseFixture
+{
+public:
+  bool
+  isTheSameMulticastEndpoint(const UdpFactory::Error& e) {
+    return strcmp(e.what(),
+                  "Cannot create the requested UDP unicast channel, local "
+                  "endpoint is already allocated for a UDP multicast face") == 0;
+  }
+
+  bool
+  isNotMulticastAddress(const UdpFactory::Error& e) {
+    return strcmp(e.what(),
+                  "Cannot create the requested UDP multicast face, "
+                  "the multicast group given as input is not a multicast address") == 0;
+  }
+
+  bool
+  isTheSameUnicastEndpoint(const UdpFactory::Error& e) {
+    return strcmp(e.what(),
+                  "Cannot create the requested UDP multicast face, local "
+                  "endpoint is already allocated for a UDP unicast channel") == 0;
+  }
+
+  bool
+  isLocalEndpointOnDifferentGroup(const UdpFactory::Error& e) {
+    return strcmp(e.what(),
+                  "Cannot create the requested UDP multicast face, local "
+                  "endpoint is already allocated for a UDP multicast face "
+                  "on a different multicast group") == 0;
+  }
+};
+
+BOOST_FIXTURE_TEST_CASE(ChannelMapUdp, FactoryErrorCheck)
+{
+  using boost::asio::ip::udp;
+
+  UdpFactory factory = UdpFactory();
+
+  //to instantiate multicast face on a specific ip address, change interfaceIp
+  std::string interfaceIp = "0.0.0.0";
+
+  shared_ptr<UdpChannel> channel1 = factory.createChannel("127.0.0.1", "20070");
+  shared_ptr<UdpChannel> channel1a = factory.createChannel("127.0.0.1", "20070");
+  BOOST_CHECK_EQUAL(channel1, channel1a);
+  BOOST_CHECK_EQUAL(channel1->getUri().toString(), "udp4://127.0.0.1:20070");
+
+  shared_ptr<UdpChannel> channel2 = factory.createChannel("127.0.0.1", "20071");
+  BOOST_CHECK_NE(channel1, channel2);
+
+  shared_ptr<UdpChannel> channel3 = factory.createChannel(interfaceIp, "20070");
+
+  shared_ptr<UdpChannel> channel4 = factory.createChannel("::1", "20071");
+  BOOST_CHECK_NE(channel2, channel4);
+  BOOST_CHECK_EQUAL(channel4->getUri().toString(), "udp6://[::1]:20071");
+
+  //same endpoint of a unicast channel
+  BOOST_CHECK_EXCEPTION(factory.createMulticastFace(interfaceIp,
+                                                    "224.0.0.1",
+                                                    "20070"),
+                        UdpFactory::Error,
+                        isTheSameUnicastEndpoint);
+
+
+  shared_ptr<MulticastUdpFace> multicastFace1 = factory.createMulticastFace(interfaceIp,
+                                                                            "224.0.0.1",
+                                                                            "20072");
+  shared_ptr<MulticastUdpFace> multicastFace1a = factory.createMulticastFace(interfaceIp,
+                                                                            "224.0.0.1",
+                                                                            "20072");
+  BOOST_CHECK_EQUAL(multicastFace1, multicastFace1a);
+
+
+  //same endpoint of a multicast face
+  BOOST_CHECK_EXCEPTION(factory.createChannel(interfaceIp, "20072"),
+                        UdpFactory::Error,
+                        isTheSameMulticastEndpoint);
+
+  //same multicast endpoint, different group
+  BOOST_CHECK_EXCEPTION(factory.createMulticastFace(interfaceIp,
+                                                    "224.0.0.42",
+                                                    "20072"),
+                        UdpFactory::Error,
+                        isLocalEndpointOnDifferentGroup);
+
+  BOOST_CHECK_EXCEPTION(factory.createMulticastFace(interfaceIp,
+                                                    "192.168.10.15",
+                                                    "20025"),
+                        UdpFactory::Error,
+                        isNotMulticastAddress);
+
+
+//  //Test commented because it required to be run in a machine that can resolve ipv6 query
+//  shared_ptr<UdpChannel> channel1v6 = factory.createChannel(//"::1",
+//                                                     "fe80::5e96:9dff:fe7d:9c8d%en1",
+//                                                     //"fe80::aa54:b2ff:fe08:27b8%wlan0",
+//                                                     "20070");
+//
+//  //the creation of multicastFace2 works properly. It has been disable because it needs an IP address of
+//  //an available network interface (different from the first one used)
+//  shared_ptr<MulticastUdpFace> multicastFace2 = factory.createMulticastFace("192.168.1.17",
+//                                                                            "224.0.0.1",
+//                                                                            "20073");
+//  BOOST_CHECK_NE(multicastFace1, multicastFace2);
+//
+//
+//  //ipv6 - work in progress
+//  shared_ptr<MulticastUdpFace> multicastFace3 = factory.createMulticastFace("fe80::5e96:9dff:fe7d:9c8d%en1",
+//                                                                            "FF01:0:0:0:0:0:0:2",
+//                                                                            "20073");
+//
+//  shared_ptr<MulticastUdpFace> multicastFace4 = factory.createMulticastFace("fe80::aa54:b2ff:fe08:27b8%wlan0",
+//                                                                            "FF01:0:0:0:0:0:0:2",
+//                                                                            "20073");
+//
+//  BOOST_CHECK_EQUAL(multicastFace3, multicastFace4);
+//
+//  shared_ptr<MulticastUdpFace> multicastFace5 = factory.createMulticastFace("::1",
+//                                                                            "FF01:0:0:0:0:0:0:2",
+//                                                                            "20073");
+//
+//  BOOST_CHECK_NE(multicastFace3, multicastFace5);
+//
+//  //same local ipv6 endpoint for a different multicast group
+//  BOOST_CHECK_THROW(factory.createMulticastFace("fe80::aa54:b2ff:fe08:27b8%wlan0",
+//                                                "FE01:0:0:0:0:0:0:2",
+//                                                "20073"),
+//                    UdpFactory::Error);
+//
+//  //same local ipv6 (expect for th port number) endpoint for a different multicast group
+//  BOOST_CHECK_THROW(factory.createMulticastFace("fe80::aa54:b2ff:fe08:27b8%wlan0",
+//                                                "FE01:0:0:0:0:0:0:2",
+//                                                "20075"),
+//                    UdpFactory::Error);
+//
+//  BOOST_CHECK_THROW(factory.createMulticastFace("fa80::20a:9dff:fef6:12ff",
+//                                                "FE12:0:0:0:0:0:0:2",
+//                                                "20075"),
+//                    UdpFactory::Error);
+//
+//  //not a multicast ipv6
+//  BOOST_CHECK_THROW(factory.createMulticastFace("fa80::20a:9dff:fef6:12ff",
+//                                                "A112:0:0:0:0:0:0:2",
+//                                                "20075"),
+//                    UdpFactory::Error);
+}
+
+class FaceCreateFixture : protected BaseFixture
+{
+public:
+  void
+  ignore()
+  {
+  }
+
+  void
+  checkError(const std::string& errorActual, const std::string& errorExpected)
+  {
+    BOOST_CHECK_EQUAL(errorActual, errorExpected);
+  }
+
+  void
+  failIfError(const std::string& errorActual)
+  {
+    BOOST_FAIL("No error expected, but got: [" << errorActual << "]");
+  }
+};
+
+BOOST_FIXTURE_TEST_CASE(FaceCreate, FaceCreateFixture)
+{
+  UdpFactory factory = UdpFactory();
+
+  factory.createFace(FaceUri("udp4://127.0.0.1"),
+                     bind(&FaceCreateFixture::ignore, this),
+                     bind(&FaceCreateFixture::failIfError, this, _1));
+
+  factory.createFace(FaceUri("udp4://127.0.0.1/"),
+                     bind(&FaceCreateFixture::ignore, this),
+                     bind(&FaceCreateFixture::failIfError, this, _1));
+
+  factory.createFace(FaceUri("udp4://127.0.0.1/path"),
+                     bind(&FaceCreateFixture::ignore, this),
+                     bind(&FaceCreateFixture::checkError, this, _1, "Invalid URI"));
+
+}
+
+class EndToEndFixture : protected BaseFixture
+{
+public:
+  void
+  channel1_onFaceCreated(const shared_ptr<Face>& newFace)
+  {
+    BOOST_CHECK(!static_cast<bool>(face1));
+    channel1_onFaceCreatedNoCheck(newFace);
+  }
+
+  void
+  channel1_onFaceCreatedNoCheck(const shared_ptr<Face>& newFace)
+  {
+    face1 = newFace;
+    face1->onReceiveInterest +=
+      bind(&EndToEndFixture::face1_onReceiveInterest, this, _1);
+    face1->onReceiveData +=
+      bind(&EndToEndFixture::face1_onReceiveData, this, _1);
+    face1->onFail +=
+      bind(&EndToEndFixture::face1_onFail, this);
+    BOOST_CHECK_MESSAGE(true, "channel 1 face created");
+
+    faces.push_back(face1);
+
+    limitedIo.afterOp();
+  }
+
+  void
+  channel1_onConnectFailed(const std::string& reason)
+  {
+    BOOST_CHECK_MESSAGE(false, reason);
+
+    limitedIo.afterOp();
+  }
+
+  void
+  face1_onReceiveInterest(const Interest& interest)
+  {
+    face1_receivedInterests.push_back(interest);
+
+    limitedIo.afterOp();
+  }
+
+  void
+  face1_onReceiveData(const Data& data)
+  {
+    face1_receivedDatas.push_back(data);
+
+    limitedIo.afterOp();
+  }
+
+  void
+  face1_onFail()
+  {
+    face1.reset();
+    limitedIo.afterOp();
+  }
+
+  void
+  channel2_onFaceCreated(const shared_ptr<Face>& newFace)
+  {
+    BOOST_CHECK(!static_cast<bool>(face2));
+    face2 = newFace;
+    face2->onReceiveInterest +=
+      bind(&EndToEndFixture::face2_onReceiveInterest, this, _1);
+    face2->onReceiveData +=
+      bind(&EndToEndFixture::face2_onReceiveData, this, _1);
+    face2->onFail +=
+      bind(&EndToEndFixture::face2_onFail, this);
+
+    faces.push_back(face2);
+
+    BOOST_CHECK_MESSAGE(true, "channel 2 face created");
+    limitedIo.afterOp();
+  }
+
+  void
+  channel2_onConnectFailed(const std::string& reason)
+  {
+    BOOST_CHECK_MESSAGE(false, reason);
+
+    limitedIo.afterOp();
+  }
+
+  void
+  face2_onReceiveInterest(const Interest& interest)
+  {
+    face2_receivedInterests.push_back(interest);
+
+    limitedIo.afterOp();
+  }
+
+  void
+  face2_onReceiveData(const Data& data)
+  {
+    face2_receivedDatas.push_back(data);
+
+    limitedIo.afterOp();
+  }
+
+  void
+  face2_onFail()
+  {
+    face2.reset();
+    limitedIo.afterOp();
+  }
+
+  void
+  channel3_onFaceCreated(const shared_ptr<Face>& newFace)
+  {
+    BOOST_CHECK(!static_cast<bool>(face1));
+    face3 = newFace;
+    faces.push_back(newFace);
+
+    limitedIo.afterOp();
+  }
+
+  void
+  channel_onFaceCreated(const shared_ptr<Face>& newFace)
+  {
+    faces.push_back(newFace);
+    limitedIo.afterOp();
+  }
+
+  void
+  channel_onConnectFailed(const std::string& reason)
+  {
+    BOOST_CHECK_MESSAGE(false, reason);
+
+    limitedIo.afterOp();
+  }
+
+  void
+  channel_onConnectFailedOk(const std::string& reason)
+  {
+    // it's ok, it was supposed to fail
+    limitedIo.afterOp();
+  }
+
+  void
+  checkFaceList(size_t shouldBe)
+  {
+    BOOST_CHECK_EQUAL(faces.size(), shouldBe);
+  }
+
+  void
+  connect(const shared_ptr<UdpChannel>& channel,
+          const std::string& remoteHost,
+          const std::string& remotePort)
+  {
+    channel->connect(remoteHost, remotePort,
+                     bind(&EndToEndFixture::channel_onFaceCreated, this, _1),
+                     bind(&EndToEndFixture::channel_onConnectFailed, this, _1));
+  }
+
+public:
+  LimitedIo limitedIo;
+
+  shared_ptr<Face> face1;
+  std::vector<Interest> face1_receivedInterests;
+  std::vector<Data> face1_receivedDatas;
+  shared_ptr<Face> face2;
+  std::vector<Interest> face2_receivedInterests;
+  std::vector<Data> face2_receivedDatas;
+  shared_ptr<Face> face3;
+
+  std::list< shared_ptr<Face> > faces;
+};
+
+
+BOOST_FIXTURE_TEST_CASE(EndToEnd4, EndToEndFixture)
+{
+  UdpFactory factory;
+
+  factory.createChannel("127.0.0.1", "20071");
+
+  factory.createFace(FaceUri("udp4://127.0.0.1:20070"),
+                     bind(&EndToEndFixture::channel2_onFaceCreated, this, _1),
+                     bind(&EndToEndFixture::channel2_onConnectFailed, this, _1));
+
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(1, time::seconds(1)) == LimitedIo::EXCEED_OPS,
+                      "UdpChannel error: cannot connect or cannot accept connection");
+
+  BOOST_REQUIRE(static_cast<bool>(face2));
+  BOOST_CHECK_EQUAL(face2->getRemoteUri().toString(), "udp4://127.0.0.1:20070");
+  BOOST_CHECK_EQUAL(face2->getLocalUri().toString(), "udp4://127.0.0.1:20071");
+  BOOST_CHECK_EQUAL(face2->isLocal(), false);
+  BOOST_CHECK_EQUAL(face2->getCounters().getNOutBytes(), 0);
+  BOOST_CHECK_EQUAL(face2->getCounters().getNInBytes(), 0);
+  // face1 is not created yet
+
+  shared_ptr<UdpChannel> channel1 = factory.createChannel("127.0.0.1", "20070");
+  channel1->listen(bind(&EndToEndFixture::channel1_onFaceCreated,   this, _1),
+                   bind(&EndToEndFixture::channel1_onConnectFailed, this, _1));
+
+  Interest interest1("ndn:/TpnzGvW9R");
+  Data     data1    ("ndn:/KfczhUqVix");
+  data1.setContent(0, 0);
+  Interest interest2("ndn:/QWiIMfj5sL");
+  Data     data2    ("ndn:/XNBV796f");
+  data2.setContent(0, 0);
+  Interest interest3("ndn:/QWiIhjgkj5sL");
+  Data     data3    ("ndn:/XNBV794f");
+  data3.setContent(0, 0);
+
+
+  ndn::SignatureSha256WithRsa fakeSignature;
+  fakeSignature.setValue(ndn::dataBlock(tlv::SignatureValue,
+                                        reinterpret_cast<const uint8_t*>(0),
+                                        0));
+
+  // set fake signature on data1 and data2
+  data1.setSignature(fakeSignature);
+  data2.setSignature(fakeSignature);
+  data3.setSignature(fakeSignature);
+
+  face2->sendInterest(interest2);
+  face2->sendData    (data2    );
+  face2->sendData    (data2    );
+  face2->sendData    (data2    );
+  size_t nBytesSent2 = interest2.wireEncode().size() + data2.wireEncode().size() * 3;
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(5,//4 send + 1 listen return
+                      time::seconds(4)) == LimitedIo::EXCEED_OPS,
+                      "UdpChannel error: cannot send or receive Interest/Data packets");
+
+  BOOST_REQUIRE(static_cast<bool>(face1));
+  BOOST_CHECK_EQUAL(face1->getRemoteUri().toString(), "udp4://127.0.0.1:20071");
+  BOOST_CHECK_EQUAL(face1->getLocalUri().toString(), "udp4://127.0.0.1:20070");
+  BOOST_CHECK_EQUAL(face1->isLocal(), false);
+
+  face1->sendInterest(interest1);
+  face1->sendInterest(interest1);
+  face1->sendInterest(interest1);
+  face1->sendData    (data1    );
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(4, time::seconds(4)) == LimitedIo::EXCEED_OPS,
+                      "UdpChannel error: cannot send or receive Interest/Data packets");
+
+  BOOST_REQUIRE_EQUAL(face1_receivedInterests.size(), 1);
+  BOOST_REQUIRE_EQUAL(face1_receivedDatas    .size(), 3);
+  BOOST_REQUIRE_EQUAL(face2_receivedInterests.size(), 3);
+  BOOST_REQUIRE_EQUAL(face2_receivedDatas    .size(), 1);
+
+  BOOST_CHECK_EQUAL(face1_receivedInterests[0].getName(), interest2.getName());
+  BOOST_CHECK_EQUAL(face1_receivedDatas    [0].getName(), data2.getName());
+  BOOST_CHECK_EQUAL(face2_receivedInterests[0].getName(), interest1.getName());
+  BOOST_CHECK_EQUAL(face2_receivedDatas    [0].getName(), data1.getName());
+
+
+
+  //checking if the connection accepting mechanism works properly.
+
+  face2->sendData    (data3    );
+  face2->sendInterest(interest3);
+  nBytesSent2 += data3.wireEncode().size() + interest3.wireEncode().size();
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(2, time::seconds(1)) == LimitedIo::EXCEED_OPS,
+                      "UdpChannel error: cannot send or receive Interest/Data packets");
+
+  BOOST_REQUIRE_EQUAL(face1_receivedInterests.size(), 2);
+  BOOST_REQUIRE_EQUAL(face1_receivedDatas    .size(), 4);
+
+  BOOST_CHECK_EQUAL(face1_receivedInterests[1].getName(), interest3.getName());
+  BOOST_CHECK_EQUAL(face1_receivedDatas    [3].getName(), data3.getName());
+
+  const FaceCounters& counters1 = face1->getCounters();
+  BOOST_CHECK_EQUAL(counters1.getNInInterests() , 2);
+  BOOST_CHECK_EQUAL(counters1.getNInDatas()     , 4);
+  BOOST_CHECK_EQUAL(counters1.getNOutInterests(), 3);
+  BOOST_CHECK_EQUAL(counters1.getNOutDatas()    , 1);
+  BOOST_CHECK_EQUAL(counters1.getNInBytes(), nBytesSent2);
+
+  const FaceCounters& counters2 = face2->getCounters();
+  BOOST_CHECK_EQUAL(counters2.getNInInterests() , 3);
+  BOOST_CHECK_EQUAL(counters2.getNInDatas()     , 1);
+  BOOST_CHECK_EQUAL(counters2.getNOutInterests(), 2);
+  BOOST_CHECK_EQUAL(counters2.getNOutDatas()    , 4);
+  BOOST_CHECK_EQUAL(counters2.getNOutBytes(), nBytesSent2);
+}
+
+BOOST_FIXTURE_TEST_CASE(EndToEnd6, EndToEndFixture)
+{
+  UdpFactory factory;
+
+  factory.createChannel("::1", "20071");
+
+  factory.createFace(FaceUri("udp://[::1]:20070"),
+                     bind(&EndToEndFixture::channel2_onFaceCreated, this, _1),
+                     bind(&EndToEndFixture::channel2_onConnectFailed, this, _1));
+
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(1, time::seconds(1)) == LimitedIo::EXCEED_OPS,
+                      "UdpChannel error: cannot connect or cannot accept connection");
+
+  BOOST_REQUIRE(static_cast<bool>(face2));
+  BOOST_CHECK_EQUAL(face2->getRemoteUri().toString(), "udp6://[::1]:20070");
+  BOOST_CHECK_EQUAL(face2->getLocalUri().toString(), "udp6://[::1]:20071");
+  BOOST_CHECK_EQUAL(face2->isLocal(), false);
+  // face1 is not created yet
+
+  shared_ptr<UdpChannel> channel1 = factory.createChannel("::1", "20070");
+  channel1->listen(bind(&EndToEndFixture::channel1_onFaceCreated,   this, _1),
+                   bind(&EndToEndFixture::channel1_onConnectFailed, this, _1));
+
+  Interest interest1("ndn:/TpnzGvW9R");
+  Data     data1    ("ndn:/KfczhUqVix");
+  data1.setContent(0, 0);
+  Interest interest2("ndn:/QWiIMfj5sL");
+  Data     data2    ("ndn:/XNBV796f");
+  data2.setContent(0, 0);
+  Interest interest3("ndn:/QWiIhjgkj5sL");
+  Data     data3    ("ndn:/XNBV794f");
+  data3.setContent(0, 0);
+
+
+  ndn::SignatureSha256WithRsa fakeSignature;
+  fakeSignature.setValue(ndn::dataBlock(tlv::SignatureValue,
+                                        reinterpret_cast<const uint8_t*>(0),
+                                        0));
+
+  // set fake signature on data1 and data2
+  data1.setSignature(fakeSignature);
+  data2.setSignature(fakeSignature);
+  data3.setSignature(fakeSignature);
+
+  face2->sendInterest(interest2);
+  face2->sendData    (data2    );
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(3,//2 send + 1 listen return
+                      time::seconds(1)) == LimitedIo::EXCEED_OPS,
+                      "UdpChannel error: cannot send or receive Interest/Data packets");
+
+  BOOST_REQUIRE(static_cast<bool>(face1));
+  BOOST_CHECK_EQUAL(face1->getRemoteUri().toString(), "udp6://[::1]:20071");
+  BOOST_CHECK_EQUAL(face1->getLocalUri().toString(), "udp6://[::1]:20070");
+  BOOST_CHECK_EQUAL(face1->isLocal(), false);
+
+  face1->sendInterest(interest1);
+  face1->sendData    (data1    );
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(2, time::seconds(1)) == LimitedIo::EXCEED_OPS,
+                      "UdpChannel error: cannot send or receive Interest/Data packets");
+
+
+  BOOST_REQUIRE_EQUAL(face1_receivedInterests.size(), 1);
+  BOOST_REQUIRE_EQUAL(face1_receivedDatas    .size(), 1);
+  BOOST_REQUIRE_EQUAL(face2_receivedInterests.size(), 1);
+  BOOST_REQUIRE_EQUAL(face2_receivedDatas    .size(), 1);
+
+  BOOST_CHECK_EQUAL(face1_receivedInterests[0].getName(), interest2.getName());
+  BOOST_CHECK_EQUAL(face1_receivedDatas    [0].getName(), data2.getName());
+  BOOST_CHECK_EQUAL(face2_receivedInterests[0].getName(), interest1.getName());
+  BOOST_CHECK_EQUAL(face2_receivedDatas    [0].getName(), data1.getName());
+
+
+
+  //checking if the connection accepting mechanism works properly.
+
+  face2->sendData    (data3    );
+  face2->sendInterest(interest3);
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(2, time::seconds(1)) == LimitedIo::EXCEED_OPS,
+                      "UdpChannel error: cannot send or receive Interest/Data packets");
+
+  BOOST_REQUIRE_EQUAL(face1_receivedInterests.size(), 2);
+  BOOST_REQUIRE_EQUAL(face1_receivedDatas    .size(), 2);
+
+  BOOST_CHECK_EQUAL(face1_receivedInterests[1].getName(), interest3.getName());
+  BOOST_CHECK_EQUAL(face1_receivedDatas    [1].getName(), data3.getName());
+}
+
+BOOST_FIXTURE_TEST_CASE(MultipleAccepts, EndToEndFixture)
+{
+  Interest interest1("ndn:/TpnzGvW9R");
+  Interest interest2("ndn:/QWiIMfj5sL");
+
+  UdpFactory factory;
+
+  shared_ptr<UdpChannel> channel1 = factory.createChannel("127.0.0.1", "20070");
+  shared_ptr<UdpChannel> channel2 = factory.createChannel("127.0.0.1", "20071");
+
+  channel1->listen(bind(&EndToEndFixture::channel_onFaceCreated,   this, _1),
+                   bind(&EndToEndFixture::channel_onConnectFailed, this, _1));
+
+  channel2->connect("127.0.0.1", "20070",
+                    bind(&EndToEndFixture::channel2_onFaceCreated, this, _1),
+                    bind(&EndToEndFixture::channel_onConnectFailed, this, _1));
+
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(1, time::seconds(4)) == LimitedIo::EXCEED_OPS,
+                      "UdpChannel error: cannot connect or cannot accept connection");
+
+  BOOST_CHECK_EQUAL(faces.size(), 1);
+
+  shared_ptr<UdpChannel> channel3 = factory.createChannel("127.0.0.1", "20072");
+  channel3->connect("127.0.0.1", "20070",
+                    bind(&EndToEndFixture::channel3_onFaceCreated, this, _1),
+                    bind(&EndToEndFixture::channel_onConnectFailed, this, _1));
+
+  shared_ptr<UdpChannel> channel4 = factory.createChannel("127.0.0.1", "20073");
+
+  BOOST_CHECK_NE(channel3, channel4);
+
+  scheduler::schedule(time::milliseconds(500),
+                      bind(&EndToEndFixture::connect, this, channel4, "127.0.0.1", "20070"));
+
+  scheduler::schedule(time::milliseconds(400), bind(&EndToEndFixture::checkFaceList, this, 2));
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(2,// 2 connects
+                      time::seconds(4)) == LimitedIo::EXCEED_OPS,
+                      "UdpChannel error: cannot connect or cannot accept multiple connections");
+
+  BOOST_CHECK_EQUAL(faces.size(), 3);
+
+
+  face2->sendInterest(interest1);
+  BOOST_CHECK_MESSAGE(limitedIo.run(1, time::seconds(1)) == LimitedIo::EXCEED_OPS,
+                      "UdpChannel error: cannot send or receive Interest/Data packets");
+
+  BOOST_CHECK_EQUAL(faces.size(), 4);
+
+  face3->sendInterest(interest2);
+  BOOST_CHECK_MESSAGE(limitedIo.run(1, time::seconds(1)) == LimitedIo::EXCEED_OPS,
+                      "UdpChannel error: cannot send or receive Interest/Data packets");
+
+  //channel1 should have created 2 faces, one when face2 sent an interest, one when face3 sent an interest
+  BOOST_CHECK_EQUAL(faces.size(), 5);
+  BOOST_CHECK_THROW(channel1->listen(bind(&EndToEndFixture::channel_onFaceCreated,     this, _1),
+                                     bind(&EndToEndFixture::channel_onConnectFailedOk, this, _1)),
+                    UdpChannel::Error);
+}
+
+//Test commented because it required to be run in a machine that can resolve ipv6 query
+//BOOST_FIXTURE_TEST_CASE(EndToEndIpv6, EndToEndFixture)
+//{
+//  UdpFactory factory = UdpFactory();
+//  Scheduler scheduler(g_io); // to limit the amount of time the test may take
+//
+//  EventId abortEvent =
+//  scheduler.scheduleEvent(time::seconds(1),
+//                          bind(&EndToEndFixture::abortTestCase, this,
+//                              "UdpChannel error: cannot connect or cannot accept connection"));
+//
+//  limitedIoRemaining = 1;
+//
+//  shared_ptr<UdpChannel> channel1 = factory.createChannel("::1", "20070");
+//  shared_ptr<UdpChannel> channel2 = factory.createChannel("::1", "20071");
+//
+//  channel1->listen(bind(&EndToEndFixture::channel1_onFaceCreated,   this, _1),
+//                   bind(&EndToEndFixture::channel1_onConnectFailed, this, _1));
+//
+//  channel2->connect("::1", "20070",
+//                    bind(&EndToEndFixture::channel2_onFaceCreated, this, _1),
+//                    bind(&EndToEndFixture::channel2_onConnectFailed, this, _1));
+//  g_io.run();
+//  g_io.reset();
+//  scheduler.cancelEvent(abortEvent);
+//
+//  BOOST_REQUIRE(static_cast<bool>(face2));
+//
+//  abortEvent =
+//  scheduler.scheduleEvent(time::seconds(1),
+//                          bind(&EndToEndFixture::abortTestCase, this,
+//                               "UdpChannel error: cannot send or receive Interest/Data packets"));
+//
+//  Interest interest1("ndn:/TpnzGvW9R");
+//  Data     data1    ("ndn:/KfczhUqVix");
+//  data1.setContent(0, 0);
+//  Interest interest2("ndn:/QWiIMfj5sL");
+//  Data     data2    ("ndn:/XNBV796f");
+//  data2.setContent(0, 0);
+//  Interest interest3("ndn:/QWiIhjgkj5sL");
+//  Data     data3    ("ndn:/XNBV794f");
+//  data3.setContent(0, 0);
+//
+//  ndn::SignatureSha256WithRsa fakeSignature;
+//  fakeSignature.setValue(ndn::dataBlock(tlv::SignatureValue, reinterpret_cast<const uint8_t*>(0), 0));
+//
+//  // set fake signature on data1 and data2
+//  data1.setSignature(fakeSignature);
+//  data2.setSignature(fakeSignature);
+//  data3.setSignature(fakeSignature);
+//
+//  face2->sendInterest(interest2);
+//  face2->sendData    (data2    );
+//
+//  limitedIoRemaining = 3; //2 send + 1 listen return
+//  g_io.run();
+//  g_io.reset();
+//  scheduler.cancelEvent(abortEvent);
+//
+//  BOOST_REQUIRE(static_cast<bool>(face1));
+//
+//  face1->sendInterest(interest1);
+//  face1->sendData    (data1    );
+//
+//  abortEvent =
+//  scheduler.scheduleEvent(time::seconds(1),
+//                          bind(&EndToEndFixture::abortTestCase, this,
+//                               "UdpChannel error: cannot send or receive Interest/Data packets"));
+//  limitedIoRemaining = 2;
+//  g_io.run();
+//  g_io.reset();
+//  scheduler.cancelEvent(abortEvent);
+//
+//  BOOST_REQUIRE_EQUAL(face1_receivedInterests.size(), 1);
+//  BOOST_REQUIRE_EQUAL(face1_receivedDatas    .size(), 1);
+//  BOOST_REQUIRE_EQUAL(face2_receivedInterests.size(), 1);
+//  BOOST_REQUIRE_EQUAL(face2_receivedDatas    .size(), 1);
+//
+//  BOOST_CHECK_EQUAL(face1_receivedInterests[0].getName(), interest2.getName());
+//  BOOST_CHECK_EQUAL(face1_receivedDatas    [0].getName(), data2.getName());
+//  BOOST_CHECK_EQUAL(face2_receivedInterests[0].getName(), interest1.getName());
+//  BOOST_CHECK_EQUAL(face2_receivedDatas    [0].getName(), data1.getName());
+//
+//  //checking if the connection accepting mechanism works properly.
+//
+//  face2->sendData    (data3    );
+//  face2->sendInterest(interest3);
+//
+//  abortEvent =
+//  scheduler.scheduleEvent(time::seconds(1),
+//                          bind(&EndToEndFixture::abortTestCase, this,
+//                               "UdpChannel error: cannot send or receive Interest/Data packets"));
+//  limitedIoRemaining = 2;
+//  g_io.run();
+//  g_io.reset();
+//  scheduler.cancelEvent(abortEvent);
+//
+//  BOOST_REQUIRE_EQUAL(face1_receivedInterests.size(), 2);
+//  BOOST_REQUIRE_EQUAL(face1_receivedDatas    .size(), 2);
+//
+//  BOOST_CHECK_EQUAL(face1_receivedInterests[1].getName(), interest3.getName());
+//  BOOST_CHECK_EQUAL(face1_receivedDatas    [1].getName(), data3.getName());
+//
+//}
+
+
+//Test commented because it required to be run in a machine that can resolve ipv6 query
+//BOOST_FIXTURE_TEST_CASE(MultipleAcceptsIpv6, EndToEndFixture)
+//{
+//  Interest interest1("ndn:/TpnzGvW9R");
+//  Interest interest2("ndn:/QWiIMfj5sL");
+//  Interest interest3("ndn:/QWiIhjgkj5sL");
+//
+//  UdpFactory factory = UdpFactory();
+//  Scheduler scheduler(g_io); // to limit the amount of time the test may take
+//
+//  EventId abortEvent =
+//  scheduler.scheduleEvent(time::seconds(4),
+//                          bind(&EndToEndFixture::abortTestCase, this,
+//                               "UdpChannel error: cannot connect or cannot accept connection"));
+//
+//  shared_ptr<UdpChannel> channel1 = factory.createChannel("::1", "20070");
+//  shared_ptr<UdpChannel> channel2 = factory.createChannel("::1", "20071");
+//
+//  channel1->listen(bind(&EndToEndFixture::channel_onFaceCreated,   this, _1),
+//                   bind(&EndToEndFixture::channel_onConnectFailed, this, _1));
+//
+//  channel2->connect("::1", "20070",
+//                    bind(&EndToEndFixture::channel2_onFaceCreated, this, _1),
+//                    bind(&EndToEndFixture::channel_onConnectFailed, this, _1));
+//
+//  limitedIoRemaining = 1;
+//  g_io.run();
+//  g_io.reset();
+//  scheduler.cancelEvent(abortEvent);
+//
+//  BOOST_CHECK_EQUAL(faces.size(), 1);
+//
+//  shared_ptr<UdpChannel> channel3 = factory.createChannel("::1", "20072");
+//  channel3->connect("::1", "20070",
+//                    bind(&EndToEndFixture::channel3_onFaceCreated, this, _1),
+//                    bind(&EndToEndFixture::channel_onConnectFailed, this, _1));
+//
+//  shared_ptr<UdpChannel> channel4 = factory.createChannel("::1", "20073");
+//
+//  BOOST_CHECK_NE(channel3, channel4);
+//
+//  scheduler
+//  .scheduleEvent(time::milliseconds(500),
+//                 bind(&UdpChannel::connect, channel4,
+//                      "::1", "20070",
+//                      static_cast<UdpChannel::FaceCreatedCallback>(bind(&EndToEndFixture::
+//                                                                        channel_onFaceCreated, this, _1)),
+//                      static_cast<UdpChannel::ConnectFailedCallback>(bind(&EndToEndFixture::
+//                                                                          channel_onConnectFailed, this, _1))));
+//
+//  limitedIoRemaining = 2; // 2 connects
+//  abortEvent =
+//  scheduler.scheduleEvent(time::seconds(4),
+//                          bind(&EndToEndFixture::abortTestCase, this,
+//                               "UdpChannel error: cannot connect or cannot accept multiple connections"));
+//
+//  scheduler.scheduleEvent(time::seconds(0.4),
+//                          bind(&EndToEndFixture::checkFaceList, this, 2));
+//
+//  g_io.run();
+//  g_io.reset();
+//
+//  BOOST_CHECK_EQUAL(faces.size(), 3);
+//
+//
+//  face2->sendInterest(interest1);
+//  abortEvent =
+//  scheduler.scheduleEvent(time::seconds(1),
+//                          bind(&EndToEndFixture::abortTestCase, this,
+//                               "UdpChannel error: cannot send or receive Interest/Data packets"));
+//  limitedIoRemaining = 1;
+//  g_io.run();
+//  g_io.reset();
+//  scheduler.cancelEvent(abortEvent);
+//
+//  BOOST_CHECK_EQUAL(faces.size(), 4);
+//
+//  face3->sendInterest(interest2);
+//  abortEvent =
+//  scheduler.scheduleEvent(time::seconds(1),
+//                          bind(&EndToEndFixture::abortTestCase, this,
+//                               "UdpChannel error: cannot send or receive Interest/Data packets"));
+//  limitedIoRemaining = 1;
+//  g_io.run();
+//  g_io.reset();
+//  scheduler.cancelEvent(abortEvent);
+//
+//  //channel1 should have created 2 faces, one when face2 sent an interest, one when face3 sent an interest
+//  BOOST_CHECK_EQUAL(faces.size(), 5);
+//  BOOST_CHECK_THROW(channel1->listen(bind(&EndToEndFixture::channel_onFaceCreated,
+//                                          this,
+//                                          _1),
+//                                      bind(&EndToEndFixture::channel_onConnectFailedOk,
+//                                          this,
+//                                          _1)),
+//                    UdpChannel::Error);
+//}
+
+
+BOOST_FIXTURE_TEST_CASE(FaceClosing, EndToEndFixture)
+{
+  UdpFactory factory = UdpFactory();
+
+  shared_ptr<UdpChannel> channel1 = factory.createChannel("127.0.0.1", "20070");
+  shared_ptr<UdpChannel> channel2 = factory.createChannel("127.0.0.1", "20071");
+
+  channel1->listen(bind(&EndToEndFixture::channel1_onFaceCreated,   this, _1),
+                   bind(&EndToEndFixture::channel1_onConnectFailed, this, _1));
+
+  channel2->connect("127.0.0.1", "20070",
+                    bind(&EndToEndFixture::channel2_onFaceCreated, this, _1),
+                    bind(&EndToEndFixture::channel2_onConnectFailed, this, _1));
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(1, time::seconds(4)) == LimitedIo::EXCEED_OPS,
+                      "UdpChannel error: cannot connect or cannot accept connection");
+
+  BOOST_CHECK_EQUAL(channel2->size(), 1);
+
+  BOOST_CHECK(static_cast<bool>(face2));
+
+  // Face::close must be invoked during io run to be counted as an op
+  scheduler::schedule(time::milliseconds(100), bind(&Face::close, face2));
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(1, time::seconds(4)) == LimitedIo::EXCEED_OPS,
+                      "FaceClosing error: cannot properly close faces");
+
+  BOOST_CHECK(!static_cast<bool>(face2));
+
+  BOOST_CHECK_EQUAL(channel2->size(), 0);
+}
+
+BOOST_FIXTURE_TEST_CASE(ClosingIdleFace, EndToEndFixture)
+{
+  Interest interest1("ndn:/TpnzGvW9R");
+  Interest interest2("ndn:/QWiIMfj5sL");
+
+  UdpFactory factory;
+
+  shared_ptr<UdpChannel> channel1 = factory.createChannel("127.0.0.1", "20070",
+                                                          time::seconds(2));
+  shared_ptr<UdpChannel> channel2 = factory.createChannel("127.0.0.1", "20071",
+                                                          time::seconds(2));
+
+  channel1->listen(bind(&EndToEndFixture::channel1_onFaceCreated,   this, _1),
+                   bind(&EndToEndFixture::channel1_onConnectFailed, this, _1));
+
+  channel2->connect("127.0.0.1", "20070",
+                    bind(&EndToEndFixture::channel2_onFaceCreated, this, _1),
+                    bind(&EndToEndFixture::channel2_onConnectFailed, this, _1));
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(1, time::seconds(4)) == LimitedIo::EXCEED_OPS,
+                      "UdpChannel error: cannot connect or cannot accept connection");
+
+  face2->sendInterest(interest1);
+  BOOST_CHECK(!face2->isOnDemand());
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(2,//1 send + 1 listen return
+                                      time::seconds(1)) == LimitedIo::EXCEED_OPS,
+                      "UdpChannel error: cannot send or receive Interest/Data packets");
+
+  BOOST_CHECK_EQUAL(faces.size(), 2);
+  BOOST_CHECK_MESSAGE(limitedIo.run(1, time::seconds(2)) == LimitedIo::EXCEED_TIME,
+                      "Idle face should be still open because has been used recently");
+  BOOST_CHECK_EQUAL(faces.size(), 2);
+  BOOST_CHECK_MESSAGE(limitedIo.run(1, time::seconds(4)) == LimitedIo::EXCEED_OPS,
+                      "Closing idle face error: face should be closed by now");
+
+  //the face on listen should be closed now
+  BOOST_CHECK_EQUAL(channel1->size(), 0);
+  //checking that face2 has not been closed
+  BOOST_CHECK_EQUAL(channel2->size(), 1);
+  BOOST_REQUIRE(static_cast<bool>(face2));
+
+  face2->sendInterest(interest2);
+  BOOST_CHECK_MESSAGE(limitedIo.run(2,//1 send + 1 listen return
+                                      time::seconds(1)) == LimitedIo::EXCEED_OPS,
+                      "UdpChannel error: cannot send or receive Interest/Data packets");
+  //channel1 should have created a new face by now
+  BOOST_CHECK_EQUAL(channel1->size(), 1);
+  BOOST_CHECK_EQUAL(channel2->size(), 1);
+  BOOST_REQUIRE(static_cast<bool>(face1));
+  BOOST_CHECK(face1->isOnDemand());
+
+  channel1->connect("127.0.0.1", "20071",
+                    bind(&EndToEndFixture::channel1_onFaceCreatedNoCheck, this, _1),
+                    bind(&EndToEndFixture::channel1_onConnectFailed, this, _1));
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(1,//1 connect
+                                      time::seconds(1)) == LimitedIo::EXCEED_OPS,
+                      "UdpChannel error: cannot connect");
+
+  BOOST_CHECK(!face1->isOnDemand());
+
+  //the connect should have set face1 as permanent face,
+  //but it shouln't have created any additional faces
+  BOOST_CHECK_EQUAL(channel1->size(), 1);
+  BOOST_CHECK_EQUAL(channel2->size(), 1);
+  BOOST_CHECK_MESSAGE(limitedIo.run(1, time::seconds(4)) == LimitedIo::EXCEED_TIME,
+                      "Idle face should be still open because it's permanent now");
+  //both faces are permanent, nothing should have changed
+  BOOST_CHECK_EQUAL(channel1->size(), 1);
+  BOOST_CHECK_EQUAL(channel2->size(), 1);
+}
+
+class FakeNetworkInterfaceFixture : public BaseFixture
+{
+public:
+  FakeNetworkInterfaceFixture()
+  {
+    using namespace boost::asio::ip;
+
+    auto fakeInterfaces = make_shared<std::vector<NetworkInterfaceInfo>>();
+
+    fakeInterfaces->push_back(
+      NetworkInterfaceInfo {0, "eth0",
+        ethernet::Address::fromString("3e:15:c2:8b:65:00"),
+        {address_v4::from_string("0.0.0.0")},
+        {address_v6::from_string("::")},
+        address_v4(),
+        IFF_UP});
+    fakeInterfaces->push_back(
+      NetworkInterfaceInfo {1, "eth0",
+        ethernet::Address::fromString("3e:15:c2:8b:65:00"),
+        {address_v4::from_string("192.168.2.1"), address_v4::from_string("192.168.2.2")},
+        {},
+        address_v4::from_string("192.168.2.255"),
+        0});
+    fakeInterfaces->push_back(
+      NetworkInterfaceInfo {2, "eth1",
+        ethernet::Address::fromString("3e:15:c2:8b:65:00"),
+        {address_v4::from_string("198.51.100.1")},
+        {address_v6::from_string("2001:db8::2"), address_v6::from_string("2001:db8::3")},
+        address_v4::from_string("198.51.100.255"),
+        IFF_MULTICAST | IFF_BROADCAST | IFF_UP});
+
+    setDebugNetworkInterfaces(fakeInterfaces);
+  }
+
+  ~FakeNetworkInterfaceFixture()
+  {
+    setDebugNetworkInterfaces(nullptr);
+  }
+};
+
+BOOST_FIXTURE_TEST_CASE(Bug2292, FakeNetworkInterfaceFixture)
+{
+  using namespace boost::asio::ip;
+
+  UdpFactory factory;
+  factory.prohibitEndpoint(udp::Endpoint(address_v4::from_string("192.168.2.1"), 1024));
+  BOOST_REQUIRE_EQUAL(factory.m_prohibitedEndpoints.size(), 1);
+  BOOST_CHECK((factory.m_prohibitedEndpoints ==
+               std::set<udp::Endpoint> {
+                 udp::Endpoint(address_v4::from_string("192.168.2.1"), 1024),
+               }));
+
+  factory.m_prohibitedEndpoints.clear();
+  factory.prohibitEndpoint(udp::Endpoint(address_v6::from_string("2001:db8::1"), 2048));
+  BOOST_REQUIRE_EQUAL(factory.m_prohibitedEndpoints.size(), 1);
+  BOOST_CHECK((factory.m_prohibitedEndpoints ==
+               std::set<udp::Endpoint> {
+                 udp::Endpoint(address_v6::from_string("2001:db8::1"), 2048),
+               }));
+
+  factory.m_prohibitedEndpoints.clear();
+  factory.prohibitEndpoint(udp::Endpoint(address_v4(), 1024));
+  BOOST_REQUIRE_EQUAL(factory.m_prohibitedEndpoints.size(), 6);
+  BOOST_CHECK((factory.m_prohibitedEndpoints ==
+               std::set<udp::Endpoint> {
+                 udp::Endpoint(address_v4::from_string("192.168.2.1"), 1024),
+                 udp::Endpoint(address_v4::from_string("192.168.2.2"), 1024),
+                 udp::Endpoint(address_v4::from_string("198.51.100.1"), 1024),
+                 udp::Endpoint(address_v4::from_string("198.51.100.255"), 1024),
+                 udp::Endpoint(address_v4::from_string("255.255.255.255"), 1024),
+                 udp::Endpoint(address_v4::from_string("0.0.0.0"), 1024)
+               }));
+
+  factory.m_prohibitedEndpoints.clear();
+  factory.prohibitEndpoint(udp::Endpoint(address_v6(), 2048));
+  BOOST_REQUIRE_EQUAL(factory.m_prohibitedEndpoints.size(), 3);
+  BOOST_CHECK((factory.m_prohibitedEndpoints ==
+               std::set<udp::Endpoint> {
+                 udp::Endpoint(address_v6::from_string("2001:db8::2"), 2048),
+                 udp::Endpoint(address_v6::from_string("2001:db8::3"), 2048),
+                 udp::Endpoint(address_v6::from_string("::"), 2048),
+               }));
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/NFD/tests/daemon/face/unix-stream.cpp b/NFD/tests/daemon/face/unix-stream.cpp
new file mode 100644
index 0000000..24154ec
--- /dev/null
+++ b/NFD/tests/daemon/face/unix-stream.cpp
@@ -0,0 +1,505 @@
+/* -*- 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/>.
+ */
+
+#include "face/unix-stream-factory.hpp"
+
+#include "tests/test-common.hpp"
+#include "tests/limited-io.hpp"
+#include "dummy-stream-sender.hpp"
+#include "packet-datasets.hpp"
+
+namespace nfd {
+namespace tests {
+
+using namespace boost::asio::local;
+
+#define CHANNEL_PATH1 "unix-stream-test.1.sock"
+#define CHANNEL_PATH2 "unix-stream-test.2.sock"
+
+BOOST_FIXTURE_TEST_SUITE(FaceUnixStream, BaseFixture)
+
+BOOST_AUTO_TEST_CASE(ChannelMap)
+{
+  UnixStreamFactory factory;
+
+  shared_ptr<UnixStreamChannel> channel1 = factory.createChannel(CHANNEL_PATH1);
+  shared_ptr<UnixStreamChannel> channel1a = factory.createChannel(CHANNEL_PATH1);
+  BOOST_CHECK_EQUAL(channel1, channel1a);
+  std::string channel1uri = channel1->getUri().toString();
+  BOOST_CHECK_EQUAL(channel1uri.find("unix:///"), 0); // third '/' is the path separator
+  BOOST_CHECK_EQUAL(channel1uri.rfind(CHANNEL_PATH1),
+                    channel1uri.size() - std::string(CHANNEL_PATH1).size());
+
+  shared_ptr<UnixStreamChannel> channel2 = factory.createChannel(CHANNEL_PATH2);
+  BOOST_CHECK_NE(channel1, channel2);
+}
+
+BOOST_AUTO_TEST_CASE(GetChannels)
+{
+  UnixStreamFactory factory;
+  BOOST_REQUIRE_EQUAL(factory.getChannels().empty(), true);
+
+  std::vector<shared_ptr<const Channel> > expectedChannels;
+
+  expectedChannels.push_back(factory.createChannel(CHANNEL_PATH1));
+  expectedChannels.push_back(factory.createChannel(CHANNEL_PATH2));
+
+  std::list<shared_ptr<const Channel> > channels = factory.getChannels();
+  for (std::list<shared_ptr<const Channel> >::const_iterator i = channels.begin();
+       i != channels.end(); ++i)
+    {
+      std::vector<shared_ptr<const Channel> >::iterator pos =
+        std::find(expectedChannels.begin(), expectedChannels.end(), *i);
+
+      BOOST_REQUIRE(pos != expectedChannels.end());
+      expectedChannels.erase(pos);
+    }
+
+  BOOST_CHECK_EQUAL(expectedChannels.size(), 0);
+}
+
+class EndToEndFixture : protected BaseFixture
+{
+public:
+  void
+  client_onConnect(const boost::system::error_code& error)
+  {
+    BOOST_CHECK_MESSAGE(!error, error.message());
+
+    limitedIo.afterOp();
+  }
+
+  void
+  channel1_onFaceCreated(const shared_ptr<Face>& newFace)
+  {
+    BOOST_CHECK(!static_cast<bool>(face1));
+    face1 = static_pointer_cast<UnixStreamFace>(newFace);
+    face1->onReceiveInterest +=
+      bind(&EndToEndFixture::face1_onReceiveInterest, this, _1);
+    face1->onReceiveData +=
+      bind(&EndToEndFixture::face1_onReceiveData, this, _1);
+
+    limitedIo.afterOp();
+  }
+
+  void
+  channel1_onConnectFailed(const std::string& reason)
+  {
+    BOOST_CHECK_MESSAGE(false, reason);
+
+    limitedIo.afterOp();
+  }
+
+  void
+  face1_onReceiveInterest(const Interest& interest)
+  {
+    face1_receivedInterests.push_back(interest);
+
+    limitedIo.afterOp();
+  }
+
+  void
+  face1_onReceiveData(const Data& data)
+  {
+    face1_receivedDatas.push_back(data);
+
+    limitedIo.afterOp();
+  }
+
+  void
+  face2_onReceiveInterest(const Interest& interest)
+  {
+    face2_receivedInterests.push_back(interest);
+
+    limitedIo.afterOp();
+  }
+
+  void
+  face2_onReceiveData(const Data& data)
+  {
+    face2_receivedDatas.push_back(data);
+
+    limitedIo.afterOp();
+  }
+
+  void
+  channel_onFaceCreated(const shared_ptr<Face>& newFace)
+  {
+    faces.push_back(static_pointer_cast<UnixStreamFace>(newFace));
+
+    limitedIo.afterOp();
+  }
+
+  void
+  channel_onConnectFailed(const std::string& reason)
+  {
+    BOOST_CHECK_MESSAGE(false, reason);
+
+    limitedIo.afterOp();
+  }
+
+protected:
+  LimitedIo limitedIo;
+
+  shared_ptr<UnixStreamFace> face1;
+  std::vector<Interest> face1_receivedInterests;
+  std::vector<Data> face1_receivedDatas;
+  shared_ptr<UnixStreamFace> face2;
+  std::vector<Interest> face2_receivedInterests;
+  std::vector<Data> face2_receivedDatas;
+
+  std::list< shared_ptr<UnixStreamFace> > faces;
+};
+
+
+BOOST_FIXTURE_TEST_CASE(EndToEnd, EndToEndFixture)
+{
+  UnixStreamFactory factory;
+
+  shared_ptr<UnixStreamChannel> channel1 = factory.createChannel(CHANNEL_PATH1);
+  channel1->listen(bind(&EndToEndFixture::channel1_onFaceCreated,   this, _1),
+                   bind(&EndToEndFixture::channel1_onConnectFailed, this, _1));
+
+  shared_ptr<stream_protocol::socket> client = make_shared<stream_protocol::socket>(ref(g_io));
+  client->async_connect(stream_protocol::endpoint(CHANNEL_PATH1),
+                        bind(&EndToEndFixture::client_onConnect, this, _1));
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(2, time::seconds(1)) == LimitedIo::EXCEED_OPS,
+                      "UnixStreamChannel error: cannot connect or cannot accept connection");
+
+  BOOST_REQUIRE(static_cast<bool>(face1));
+
+  BOOST_CHECK_EQUAL(face1->getRemoteUri().getScheme(), "fd");
+  BOOST_CHECK_NO_THROW(boost::lexical_cast<int>(face1->getRemoteUri().getHost()));
+  std::string face1localUri = face1->getLocalUri().toString();
+  BOOST_CHECK_EQUAL(face1localUri.find("unix:///"), 0); // third '/' is the path separator
+  BOOST_CHECK_EQUAL(face1localUri.rfind(CHANNEL_PATH1),
+                    face1localUri.size() - std::string(CHANNEL_PATH1).size());
+
+  face2 = make_shared<UnixStreamFace>(client);
+  face2->onReceiveInterest +=
+    bind(&EndToEndFixture::face2_onReceiveInterest, this, _1);
+  face2->onReceiveData +=
+    bind(&EndToEndFixture::face2_onReceiveData, this, _1);
+
+  shared_ptr<Interest> interest1 = makeInterest("ndn:/TpnzGvW9R");
+  shared_ptr<Data>     data1     = makeData("ndn:/KfczhUqVix");
+  shared_ptr<Interest> interest2 = makeInterest("ndn:/QWiIMfj5sL");
+  shared_ptr<Data>     data2     = makeData("ndn:/XNBV796f");
+
+  face1->sendInterest(*interest1);
+  face1->sendInterest(*interest1);
+  face1->sendInterest(*interest1);
+  face1->sendData    (*data1    );
+  size_t nBytesSent1 = interest1->wireEncode().size() * 3 + data1->wireEncode().size();
+  face2->sendInterest(*interest2);
+  face2->sendData    (*data2    );
+  face2->sendData    (*data2    );
+  face2->sendData    (*data2    );
+  size_t nBytesSent2 = interest2->wireEncode().size() + data2->wireEncode().size() * 3;
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(8, time::seconds(1)) == LimitedIo::EXCEED_OPS,
+                      "UnixStreamChannel error: cannot send or receive Interest/Data packets");
+
+  BOOST_REQUIRE_EQUAL(face1_receivedInterests.size(), 1);
+  BOOST_REQUIRE_EQUAL(face1_receivedDatas    .size(), 3);
+  BOOST_REQUIRE_EQUAL(face2_receivedInterests.size(), 3);
+  BOOST_REQUIRE_EQUAL(face2_receivedDatas    .size(), 1);
+
+  BOOST_CHECK_EQUAL(face1_receivedInterests[0].getName(), interest2->getName());
+  BOOST_CHECK_EQUAL(face1_receivedDatas    [0].getName(), data2->getName());
+  BOOST_CHECK_EQUAL(face2_receivedInterests[0].getName(), interest1->getName());
+  BOOST_CHECK_EQUAL(face2_receivedDatas    [0].getName(), data1->getName());
+
+  // needed to ensure NOutBytes counters are accurate
+  limitedIo.run(LimitedIo::UNLIMITED_OPS, time::seconds(1));
+
+  const FaceCounters& counters1 = face1->getCounters();
+  BOOST_CHECK_EQUAL(counters1.getNInInterests() , 1);
+  BOOST_CHECK_EQUAL(counters1.getNInDatas()     , 3);
+  BOOST_CHECK_EQUAL(counters1.getNOutInterests(), 3);
+  BOOST_CHECK_EQUAL(counters1.getNOutDatas()    , 1);
+  BOOST_CHECK_EQUAL(counters1.getNInBytes(), nBytesSent2);
+  BOOST_CHECK_EQUAL(counters1.getNOutBytes(), nBytesSent1);
+
+  const FaceCounters& counters2 = face2->getCounters();
+  BOOST_CHECK_EQUAL(counters2.getNInInterests() , 3);
+  BOOST_CHECK_EQUAL(counters2.getNInDatas()     , 1);
+  BOOST_CHECK_EQUAL(counters2.getNOutInterests(), 1);
+  BOOST_CHECK_EQUAL(counters2.getNOutDatas()    , 3);
+}
+
+BOOST_FIXTURE_TEST_CASE(MultipleAccepts, EndToEndFixture)
+{
+  UnixStreamFactory factory;
+
+  shared_ptr<UnixStreamChannel> channel = factory.createChannel(CHANNEL_PATH1);
+  channel->listen(bind(&EndToEndFixture::channel_onFaceCreated,   this, _1),
+                  bind(&EndToEndFixture::channel_onConnectFailed, this, _1));
+
+  shared_ptr<stream_protocol::socket> client1 = make_shared<stream_protocol::socket>(ref(g_io));
+  client1->async_connect(stream_protocol::endpoint(CHANNEL_PATH1),
+                         bind(&EndToEndFixture::client_onConnect, this, _1));
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(2, time::seconds(1)) == LimitedIo::EXCEED_OPS,
+                      "UnixStreamChannel error: cannot connect or cannot accept connection");
+
+  BOOST_CHECK_EQUAL(faces.size(), 1);
+
+  shared_ptr<stream_protocol::socket> client2 = make_shared<stream_protocol::socket>(ref(g_io));
+  client2->async_connect(stream_protocol::endpoint(CHANNEL_PATH1),
+                         bind(&EndToEndFixture::client_onConnect, this, _1));
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(2, time::seconds(1)) == LimitedIo::EXCEED_OPS,
+                      "UnixStreamChannel error: cannot accept multiple connections");
+
+  BOOST_CHECK_EQUAL(faces.size(), 2);
+
+  // now close one of the faces
+  faces.front()->close();
+
+  // we should still be able to send/receive with the other one
+  face1 = faces.back();
+  face1->onReceiveInterest += bind(&EndToEndFixture::face1_onReceiveInterest, this, _1);
+  face1->onReceiveData += bind(&EndToEndFixture::face1_onReceiveData, this, _1);
+
+  face2 = make_shared<UnixStreamFace>(client2);
+  face2->onReceiveInterest += bind(&EndToEndFixture::face2_onReceiveInterest, this, _1);
+  face2->onReceiveData += bind(&EndToEndFixture::face2_onReceiveData, this, _1);
+
+  shared_ptr<Interest> interest1 = makeInterest("ndn:/TpnzGvW9R");
+  shared_ptr<Data>     data1     = makeData("ndn:/KfczhUqVix");
+  shared_ptr<Interest> interest2 = makeInterest("ndn:/QWiIMfj5sL");
+  shared_ptr<Data>     data2     = makeData("ndn:/XNBV796f");
+
+  face1->sendInterest(*interest1);
+  face1->sendData    (*data1    );
+  face2->sendInterest(*interest2);
+  face2->sendData    (*data2    );
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(4, time::seconds(1)) == LimitedIo::EXCEED_OPS,
+                      "UnixStreamChannel error: cannot send or receive Interest/Data packets");
+
+  BOOST_REQUIRE_EQUAL(face1_receivedInterests.size(), 1);
+  BOOST_REQUIRE_EQUAL(face1_receivedDatas    .size(), 1);
+  BOOST_REQUIRE_EQUAL(face2_receivedInterests.size(), 1);
+  BOOST_REQUIRE_EQUAL(face2_receivedDatas    .size(), 1);
+
+  BOOST_CHECK_EQUAL(face1_receivedInterests[0].getName(), interest2->getName());
+  BOOST_CHECK_EQUAL(face1_receivedDatas    [0].getName(), data2->getName());
+  BOOST_CHECK_EQUAL(face2_receivedInterests[0].getName(), interest1->getName());
+  BOOST_CHECK_EQUAL(face2_receivedDatas    [0].getName(), data1->getName());
+}
+
+static inline void
+noOp()
+{
+}
+
+BOOST_FIXTURE_TEST_CASE(UnixStreamFaceLocalControlHeader, EndToEndFixture)
+{
+  UnixStreamFactory factory;
+
+  shared_ptr<UnixStreamChannel> channel1 = factory.createChannel(CHANNEL_PATH1);
+  channel1->listen(bind(&EndToEndFixture::channel1_onFaceCreated,   this, _1),
+                   bind(&EndToEndFixture::channel1_onConnectFailed, this, _1));
+
+  shared_ptr<stream_protocol::socket> client = make_shared<stream_protocol::socket>(ref(g_io));
+  client->async_connect(stream_protocol::endpoint(CHANNEL_PATH1),
+                        bind(&EndToEndFixture::client_onConnect, this, _1));
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(2, time::seconds(1)) == LimitedIo::EXCEED_OPS,
+                      "UnixStreamChannel error: cannot connect or cannot accept connection");
+
+  BOOST_REQUIRE(static_cast<bool>(face1));
+
+  face2 = make_shared<UnixStreamFace>(client);
+  face2->onReceiveInterest +=
+    bind(&EndToEndFixture::face2_onReceiveInterest, this, _1);
+  face2->onReceiveData +=
+    bind(&EndToEndFixture::face2_onReceiveData, this, _1);
+
+  shared_ptr<Interest> interest1 = makeInterest("ndn:/TpnzGvW9R");
+  shared_ptr<Data>     data1     = makeData("ndn:/KfczhUqVix");
+
+  face1->setLocalControlHeaderFeature(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID);
+  face1->setLocalControlHeaderFeature(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID);
+
+  BOOST_CHECK(face1->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID));
+  BOOST_CHECK(face1->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID));
+
+  face2->setLocalControlHeaderFeature(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID);
+  face2->setLocalControlHeaderFeature(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID);
+
+  BOOST_CHECK(face2->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID));
+  BOOST_CHECK(face2->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID));
+
+  ////////////////////////////////////////////////////////
+
+  interest1->setIncomingFaceId(11);
+  interest1->setNextHopFaceId(111);
+
+  face1->sendInterest(*interest1);
+
+  data1->setIncomingFaceId(22);
+  data1->getLocalControlHeader().setNextHopFaceId(222);
+
+  face1->sendData(*data1);
+
+  //
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(2, time::seconds(1)) == LimitedIo::EXCEED_OPS,
+                      "UnixStreamChannel error: cannot send or receive Interest/Data packets");
+
+  BOOST_REQUIRE_EQUAL(face2_receivedInterests.size(), 1);
+  BOOST_REQUIRE_EQUAL(face2_receivedDatas    .size(), 1);
+
+  // sending allows only IncomingFaceId, receiving allows only NextHopFaceId
+  BOOST_CHECK_EQUAL(face2_receivedInterests[0].getLocalControlHeader().hasIncomingFaceId(), false);
+  BOOST_CHECK_EQUAL(face2_receivedInterests[0].getLocalControlHeader().hasNextHopFaceId(), false);
+
+  BOOST_CHECK_EQUAL(face2_receivedDatas[0].getLocalControlHeader().hasIncomingFaceId(), false);
+  BOOST_CHECK_EQUAL(face2_receivedDatas[0].getLocalControlHeader().hasNextHopFaceId(), false);
+
+  ////////////////////////////////////////////////////////
+
+  using namespace boost::asio;
+
+  std::vector<const_buffer> interestWithHeader;
+  Block iHeader  = interest1->getLocalControlHeader().wireEncode(*interest1, true, true);
+  Block iPayload = interest1->wireEncode();
+  interestWithHeader.push_back(buffer(iHeader.wire(),  iHeader.size()));
+  interestWithHeader.push_back(buffer(iPayload.wire(), iPayload.size()));
+
+  std::vector<const_buffer> dataWithHeader;
+  Block dHeader  = data1->getLocalControlHeader().wireEncode(*data1, true, true);
+  Block dPayload = data1->wireEncode();
+  dataWithHeader.push_back(buffer(dHeader.wire(),  dHeader.size()));
+  dataWithHeader.push_back(buffer(dPayload.wire(), dPayload.size()));
+
+  //
+
+  client->async_send(interestWithHeader, bind(&noOp));
+  client->async_send(dataWithHeader, bind(&noOp));
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(2, time::seconds(1)) == LimitedIo::EXCEED_OPS,
+                      "UnixStreamChannel error: cannot send or receive Interest/Data packets");
+
+  BOOST_REQUIRE_EQUAL(face1_receivedInterests.size(), 1);
+  BOOST_REQUIRE_EQUAL(face1_receivedDatas    .size(), 1);
+
+  BOOST_CHECK_EQUAL(face1_receivedInterests[0].getLocalControlHeader().hasIncomingFaceId(), false);
+  BOOST_CHECK_EQUAL(face1_receivedInterests[0].getLocalControlHeader().hasNextHopFaceId(), true);
+  BOOST_CHECK_EQUAL(face1_receivedInterests[0].getNextHopFaceId(), 111);
+
+  BOOST_CHECK_EQUAL(face1_receivedDatas[0].getLocalControlHeader().hasIncomingFaceId(), false);
+  BOOST_CHECK_EQUAL(face1_receivedDatas[0].getLocalControlHeader().hasNextHopFaceId(), false);
+}
+
+
+class SimpleEndToEndFixture : protected BaseFixture
+{
+public:
+  void
+  onFaceCreated(const shared_ptr<Face>& face)
+  {
+    face->onReceiveInterest +=
+      bind(&SimpleEndToEndFixture::onReceiveInterest, this, _1);
+    face->onReceiveData +=
+      bind(&SimpleEndToEndFixture::onReceiveData, this, _1);
+    face->onFail +=
+      bind(&SimpleEndToEndFixture::onFail, this, face);
+
+    if (static_cast<bool>(dynamic_pointer_cast<LocalFace>(face))) {
+      static_pointer_cast<LocalFace>(face)->setLocalControlHeaderFeature(
+        LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID);
+
+      static_pointer_cast<LocalFace>(face)->setLocalControlHeaderFeature(
+        LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID);
+    }
+
+    limitedIo.afterOp();
+  }
+
+  void
+  onConnectFailed(const std::string& reason)
+  {
+    BOOST_CHECK_MESSAGE(false, reason);
+
+    limitedIo.afterOp();
+  }
+
+  void
+  onReceiveInterest(const Interest& interest)
+  {
+    receivedInterests.push_back(interest);
+
+    limitedIo.afterOp();
+  }
+
+  void
+  onReceiveData(const Data& data)
+  {
+    receivedDatas.push_back(data);
+
+    limitedIo.afterOp();
+  }
+
+  void
+  onFail(const shared_ptr<Face>& face)
+  {
+    limitedIo.afterOp();
+  }
+
+public:
+  LimitedIo limitedIo;
+
+  std::vector<Interest> receivedInterests;
+  std::vector<Data> receivedDatas;
+};
+
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(CorruptedInput, Dataset,
+                                 CorruptedPackets, SimpleEndToEndFixture)
+{
+  UnixStreamFactory factory;
+
+  shared_ptr<UnixStreamChannel> channel = factory.createChannel(CHANNEL_PATH1);
+  channel->listen(bind(&SimpleEndToEndFixture::onFaceCreated,   this, _1),
+                  bind(&SimpleEndToEndFixture::onConnectFailed, this, _1));
+
+  DummyStreamSender<stream_protocol, Dataset> sender;
+  sender.start(stream_protocol::endpoint(CHANNEL_PATH1));
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(LimitedIo::UNLIMITED_OPS,
+                                    time::seconds(1)) == LimitedIo::EXCEED_TIME,
+                      "Exception thrown for " + Dataset::getName());
+}
+
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/NFD/tests/daemon/face/websocket.cpp b/NFD/tests/daemon/face/websocket.cpp
new file mode 100644
index 0000000..fa93b6a
--- /dev/null
+++ b/NFD/tests/daemon/face/websocket.cpp
@@ -0,0 +1,293 @@
+/* -*- 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/>.
+ */
+
+#include "face/websocket-factory.hpp"
+#include "tests/test-common.hpp"
+#include "tests/limited-io.hpp"
+
+#include <websocketpp/config/asio_no_tls_client.hpp>
+#include <websocketpp/client.hpp>
+
+typedef websocketpp::client<websocketpp::config::asio_client> Client;
+
+namespace nfd {
+namespace tests {
+
+BOOST_FIXTURE_TEST_SUITE(FaceWebSocket, BaseFixture)
+
+BOOST_AUTO_TEST_CASE(GetChannels)
+{
+  WebSocketFactory factory("19596");
+  BOOST_REQUIRE_EQUAL(factory.getChannels().empty(), true);
+
+  std::vector<shared_ptr<const Channel> > expectedChannels;
+
+  expectedChannels.push_back(factory.createChannel("127.0.0.1", "20070"));
+  expectedChannels.push_back(factory.createChannel("127.0.0.1", "20071"));
+  expectedChannels.push_back(factory.createChannel("::1", "20071"));
+
+  std::list<shared_ptr<const Channel> > channels = factory.getChannels();
+  for (std::list<shared_ptr<const Channel> >::const_iterator i = channels.begin();
+       i != channels.end(); ++i)
+    {
+      std::vector<shared_ptr<const Channel> >::iterator pos =
+        std::find(expectedChannels.begin(), expectedChannels.end(), *i);
+
+      BOOST_REQUIRE(pos != expectedChannels.end());
+      expectedChannels.erase(pos);
+    }
+
+  BOOST_CHECK_EQUAL(expectedChannels.size(), 0);
+}
+
+class EndToEndFixture : protected BaseFixture
+{
+public:
+  void
+  channel1_onFaceCreated(const shared_ptr<Face>& newFace)
+  {
+    BOOST_CHECK(!static_cast<bool>(face1));
+    face1 = newFace;
+    face1->onReceiveInterest +=
+      bind(&EndToEndFixture::face1_onReceiveInterest, this, _1);
+    face1->onReceiveData +=
+      bind(&EndToEndFixture::face1_onReceiveData, this, _1);
+    face1->onFail +=
+      bind(&EndToEndFixture::face1_onFail, this);
+
+    limitedIo.afterOp();
+  }
+
+  void
+  face1_onReceiveInterest(const Interest& interest)
+  {
+    face1_receivedInterests.push_back(interest);
+    limitedIo.afterOp();
+  }
+
+  void
+  face1_onReceiveData(const Data& data)
+  {
+    face1_receivedDatas.push_back(data);
+    limitedIo.afterOp();
+  }
+
+  void
+  face1_onFail()
+  {
+    face1.reset();
+    limitedIo.afterOp();
+  }
+
+  void
+  client1_onOpen(websocketpp::connection_hdl hdl)
+  {
+    handle = hdl;
+    limitedIo.afterOp();
+  }
+
+  void
+  client1_onClose(websocketpp::connection_hdl hdl)
+  {
+    limitedIo.afterOp();
+  }
+
+  void
+  client1_onFail(websocketpp::connection_hdl hdl)
+  {
+    limitedIo.afterOp();
+  }
+
+  bool
+  client1_onPing(websocketpp::connection_hdl hdl, std::string msg)
+  {
+    limitedIo.afterOp();
+    // Return false to suppress the pong response,
+    // which will cause timeout in the websocket channel
+    return false;
+  }
+
+  void
+  client1_sendInterest(const Interest& interest)
+  {
+    const Block& payload = interest.wireEncode();
+    client1.send(handle, payload.wire(), payload.size(), websocketpp::frame::opcode::binary);
+  }
+
+  void
+  client1_sendData(const Data& data)
+  {
+    const Block& payload = data.wireEncode();
+    client1.send(handle, payload.wire(), payload.size(), websocketpp::frame::opcode::binary);
+  }
+
+  void
+  client1_onMessage(websocketpp::connection_hdl hdl,
+                    websocketpp::config::asio_client::message_type::ptr msg)
+  {
+    bool isOk = true;
+    Block element;
+    const std::string& payload = msg->get_payload();
+    isOk = Block::fromBuffer(reinterpret_cast<const uint8_t*>(payload.c_str()),
+                             payload.size(), element);
+    if (isOk)
+      {
+        try {
+          if (element.type() == tlv::Interest)
+            {
+              shared_ptr<Interest> i = make_shared<Interest>();
+              i->wireDecode(element);
+              client1_onReceiveInterest(*i);
+            }
+          else if (element.type() == tlv::Data)
+            {
+              shared_ptr<Data> d = make_shared<Data>();
+              d->wireDecode(element);
+              client1_onReceiveData(*d);
+            }
+        }
+        catch (tlv::Error&) {
+          // Do something?
+        }
+      }
+    limitedIo.afterOp();
+  }
+
+  void
+  client1_onReceiveInterest(const Interest& interest)
+  {
+    client1_receivedInterests.push_back(interest);
+    limitedIo.afterOp();
+  }
+
+  void
+  client1_onReceiveData(const Data& data)
+  {
+    client1_receivedDatas.push_back(data);
+    limitedIo.afterOp();
+  }
+
+public:
+  LimitedIo limitedIo;
+
+  shared_ptr<Face> face1;
+  std::vector<Interest> face1_receivedInterests;
+  std::vector<Data> face1_receivedDatas;
+  Client client1;
+  websocketpp::connection_hdl handle;
+  std::vector<Interest> client1_receivedInterests;
+  std::vector<Data> client1_receivedDatas;
+};
+
+BOOST_FIXTURE_TEST_CASE(EndToEnd4, EndToEndFixture)
+{
+  WebSocketFactory factory1("9696");
+
+  shared_ptr<WebSocketChannel> channel1 = factory1.createChannel("127.0.0.1", "20070");
+  channel1->setPingInterval(time::milliseconds(3000));
+  channel1->setPongTimeout(time::milliseconds(1000));
+
+  BOOST_CHECK_EQUAL(channel1->isListening(), false);
+
+  channel1->listen(bind(&EndToEndFixture::channel1_onFaceCreated,   this, _1));
+
+  BOOST_CHECK_EQUAL(channel1->isListening(), true);
+
+  // Clear all logging info from websocketpp library
+  client1.clear_access_channels(websocketpp::log::alevel::all);
+
+  client1.init_asio(&getGlobalIoService());
+  client1.set_open_handler(bind(&EndToEndFixture::client1_onOpen,   this, _1));
+  client1.set_close_handler(bind(&EndToEndFixture::client1_onClose, this, _1));
+  client1.set_fail_handler(bind(&EndToEndFixture::client1_onFail,   this, _1));
+  client1.set_message_handler(bind(&EndToEndFixture::client1_onMessage, this, _1, _2));
+  client1.set_ping_handler(bind(&EndToEndFixture::client1_onPing, this, _1, _2));
+
+  websocketpp::lib::error_code ec;
+  Client::connection_ptr con = client1.get_connection("ws://127.0.0.1:20070", ec);
+  client1.connect(con);
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(2, time::seconds(10)) == LimitedIo::EXCEED_OPS,
+                      "WebSocketChannel error: cannot connect or cannot accept connection");
+
+  BOOST_CHECK_EQUAL(channel1->size(), 1);
+
+  BOOST_CHECK_EQUAL(face1->getLocalUri().toString(), "ws://127.0.0.1:20070");
+  BOOST_CHECK_EQUAL(face1->isOnDemand(), true);
+
+  //BOOST_CHECK_EQUAL(face1->isLocal(), true);
+
+  //BOOST_CHECK_EQUAL(static_cast<bool>(dynamic_pointer_cast<LocalFace>(face1)), false);
+
+  shared_ptr<Interest> interest1 = makeInterest("ndn:/TpnzGvW9R");
+  shared_ptr<Data>     data1     = makeData("ndn:/KfczhUqVix");
+  shared_ptr<Interest> interest2 = makeInterest("ndn:/QWiIMfj5sL");
+  shared_ptr<Data>     data2     = makeData("ndn:/XNBV796f");
+
+  std::string bigName("ndn:/");
+  bigName.append(9000, 'a');
+  shared_ptr<Interest> bigInterest = makeInterest(bigName);
+
+  client1_sendInterest(*interest1);
+  client1_sendInterest(*interest1);
+  client1_sendInterest(*interest1);
+  client1_sendInterest(*bigInterest);  // This one should be ignored by face1
+  face1->sendData     (*data1);
+  face1->sendInterest (*interest2);
+  client1_sendData    (*data2);
+  client1_sendData    (*data2);
+  client1_sendData    (*data2);
+  size_t nBytesSent = data1->wireEncode().size() + interest2->wireEncode().size();
+  size_t nBytesReceived = interest1->wireEncode().size() * 3 + data2->wireEncode().size() * 3;
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(8, time::seconds(10)) == LimitedIo::EXCEED_OPS,
+                      "WebSocketChannel error: cannot send or receive Interest/Data packets");
+
+  BOOST_REQUIRE_EQUAL(face1_receivedInterests.size(), 3);
+  BOOST_REQUIRE_EQUAL(face1_receivedDatas    .size(), 3);
+  BOOST_REQUIRE_EQUAL(client1_receivedInterests.size(), 1);
+  BOOST_REQUIRE_EQUAL(client1_receivedDatas    .size(), 1);
+
+  BOOST_CHECK_EQUAL(face1_receivedInterests[0].getName(), interest1->getName());
+  BOOST_CHECK_EQUAL(face1_receivedDatas    [0].getName(), data2->getName());
+  BOOST_CHECK_EQUAL(client1_receivedInterests[0].getName(), interest2->getName());
+  BOOST_CHECK_EQUAL(client1_receivedDatas    [0].getName(), data1->getName());
+
+  const FaceCounters& counters1 = face1->getCounters();
+  BOOST_CHECK_EQUAL(counters1.getNInInterests() , 3);
+  BOOST_CHECK_EQUAL(counters1.getNInDatas()     , 3);
+  BOOST_CHECK_EQUAL(counters1.getNOutInterests(), 1);
+  BOOST_CHECK_EQUAL(counters1.getNOutDatas()    , 1);
+  BOOST_CHECK_EQUAL(counters1.getNInBytes(), nBytesReceived);
+  BOOST_CHECK_EQUAL(counters1.getNOutBytes(), nBytesSent);
+
+  limitedIo.run(LimitedIo::UNLIMITED_OPS, time::seconds(8));
+  BOOST_CHECK_EQUAL(channel1->size(), 0);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/NFD/tests/daemon/fw/best-route-strategy2.cpp b/NFD/tests/daemon/fw/best-route-strategy2.cpp
new file mode 100644
index 0000000..d956e9f
--- /dev/null
+++ b/NFD/tests/daemon/fw/best-route-strategy2.cpp
@@ -0,0 +1,132 @@
+/* -*- 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/>.
+ */
+
+#include "fw/best-route-strategy2.hpp"
+#include "strategy-tester.hpp"
+
+#include "tests/test-common.hpp"
+#include "tests/daemon/face/dummy-face.hpp"
+
+namespace nfd {
+namespace tests {
+
+BOOST_FIXTURE_TEST_SUITE(FwBestRouteStrategy2, UnitTestTimeFixture)
+
+BOOST_AUTO_TEST_CASE(Forward)
+{
+  Forwarder forwarder;
+  typedef StrategyTester<fw::BestRouteStrategy2> BestRouteStrategy2Tester;
+  BestRouteStrategy2Tester strategy(forwarder);
+
+  shared_ptr<DummyFace> face1 = make_shared<DummyFace>();
+  shared_ptr<DummyFace> face2 = make_shared<DummyFace>();
+  shared_ptr<DummyFace> face3 = make_shared<DummyFace>();
+  shared_ptr<DummyFace> face4 = make_shared<DummyFace>();
+  shared_ptr<DummyFace> face5 = make_shared<DummyFace>();
+  forwarder.addFace(face1);
+  forwarder.addFace(face2);
+  forwarder.addFace(face3);
+  forwarder.addFace(face4);
+  forwarder.addFace(face5);
+
+  Fib& fib = forwarder.getFib();
+  shared_ptr<fib::Entry> fibEntry = fib.insert(Name()).first;
+  fibEntry->addNextHop(face1, 10);
+  fibEntry->addNextHop(face2, 20);
+  fibEntry->addNextHop(face3, 30);
+
+  shared_ptr<Interest> interest = makeInterest("ndn:/BzgFBchqA");
+  Pit& pit = forwarder.getPit();
+  shared_ptr<pit::Entry> pitEntry = pit.insert(*interest).first;
+
+  const time::nanoseconds TICK = time::duration_cast<time::nanoseconds>(
+    fw::BestRouteStrategy2::MIN_RETRANSMISSION_INTERVAL * 0.01);
+  const time::nanoseconds RETRANSMISSION_10P = time::duration_cast<time::nanoseconds>(
+    fw::BestRouteStrategy2::MIN_RETRANSMISSION_INTERVAL * 0.1); // 10%
+  const time::nanoseconds RETRANSMISSION_70P = time::duration_cast<time::nanoseconds>(
+    fw::BestRouteStrategy2::MIN_RETRANSMISSION_INTERVAL * 0.7); // 70%
+  const time::nanoseconds RETRANSMISSION_2 = time::duration_cast<time::nanoseconds>(
+    fw::BestRouteStrategy2::MIN_RETRANSMISSION_INTERVAL * 2.0); // x2
+
+  // first Interest goes to nexthop with lowest FIB cost,
+  // however face1 is downstream so it cannot be used
+  pitEntry->insertOrUpdateInRecord(face1, *interest);
+  strategy.afterReceiveInterest(*face1, *interest, fibEntry, pitEntry);
+  BOOST_REQUIRE_EQUAL(strategy.m_sendInterestHistory.size(), 1);
+  BOOST_CHECK_EQUAL(strategy.m_sendInterestHistory.back().get<1>(), face2);
+
+  // downstream retransmits frequently, but the strategy should not send Interests
+  // more often than MIN_RETRANSMISSION_INTERVAL
+  scheduler::EventId retxFrom4Evt;
+  size_t nSentLast = strategy.m_sendInterestHistory.size();
+  time::steady_clock::TimePoint timeSentLast = time::steady_clock::now();
+  function<void()> periodicalRetxFrom4; // let periodicalRetxFrom4 lambda capture itself
+  periodicalRetxFrom4 = [&] {
+    pitEntry->insertOrUpdateInRecord(face4, *interest);
+    strategy.afterReceiveInterest(*face4, *interest, fibEntry, pitEntry);
+
+    size_t nSent = strategy.m_sendInterestHistory.size();
+    if (nSent > nSentLast) {
+      BOOST_CHECK_EQUAL(nSent - nSentLast, 1);
+      time::steady_clock::TimePoint timeSent = time::steady_clock::now();
+      BOOST_CHECK_GE(timeSent - timeSentLast, RETRANSMISSION_70P);
+      nSentLast = nSent;
+      timeSentLast = timeSent;
+    }
+
+    retxFrom4Evt = scheduler::schedule(RETRANSMISSION_10P, periodicalRetxFrom4);
+  };
+  periodicalRetxFrom4();
+  this->advanceClocks(TICK, fw::BestRouteStrategy2::MIN_RETRANSMISSION_INTERVAL * 16);
+  scheduler::cancel(retxFrom4Evt);
+
+  // nexthops for accepted retransmissions: follow FIB cost,
+  // later forward to an eligible upstream with earliest OutRecord
+  BOOST_REQUIRE_GE(strategy.m_sendInterestHistory.size(), 6);
+  BOOST_CHECK_EQUAL(strategy.m_sendInterestHistory[1].get<1>(), face1);
+  BOOST_CHECK_EQUAL(strategy.m_sendInterestHistory[2].get<1>(), face3);
+  BOOST_CHECK_EQUAL(strategy.m_sendInterestHistory[3].get<1>(), face2);
+  BOOST_CHECK_EQUAL(strategy.m_sendInterestHistory[4].get<1>(), face1);
+  BOOST_CHECK_EQUAL(strategy.m_sendInterestHistory[5].get<1>(), face3);
+
+  fibEntry->removeNextHop(face1);
+
+  strategy.m_sendInterestHistory.clear();
+  for (int i = 0; i < 3; ++i) {
+    this->advanceClocks(TICK, RETRANSMISSION_2);
+    pitEntry->insertOrUpdateInRecord(face5, *interest);
+    strategy.afterReceiveInterest(*face5, *interest, fibEntry, pitEntry);
+  }
+  BOOST_REQUIRE_EQUAL(strategy.m_sendInterestHistory.size(), 3);
+  BOOST_CHECK_NE(strategy.m_sendInterestHistory[0].get<1>(), face1);
+  BOOST_CHECK_NE(strategy.m_sendInterestHistory[1].get<1>(), face1);
+  BOOST_CHECK_NE(strategy.m_sendInterestHistory[2].get<1>(), face1);
+  // face1 cannot be used because it's gone from FIB entry
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/NFD/tests/daemon/fw/broadcast-strategy.cpp b/NFD/tests/daemon/fw/broadcast-strategy.cpp
new file mode 100644
index 0000000..cde9b44
--- /dev/null
+++ b/NFD/tests/daemon/fw/broadcast-strategy.cpp
@@ -0,0 +1,129 @@
+/* -*- 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
+ *
+ * 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 "fw/broadcast-strategy.hpp"
+#include "strategy-tester.hpp"
+#include "tests/daemon/face/dummy-face.hpp"
+
+#include "tests/test-common.hpp"
+
+namespace nfd {
+namespace tests {
+
+BOOST_FIXTURE_TEST_SUITE(FwBroadcastStrategy, BaseFixture)
+
+BOOST_AUTO_TEST_CASE(Forward2)
+{
+  Forwarder forwarder;
+  typedef StrategyTester<fw::BroadcastStrategy> BroadcastStrategyTester;
+  BroadcastStrategyTester strategy(forwarder);
+
+  shared_ptr<DummyFace> face1 = make_shared<DummyFace>();
+  shared_ptr<DummyFace> face2 = make_shared<DummyFace>();
+  shared_ptr<DummyFace> face3 = make_shared<DummyFace>();
+  forwarder.addFace(face1);
+  forwarder.addFace(face2);
+  forwarder.addFace(face3);
+
+  Fib& fib = forwarder.getFib();
+  shared_ptr<fib::Entry> fibEntry = fib.insert(Name()).first;
+  fibEntry->addNextHop(face1, 0);
+  fibEntry->addNextHop(face2, 0);
+  fibEntry->addNextHop(face3, 0);
+
+  shared_ptr<Interest> interest = makeInterest("ndn:/H0D6i5fc");
+  Pit& pit = forwarder.getPit();
+  shared_ptr<pit::Entry> pitEntry = pit.insert(*interest).first;
+  pitEntry->insertOrUpdateInRecord(face3, *interest);
+
+  strategy.afterReceiveInterest(*face3, *interest, fibEntry, pitEntry);
+  BOOST_CHECK_EQUAL(strategy.m_rejectPendingInterestHistory.size(), 0);
+  BOOST_CHECK_EQUAL(strategy.m_sendInterestHistory.size(), 2);
+  bool hasFace1 = false;
+  bool hasFace2 = false;
+  for (std::vector<BroadcastStrategyTester::SendInterestArgs>::iterator it =
+       strategy.m_sendInterestHistory.begin();
+       it != strategy.m_sendInterestHistory.end(); ++it) {
+    if (it->get<1>() == face1) {
+      hasFace1 = true;
+    }
+    if (it->get<1>() == face2) {
+      hasFace2 = true;
+    }
+  }
+  BOOST_CHECK(hasFace1 && hasFace2);
+}
+
+BOOST_AUTO_TEST_CASE(RejectScope)
+{
+  Forwarder forwarder;
+  typedef StrategyTester<fw::BroadcastStrategy> BroadcastStrategyTester;
+  BroadcastStrategyTester strategy(forwarder);
+
+  shared_ptr<DummyFace> face1 = make_shared<DummyFace>();
+  shared_ptr<DummyFace> face2 = make_shared<DummyFace>();
+  forwarder.addFace(face1);
+  forwarder.addFace(face2);
+
+  Fib& fib = forwarder.getFib();
+  shared_ptr<fib::Entry> fibEntry = fib.insert("ndn:/localhop/uS09bub6tm").first;
+  fibEntry->addNextHop(face2, 0);
+
+  shared_ptr<Interest> interest = makeInterest("ndn:/localhop/uS09bub6tm/eG3MMoP6z");
+  Pit& pit = forwarder.getPit();
+  shared_ptr<pit::Entry> pitEntry = pit.insert(*interest).first;
+  pitEntry->insertOrUpdateInRecord(face1, *interest);
+
+  strategy.afterReceiveInterest(*face1, *interest, fibEntry, pitEntry);
+  BOOST_CHECK_EQUAL(strategy.m_rejectPendingInterestHistory.size(), 1);
+  BOOST_CHECK_EQUAL(strategy.m_sendInterestHistory.size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(RejectLoopback)
+{
+  Forwarder forwarder;
+  typedef StrategyTester<fw::BroadcastStrategy> BroadcastStrategyTester;
+  BroadcastStrategyTester strategy(forwarder);
+
+  shared_ptr<DummyFace> face1 = make_shared<DummyFace>();
+  forwarder.addFace(face1);
+
+  Fib& fib = forwarder.getFib();
+  shared_ptr<fib::Entry> fibEntry = fib.insert(Name()).first;
+  fibEntry->addNextHop(face1, 0);
+
+  shared_ptr<Interest> interest = makeInterest("ndn:/H0D6i5fc");
+  Pit& pit = forwarder.getPit();
+  shared_ptr<pit::Entry> pitEntry = pit.insert(*interest).first;
+  pitEntry->insertOrUpdateInRecord(face1, *interest);
+
+  strategy.afterReceiveInterest(*face1, *interest, fibEntry, pitEntry);
+  BOOST_CHECK_EQUAL(strategy.m_rejectPendingInterestHistory.size(), 1);
+  BOOST_CHECK_EQUAL(strategy.m_sendInterestHistory.size(), 0);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/NFD/tests/daemon/fw/client-control-strategy.cpp b/NFD/tests/daemon/fw/client-control-strategy.cpp
new file mode 100644
index 0000000..c76fb52
--- /dev/null
+++ b/NFD/tests/daemon/fw/client-control-strategy.cpp
@@ -0,0 +1,96 @@
+/* -*- 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
+ *
+ * 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 "fw/client-control-strategy.hpp"
+#include "strategy-tester.hpp"
+#include "tests/daemon/face/dummy-face.hpp"
+
+#include "tests/test-common.hpp"
+
+namespace nfd {
+namespace tests {
+
+BOOST_FIXTURE_TEST_SUITE(FwClientControlStrategy, BaseFixture)
+
+BOOST_AUTO_TEST_CASE(Forward3)
+{
+  Forwarder forwarder;
+  typedef StrategyTester<fw::ClientControlStrategy> ClientControlStrategyTester;
+  ClientControlStrategyTester strategy(forwarder);
+
+  shared_ptr<DummyFace> face1 = make_shared<DummyFace>();
+  shared_ptr<DummyFace> face2 = make_shared<DummyFace>();
+  shared_ptr<DummyFace> face3 = make_shared<DummyFace>();
+  shared_ptr<DummyLocalFace> face4 = make_shared<DummyLocalFace>();
+  face4->setLocalControlHeaderFeature(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID);
+  forwarder.addFace(face1);
+  forwarder.addFace(face2);
+  forwarder.addFace(face3);
+  forwarder.addFace(face4);
+
+  Fib& fib = forwarder.getFib();
+  shared_ptr<fib::Entry> fibEntry = fib.insert(Name()).first;
+  fibEntry->addNextHop(face2, 0);
+
+  Pit& pit = forwarder.getPit();
+
+  // Interest with valid NextHopFaceId
+  shared_ptr<Interest> interest1 = makeInterest("ndn:/0z8r6yDDe");
+  interest1->setNextHopFaceId(face1->getId());
+  shared_ptr<pit::Entry> pitEntry1 = pit.insert(*interest1).first;
+  pitEntry1->insertOrUpdateInRecord(face4, *interest1);
+
+  strategy.m_sendInterestHistory.clear();
+  strategy.afterReceiveInterest(*face4, *interest1, fibEntry, pitEntry1);
+  BOOST_REQUIRE_EQUAL(strategy.m_sendInterestHistory.size(), 1);
+  BOOST_CHECK_EQUAL(strategy.m_sendInterestHistory[0].get<1>(), face1);
+
+  // Interest without NextHopFaceId
+  shared_ptr<Interest> interest2 = makeInterest("ndn:/y6JQADGVz");
+  shared_ptr<pit::Entry> pitEntry2 = pit.insert(*interest2).first;
+  pitEntry2->insertOrUpdateInRecord(face4, *interest2);
+
+  strategy.m_sendInterestHistory.clear();
+  strategy.afterReceiveInterest(*face4, *interest2, fibEntry, pitEntry2);
+  BOOST_REQUIRE_EQUAL(strategy.m_sendInterestHistory.size(), 1);
+  BOOST_CHECK_EQUAL(strategy.m_sendInterestHistory[0].get<1>(), face2);
+
+  // Interest with invalid NextHopFaceId
+  shared_ptr<Interest> interest3 = makeInterest("ndn:/0z8r6yDDe");
+  interest3->setNextHopFaceId(face3->getId());
+  shared_ptr<pit::Entry> pitEntry3 = pit.insert(*interest3).first;
+  pitEntry3->insertOrUpdateInRecord(face4, *interest3);
+
+  face3->close(); // face3 is closed and its FaceId becomes invalid
+  strategy.m_sendInterestHistory.clear();
+  strategy.m_rejectPendingInterestHistory.clear();
+  strategy.afterReceiveInterest(*face4, *interest3, fibEntry, pitEntry3);
+  BOOST_REQUIRE_EQUAL(strategy.m_sendInterestHistory.size(), 0);
+  BOOST_REQUIRE_EQUAL(strategy.m_rejectPendingInterestHistory.size(), 1);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/NFD/tests/daemon/fw/dummy-strategy.hpp b/NFD/tests/daemon/fw/dummy-strategy.hpp
new file mode 100644
index 0000000..50de93e
--- /dev/null
+++ b/NFD/tests/daemon/fw/dummy-strategy.hpp
@@ -0,0 +1,87 @@
+/* -*- 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_TESTS_DAEMON_FW_DUMMY_STRATEGY_HPP
+#define NFD_TESTS_DAEMON_FW_DUMMY_STRATEGY_HPP
+
+#include "fw/strategy.hpp"
+
+namespace nfd {
+namespace tests {
+
+/** \brief strategy for unit testing
+ *
+ *  Triggers on DummyStrategy are recorded but does nothing
+ */
+class DummyStrategy : public fw::Strategy
+{
+public:
+  DummyStrategy(Forwarder& forwarder, const Name& name)
+    : Strategy(forwarder, name)
+  {
+  }
+
+  virtual void
+  afterReceiveInterest(const Face& inFace,
+                       const Interest& interest,
+                       shared_ptr<fib::Entry> fibEntry,
+                       shared_ptr<pit::Entry> pitEntry) DECL_OVERRIDE
+  {
+    ++m_afterReceiveInterest_count;
+
+    if (static_cast<bool>(m_interestOutFace)) {
+      this->sendInterest(pitEntry, m_interestOutFace);
+    }
+    else {
+      this->rejectPendingInterest(pitEntry);
+    }
+  }
+
+  virtual void
+  beforeSatisfyInterest(shared_ptr<pit::Entry> pitEntry,
+                        const Face& inFace, const Data& data) DECL_OVERRIDE
+  {
+    ++m_beforeSatisfyInterest_count;
+  }
+
+  virtual void
+  beforeExpirePendingInterest(shared_ptr<pit::Entry> pitEntry) DECL_OVERRIDE
+  {
+    ++m_beforeExpirePendingInterest_count;
+  }
+
+public:
+  int m_afterReceiveInterest_count;
+  int m_beforeSatisfyInterest_count;
+  int m_beforeExpirePendingInterest_count;
+
+  /// outFace to use in afterReceiveInterest, nullptr to reject
+  shared_ptr<Face> m_interestOutFace;
+};
+
+} // namespace tests
+} // namespace nfd
+
+#endif // NFD_TESTS_DAEMON_FW_DUMMY_STRATEGY_HPP
diff --git a/NFD/tests/daemon/fw/face-table.cpp b/NFD/tests/daemon/fw/face-table.cpp
new file mode 100644
index 0000000..b2bfab4
--- /dev/null
+++ b/NFD/tests/daemon/fw/face-table.cpp
@@ -0,0 +1,152 @@
+/* -*- 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/>.
+ */
+
+#include "fw/face-table.hpp"
+#include "fw/forwarder.hpp"
+
+#include "tests/test-common.hpp"
+#include "tests/daemon/face/dummy-face.hpp"
+
+namespace nfd {
+namespace tests {
+
+BOOST_FIXTURE_TEST_SUITE(FwFaceTable, BaseFixture)
+
+BOOST_AUTO_TEST_CASE(AddRemove)
+{
+  Forwarder forwarder;
+
+  FaceTable& faceTable = forwarder.getFaceTable();
+  std::vector<FaceId> onAddHistory;
+  std::vector<FaceId> onRemoveHistory;
+  faceTable.onAdd.connect([&] (shared_ptr<Face> face) {
+    onAddHistory.push_back(face->getId());
+  });
+  faceTable.onRemove.connect([&] (shared_ptr<Face> face) {
+    onRemoveHistory.push_back(face->getId());
+  });
+
+  shared_ptr<Face> face1 = make_shared<DummyFace>();
+  shared_ptr<Face> face2 = make_shared<DummyFace>();
+
+  BOOST_CHECK_EQUAL(face1->getId(), INVALID_FACEID);
+  BOOST_CHECK_EQUAL(face2->getId(), INVALID_FACEID);
+
+  forwarder.addFace(face1);
+  forwarder.addFace(face2);
+
+  BOOST_CHECK_NE(face1->getId(), INVALID_FACEID);
+  BOOST_CHECK_NE(face2->getId(), INVALID_FACEID);
+  BOOST_CHECK_NE(face1->getId(), face2->getId());
+  BOOST_CHECK_GT(face1->getId(), FACEID_RESERVED_MAX);
+  BOOST_CHECK_GT(face2->getId(), FACEID_RESERVED_MAX);
+
+  FaceId oldId1 = face1->getId();
+  faceTable.add(face1);
+  BOOST_CHECK_EQUAL(face1->getId(), oldId1);
+  BOOST_CHECK_EQUAL(faceTable.size(), 2);
+
+  BOOST_REQUIRE_EQUAL(onAddHistory.size(), 2);
+  BOOST_CHECK_EQUAL(onAddHistory[0], face1->getId());
+  BOOST_CHECK_EQUAL(onAddHistory[1], face2->getId());
+
+  face1->close();
+
+  BOOST_CHECK_EQUAL(face1->getId(), INVALID_FACEID);
+
+  BOOST_REQUIRE_EQUAL(onRemoveHistory.size(), 1);
+  BOOST_CHECK_EQUAL(onRemoveHistory[0], onAddHistory[0]);
+}
+
+BOOST_AUTO_TEST_CASE(AddReserved)
+{
+  Forwarder forwarder;
+  FaceTable& faceTable = forwarder.getFaceTable();
+
+  shared_ptr<Face> face1 = make_shared<DummyFace>();
+  BOOST_CHECK_EQUAL(face1->getId(), INVALID_FACEID);
+
+  faceTable.addReserved(face1, 5);
+  BOOST_CHECK_EQUAL(face1->getId(), 5);
+}
+
+BOOST_AUTO_TEST_CASE(Enumerate)
+{
+  Forwarder forwarder;
+  FaceTable& faceTable = forwarder.getFaceTable();
+
+  shared_ptr<Face> face1 = make_shared<DummyFace>();
+  shared_ptr<Face> face2 = make_shared<DummyFace>();
+  bool hasFace1 = false, hasFace2 = false;
+
+  BOOST_CHECK_EQUAL(faceTable.size(), 0);
+  BOOST_CHECK_EQUAL(std::distance(faceTable.begin(), faceTable.end()), faceTable.size());
+
+  faceTable.add(face1);
+  BOOST_CHECK_EQUAL(faceTable.size(), 1);
+  BOOST_CHECK_EQUAL(std::distance(faceTable.begin(), faceTable.end()), faceTable.size());
+  hasFace1 = hasFace2 = false;
+  for (FaceTable::const_iterator it = faceTable.begin(); it != faceTable.end(); ++it) {
+    if (*it == face1) {
+      hasFace1 = true;
+    }
+  }
+  BOOST_CHECK(hasFace1);
+
+  faceTable.add(face2);
+  BOOST_CHECK_EQUAL(faceTable.size(), 2);
+  BOOST_CHECK_EQUAL(std::distance(faceTable.begin(), faceTable.end()), faceTable.size());
+  hasFace1 = hasFace2 = false;
+  for (FaceTable::const_iterator it = faceTable.begin(); it != faceTable.end(); ++it) {
+    if (*it == face1) {
+      hasFace1 = true;
+    }
+    if (*it == face2) {
+      hasFace2 = true;
+    }
+  }
+  BOOST_CHECK(hasFace1);
+  BOOST_CHECK(hasFace2);
+
+  face1->close();
+  BOOST_CHECK_EQUAL(faceTable.size(), 1);
+  BOOST_CHECK_EQUAL(std::distance(faceTable.begin(), faceTable.end()), faceTable.size());
+  hasFace1 = hasFace2 = false;
+  for (FaceTable::const_iterator it = faceTable.begin(); it != faceTable.end(); ++it) {
+    if (*it == face1) {
+      hasFace1 = true;
+    }
+    if (*it == face2) {
+      hasFace2 = true;
+    }
+  }
+  BOOST_CHECK(!hasFace1);
+  BOOST_CHECK(hasFace2);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/NFD/tests/daemon/fw/forwarder.cpp b/NFD/tests/daemon/fw/forwarder.cpp
new file mode 100644
index 0000000..1b74f37
--- /dev/null
+++ b/NFD/tests/daemon/fw/forwarder.cpp
@@ -0,0 +1,466 @@
+/* -*- 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/>.
+ */
+
+#include "fw/forwarder.hpp"
+#include "tests/daemon/face/dummy-face.hpp"
+#include "dummy-strategy.hpp"
+
+#include "tests/test-common.hpp"
+#include "tests/limited-io.hpp"
+
+namespace nfd {
+namespace tests {
+
+BOOST_FIXTURE_TEST_SUITE(FwForwarder, BaseFixture)
+
+BOOST_AUTO_TEST_CASE(SimpleExchange)
+{
+  LimitedIo limitedIo;
+  auto afterOp = bind(&LimitedIo::afterOp, &limitedIo);;
+  Forwarder forwarder;
+
+  Name nameA  ("ndn:/A");
+  Name nameAB ("ndn:/A/B");
+  Name nameABC("ndn:/A/B/C");
+  shared_ptr<Interest> interestAB = makeInterest(nameAB);
+  interestAB->setInterestLifetime(time::seconds(4));
+  shared_ptr<Data> dataABC = makeData(nameABC);
+
+  shared_ptr<DummyFace> face1 = make_shared<DummyFace>();
+  shared_ptr<DummyFace> face2 = make_shared<DummyFace>();
+  face1->afterSend += afterOp;
+  face2->afterSend += afterOp;
+  forwarder.addFace(face1);
+  forwarder.addFace(face2);
+
+  Fib& fib = forwarder.getFib();
+  shared_ptr<fib::Entry> fibEntry = fib.insert(Name("ndn:/A")).first;
+  fibEntry->addNextHop(face2, 0);
+
+  BOOST_CHECK_EQUAL(forwarder.getCounters().getNInInterests (), 0);
+  BOOST_CHECK_EQUAL(forwarder.getCounters().getNOutInterests(), 0);
+  g_io.post([&] { face1->receiveInterest(*interestAB); });
+  BOOST_CHECK_EQUAL(limitedIo.run(1, time::seconds(1)), LimitedIo::EXCEED_OPS);
+  BOOST_REQUIRE_EQUAL(face2->m_sentInterests.size(), 1);
+  BOOST_CHECK_EQUAL(face2->m_sentInterests[0].getName(), nameAB);
+  BOOST_CHECK_EQUAL(face2->m_sentInterests[0].getIncomingFaceId(), face1->getId());
+  BOOST_CHECK_EQUAL(forwarder.getCounters().getNInInterests (), 1);
+  BOOST_CHECK_EQUAL(forwarder.getCounters().getNOutInterests(), 1);
+
+  BOOST_CHECK_EQUAL(forwarder.getCounters().getNInDatas (), 0);
+  BOOST_CHECK_EQUAL(forwarder.getCounters().getNOutDatas(), 0);
+  g_io.post([&] { face2->receiveData(*dataABC); });
+  BOOST_CHECK_EQUAL(limitedIo.run(1, time::seconds(1)), LimitedIo::EXCEED_OPS);
+  BOOST_REQUIRE_EQUAL(face1->m_sentDatas.size(), 1);
+  BOOST_CHECK_EQUAL(face1->m_sentDatas[0].getName(), nameABC);
+  BOOST_CHECK_EQUAL(face1->m_sentDatas[0].getIncomingFaceId(), face2->getId());
+  BOOST_CHECK_EQUAL(forwarder.getCounters().getNInDatas (), 1);
+  BOOST_CHECK_EQUAL(forwarder.getCounters().getNOutDatas(), 1);
+}
+
+BOOST_AUTO_TEST_CASE(CsMatched)
+{
+  LimitedIo limitedIo;
+  Forwarder forwarder;
+
+  shared_ptr<DummyFace> face1 = make_shared<DummyFace>();
+  shared_ptr<DummyFace> face2 = make_shared<DummyFace>();
+  shared_ptr<DummyFace> face3 = make_shared<DummyFace>();
+  forwarder.addFace(face1);
+  forwarder.addFace(face2);
+  forwarder.addFace(face3);
+
+  shared_ptr<Interest> interestA = makeInterest("ndn:/A");
+  interestA->setInterestLifetime(time::seconds(4));
+  shared_ptr<Data> dataA = makeData("ndn:/A");
+  dataA->setIncomingFaceId(face3->getId());
+
+  Fib& fib = forwarder.getFib();
+  shared_ptr<fib::Entry> fibEntry = fib.insert(Name("ndn:/A")).first;
+  fibEntry->addNextHop(face2, 0);
+
+  Pit& pit = forwarder.getPit();
+  BOOST_CHECK_EQUAL(pit.size(), 0);
+
+  Cs& cs = forwarder.getCs();
+  BOOST_REQUIRE(cs.insert(*dataA));
+
+  face1->receiveInterest(*interestA);
+  limitedIo.run(LimitedIo::UNLIMITED_OPS, time::milliseconds(5));
+  // Interest matching ContentStore should not be forwarded
+  BOOST_REQUIRE_EQUAL(face2->m_sentInterests.size(), 0);
+
+  BOOST_REQUIRE_EQUAL(face1->m_sentDatas.size(), 1);
+  // IncomingFaceId field should be reset to represent CS
+  BOOST_CHECK_EQUAL(face1->m_sentDatas[0].getIncomingFaceId(), FACEID_CONTENT_STORE);
+
+  limitedIo.run(LimitedIo::UNLIMITED_OPS, time::milliseconds(500));
+  // PIT entry should not be left behind
+  BOOST_CHECK_EQUAL(pit.size(), 0);
+}
+
+class ScopeLocalhostIncomingTestForwarder : public Forwarder
+{
+public:
+  ScopeLocalhostIncomingTestForwarder()
+  {
+  }
+
+  virtual void
+  onDataUnsolicited(Face& inFace, const Data& data)
+  {
+    ++m_onDataUnsolicited_count;
+  }
+
+protected:
+  virtual void
+  dispatchToStrategy(shared_ptr<pit::Entry> pitEntry, function<void(fw::Strategy*)> f)
+  {
+    ++m_dispatchToStrategy_count;
+  }
+
+public:
+  int m_dispatchToStrategy_count;
+  int m_onDataUnsolicited_count;
+};
+
+BOOST_AUTO_TEST_CASE(ScopeLocalhostIncoming)
+{
+  ScopeLocalhostIncomingTestForwarder forwarder;
+  shared_ptr<Face> face1 = make_shared<DummyLocalFace>();
+  shared_ptr<Face> face2 = make_shared<DummyFace>();
+  forwarder.addFace(face1);
+  forwarder.addFace(face2);
+
+  // local face, /localhost: OK
+  forwarder.m_dispatchToStrategy_count = 0;
+  shared_ptr<Interest> i1 = makeInterest("/localhost/A1");
+  forwarder.onIncomingInterest(*face1, *i1);
+  BOOST_CHECK_EQUAL(forwarder.m_dispatchToStrategy_count, 1);
+
+  // non-local face, /localhost: violate
+  forwarder.m_dispatchToStrategy_count = 0;
+  shared_ptr<Interest> i2 = makeInterest("/localhost/A2");
+  forwarder.onIncomingInterest(*face2, *i2);
+  BOOST_CHECK_EQUAL(forwarder.m_dispatchToStrategy_count, 0);
+
+  // local face, non-/localhost: OK
+  forwarder.m_dispatchToStrategy_count = 0;
+  shared_ptr<Interest> i3 = makeInterest("/A3");
+  forwarder.onIncomingInterest(*face1, *i3);
+  BOOST_CHECK_EQUAL(forwarder.m_dispatchToStrategy_count, 1);
+
+  // non-local face, non-/localhost: OK
+  forwarder.m_dispatchToStrategy_count = 0;
+  shared_ptr<Interest> i4 = makeInterest("/A4");
+  forwarder.onIncomingInterest(*face2, *i4);
+  BOOST_CHECK_EQUAL(forwarder.m_dispatchToStrategy_count, 1);
+
+  // local face, /localhost: OK
+  forwarder.m_onDataUnsolicited_count = 0;
+  shared_ptr<Data> d1 = makeData("/localhost/B1");
+  forwarder.onIncomingData(*face1, *d1);
+  BOOST_CHECK_EQUAL(forwarder.m_onDataUnsolicited_count, 1);
+
+  // non-local face, /localhost: OK
+  forwarder.m_onDataUnsolicited_count = 0;
+  shared_ptr<Data> d2 = makeData("/localhost/B2");
+  forwarder.onIncomingData(*face2, *d2);
+  BOOST_CHECK_EQUAL(forwarder.m_onDataUnsolicited_count, 0);
+
+  // local face, non-/localhost: OK
+  forwarder.m_onDataUnsolicited_count = 0;
+  shared_ptr<Data> d3 = makeData("/B3");
+  forwarder.onIncomingData(*face1, *d3);
+  BOOST_CHECK_EQUAL(forwarder.m_onDataUnsolicited_count, 1);
+
+  // non-local face, non-/localhost: OK
+  forwarder.m_onDataUnsolicited_count = 0;
+  shared_ptr<Data> d4 = makeData("/B4");
+  forwarder.onIncomingData(*face2, *d4);
+  BOOST_CHECK_EQUAL(forwarder.m_onDataUnsolicited_count, 1);
+}
+
+BOOST_AUTO_TEST_CASE(ScopeLocalhostOutgoing)
+{
+  Forwarder forwarder;
+  shared_ptr<DummyLocalFace> face1 = make_shared<DummyLocalFace>();
+  shared_ptr<DummyFace>      face2 = make_shared<DummyFace>();
+  shared_ptr<Face>           face3 = make_shared<DummyLocalFace>();
+  forwarder.addFace(face1);
+  forwarder.addFace(face2);
+  forwarder.addFace(face3);
+  Pit& pit = forwarder.getPit();
+
+  // local face, /localhost: OK
+  shared_ptr<Interest> interestA1 = makeInterest("/localhost/A1");
+  shared_ptr<pit::Entry> pitA1 = pit.insert(*interestA1).first;
+  pitA1->insertOrUpdateInRecord(face3, *interestA1);
+  face1->m_sentInterests.clear();
+  forwarder.onOutgoingInterest(pitA1, *face1);
+  BOOST_CHECK_EQUAL(face1->m_sentInterests.size(), 1);
+
+  // non-local face, /localhost: violate
+  shared_ptr<Interest> interestA2 = makeInterest("/localhost/A2");
+  shared_ptr<pit::Entry> pitA2 = pit.insert(*interestA2).first;
+  pitA2->insertOrUpdateInRecord(face3, *interestA2);
+  face2->m_sentInterests.clear();
+  forwarder.onOutgoingInterest(pitA2, *face2);
+  BOOST_CHECK_EQUAL(face2->m_sentInterests.size(), 0);
+
+  // local face, non-/localhost: OK
+  shared_ptr<Interest> interestA3 = makeInterest("/A3");
+  shared_ptr<pit::Entry> pitA3 = pit.insert(*interestA3).first;
+  pitA3->insertOrUpdateInRecord(face3, *interestA3);
+  face1->m_sentInterests.clear();
+  forwarder.onOutgoingInterest(pitA3, *face1);
+  BOOST_CHECK_EQUAL(face1->m_sentInterests.size(), 1);
+
+  // non-local face, non-/localhost: OK
+  shared_ptr<Interest> interestA4 = makeInterest("/A4");
+  shared_ptr<pit::Entry> pitA4 = pit.insert(*interestA4).first;
+  pitA4->insertOrUpdateInRecord(face3, *interestA4);
+  face2->m_sentInterests.clear();
+  forwarder.onOutgoingInterest(pitA4, *face2);
+  BOOST_CHECK_EQUAL(face2->m_sentInterests.size(), 1);
+
+  // local face, /localhost: OK
+  face1->m_sentDatas.clear();
+  forwarder.onOutgoingData(Data("/localhost/B1"), *face1);
+  BOOST_CHECK_EQUAL(face1->m_sentDatas.size(), 1);
+
+  // non-local face, /localhost: OK
+  face2->m_sentDatas.clear();
+  forwarder.onOutgoingData(Data("/localhost/B2"), *face2);
+  BOOST_CHECK_EQUAL(face2->m_sentDatas.size(), 0);
+
+  // local face, non-/localhost: OK
+  face1->m_sentDatas.clear();
+  forwarder.onOutgoingData(Data("/B3"), *face1);
+  BOOST_CHECK_EQUAL(face1->m_sentDatas.size(), 1);
+
+  // non-local face, non-/localhost: OK
+  face2->m_sentDatas.clear();
+  forwarder.onOutgoingData(Data("/B4"), *face2);
+  BOOST_CHECK_EQUAL(face2->m_sentDatas.size(), 1);
+}
+
+BOOST_AUTO_TEST_CASE(ScopeLocalhopOutgoing)
+{
+  Forwarder forwarder;
+  shared_ptr<DummyLocalFace> face1 = make_shared<DummyLocalFace>();
+  shared_ptr<DummyFace>      face2 = make_shared<DummyFace>();
+  shared_ptr<DummyLocalFace> face3 = make_shared<DummyLocalFace>();
+  shared_ptr<DummyFace>      face4 = make_shared<DummyFace>();
+  forwarder.addFace(face1);
+  forwarder.addFace(face2);
+  forwarder.addFace(face3);
+  forwarder.addFace(face4);
+  Pit& pit = forwarder.getPit();
+
+  // from local face, to local face: OK
+  shared_ptr<Interest> interest1 = makeInterest("/localhop/1");
+  shared_ptr<pit::Entry> pit1 = pit.insert(*interest1).first;
+  pit1->insertOrUpdateInRecord(face1, *interest1);
+  face3->m_sentInterests.clear();
+  forwarder.onOutgoingInterest(pit1, *face3);
+  BOOST_CHECK_EQUAL(face3->m_sentInterests.size(), 1);
+
+  // from non-local face, to local face: OK
+  shared_ptr<Interest> interest2 = makeInterest("/localhop/2");
+  shared_ptr<pit::Entry> pit2 = pit.insert(*interest2).first;
+  pit2->insertOrUpdateInRecord(face2, *interest2);
+  face3->m_sentInterests.clear();
+  forwarder.onOutgoingInterest(pit2, *face3);
+  BOOST_CHECK_EQUAL(face3->m_sentInterests.size(), 1);
+
+  // from local face, to non-local face: OK
+  shared_ptr<Interest> interest3 = makeInterest("/localhop/3");
+  shared_ptr<pit::Entry> pit3 = pit.insert(*interest3).first;
+  pit3->insertOrUpdateInRecord(face1, *interest3);
+  face4->m_sentInterests.clear();
+  forwarder.onOutgoingInterest(pit3, *face4);
+  BOOST_CHECK_EQUAL(face4->m_sentInterests.size(), 1);
+
+  // from non-local face, to non-local face: violate
+  shared_ptr<Interest> interest4 = makeInterest("/localhop/4");
+  shared_ptr<pit::Entry> pit4 = pit.insert(*interest4).first;
+  pit4->insertOrUpdateInRecord(face2, *interest4);
+  face4->m_sentInterests.clear();
+  forwarder.onOutgoingInterest(pit4, *face4);
+  BOOST_CHECK_EQUAL(face4->m_sentInterests.size(), 0);
+
+  // from local face and non-local face, to local face: OK
+  shared_ptr<Interest> interest5 = makeInterest("/localhop/5");
+  shared_ptr<pit::Entry> pit5 = pit.insert(*interest5).first;
+  pit5->insertOrUpdateInRecord(face1, *interest5);
+  pit5->insertOrUpdateInRecord(face2, *interest5);
+  face3->m_sentInterests.clear();
+  forwarder.onOutgoingInterest(pit5, *face3);
+  BOOST_CHECK_EQUAL(face3->m_sentInterests.size(), 1);
+
+  // from local face and non-local face, to non-local face: OK
+  shared_ptr<Interest> interest6 = makeInterest("/localhop/6");
+  shared_ptr<pit::Entry> pit6 = pit.insert(*interest6).first;
+  pit6->insertOrUpdateInRecord(face1, *interest6);
+  pit6->insertOrUpdateInRecord(face2, *interest6);
+  face4->m_sentInterests.clear();
+  forwarder.onOutgoingInterest(pit6, *face4);
+  BOOST_CHECK_EQUAL(face4->m_sentInterests.size(), 1);
+}
+
+BOOST_AUTO_TEST_CASE(StrategyDispatch)
+{
+  LimitedIo limitedIo;
+  Forwarder forwarder;
+  shared_ptr<Face> face1 = make_shared<DummyFace>();
+  shared_ptr<Face> face2 = make_shared<DummyFace>();
+  forwarder.addFace(face1);
+  forwarder.addFace(face2);
+
+  StrategyChoice& strategyChoice = forwarder.getStrategyChoice();
+  shared_ptr<DummyStrategy> strategyP = make_shared<DummyStrategy>(
+                                        ref(forwarder), "ndn:/strategyP");
+  shared_ptr<DummyStrategy> strategyQ = make_shared<DummyStrategy>(
+                                        ref(forwarder), "ndn:/strategyQ");
+  strategyChoice.install(strategyP);
+  strategyChoice.install(strategyQ);
+  strategyChoice.insert("ndn:/" , strategyP->getName());
+  strategyChoice.insert("ndn:/B", strategyQ->getName());
+
+  shared_ptr<Interest> interest1 = makeInterest("ndn:/A/1");
+  strategyP->m_afterReceiveInterest_count = 0;
+  strategyP->m_interestOutFace = face2;
+  forwarder.onInterest(*face1, *interest1);
+  BOOST_CHECK_EQUAL(strategyP->m_afterReceiveInterest_count, 1);
+
+  shared_ptr<Interest> interest2 = makeInterest("ndn:/B/2");
+  strategyQ->m_afterReceiveInterest_count = 0;
+  strategyQ->m_interestOutFace = face2;
+  forwarder.onInterest(*face1, *interest2);
+  BOOST_CHECK_EQUAL(strategyQ->m_afterReceiveInterest_count, 1);
+
+  limitedIo.run(LimitedIo::UNLIMITED_OPS, time::milliseconds(5));
+
+  shared_ptr<Data> data1 = makeData("ndn:/A/1/a");
+  strategyP->m_beforeSatisfyInterest_count = 0;
+  forwarder.onData(*face2, *data1);
+  BOOST_CHECK_EQUAL(strategyP->m_beforeSatisfyInterest_count, 1);
+
+  shared_ptr<Data> data2 = makeData("ndn:/B/2/b");
+  strategyQ->m_beforeSatisfyInterest_count = 0;
+  forwarder.onData(*face2, *data2);
+  BOOST_CHECK_EQUAL(strategyQ->m_beforeSatisfyInterest_count, 1);
+
+  shared_ptr<Interest> interest3 = makeInterest("ndn:/A/3");
+  interest3->setInterestLifetime(time::milliseconds(30));
+  forwarder.onInterest(*face1, *interest3);
+  shared_ptr<Interest> interest4 = makeInterest("ndn:/B/4");
+  interest4->setInterestLifetime(time::milliseconds(5000));
+  forwarder.onInterest(*face1, *interest4);
+
+  strategyP->m_beforeExpirePendingInterest_count = 0;
+  strategyQ->m_beforeExpirePendingInterest_count = 0;
+  limitedIo.run(LimitedIo::UNLIMITED_OPS, time::milliseconds(100));
+  BOOST_CHECK_EQUAL(strategyP->m_beforeExpirePendingInterest_count, 1);
+  BOOST_CHECK_EQUAL(strategyQ->m_beforeExpirePendingInterest_count, 0);
+}
+
+BOOST_AUTO_TEST_CASE(IncomingData)
+{
+  LimitedIo limitedIo;
+  Forwarder forwarder;
+  shared_ptr<DummyFace> face1 = make_shared<DummyFace>();
+  shared_ptr<DummyFace> face2 = make_shared<DummyFace>();
+  shared_ptr<DummyFace> face3 = make_shared<DummyFace>();
+  shared_ptr<DummyFace> face4 = make_shared<DummyFace>();
+  forwarder.addFace(face1);
+  forwarder.addFace(face2);
+  forwarder.addFace(face3);
+  forwarder.addFace(face4);
+
+  Pit& pit = forwarder.getPit();
+  shared_ptr<Interest> interest0 = makeInterest("ndn:/");
+  shared_ptr<pit::Entry> pit0 = pit.insert(*interest0).first;
+  pit0->insertOrUpdateInRecord(face1, *interest0);
+  shared_ptr<Interest> interestA = makeInterest("ndn:/A");
+  shared_ptr<pit::Entry> pitA = pit.insert(*interestA).first;
+  pitA->insertOrUpdateInRecord(face1, *interestA);
+  pitA->insertOrUpdateInRecord(face2, *interestA);
+  shared_ptr<Interest> interestC = makeInterest("ndn:/A/B/C");
+  shared_ptr<pit::Entry> pitC = pit.insert(*interestC).first;
+  pitC->insertOrUpdateInRecord(face3, *interestC);
+  pitC->insertOrUpdateInRecord(face4, *interestC);
+
+  shared_ptr<Data> dataD = makeData("ndn:/A/B/C/D");
+  forwarder.onIncomingData(*face3, *dataD);
+  limitedIo.run(LimitedIo::UNLIMITED_OPS, time::milliseconds(5));
+
+  BOOST_CHECK_EQUAL(face1->m_sentDatas.size(), 1);
+  BOOST_CHECK_EQUAL(face2->m_sentDatas.size(), 1);
+  BOOST_CHECK_EQUAL(face3->m_sentDatas.size(), 0);
+  BOOST_CHECK_EQUAL(face4->m_sentDatas.size(), 1);
+}
+
+BOOST_FIXTURE_TEST_CASE(InterestLoopWithShortLifetime, UnitTestTimeFixture) // Bug 1953
+{
+  Forwarder forwarder;
+  auto face1 = make_shared<DummyFace>();
+  auto face2 = make_shared<DummyFace>();
+  forwarder.addFace(face1);
+  forwarder.addFace(face2);
+
+  // cause an Interest sent out of face2 to loop back into face1 after a delay
+  face2->onSendInterest += [&face1] (const Interest& interest) {
+    scheduler::schedule(time::milliseconds(170), [&] { face1->receiveInterest(interest); });
+  };
+
+  Fib& fib = forwarder.getFib();
+  shared_ptr<fib::Entry> fibEntry = fib.insert(Name("ndn:/A")).first;
+  fibEntry->addNextHop(face2, 0);
+
+  // receive an Interest
+  shared_ptr<Interest> interest = makeInterest("ndn:/A/1");
+  interest->setNonce(82101183);
+  interest->setInterestLifetime(time::milliseconds(50));
+  face1->receiveInterest(*interest);
+
+  // interest should be forwarded only once, as long as Nonce is in Dead Nonce List
+  BOOST_ASSERT(time::milliseconds(25) * 40 < forwarder.getDeadNonceList().getLifetime());
+  this->advanceClocks(time::milliseconds(25), 40);
+
+  BOOST_CHECK_EQUAL(face2->m_sentInterests.size(), 1);
+
+  // It's unnecessary to check that Interest with duplicate Nonce can be forwarded again
+  // after it's gone from Dead Nonce List, because the entry lifetime of Dead Nonce List
+  // is an implementation decision. NDN protocol requires Name+Nonce to be unique,
+  // without specifying when Name+Nonce could repeat. Forwarder is permitted to suppress
+  // an Interest if its Name+Nonce has appeared any point in the past.
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/NFD/tests/daemon/fw/ncc-strategy.cpp b/NFD/tests/daemon/fw/ncc-strategy.cpp
new file mode 100644
index 0000000..ab3f641
--- /dev/null
+++ b/NFD/tests/daemon/fw/ncc-strategy.cpp
@@ -0,0 +1,279 @@
+/* -*- 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/>.
+ */
+
+#include "fw/ncc-strategy.hpp"
+#include "strategy-tester.hpp"
+#include "tests/daemon/face/dummy-face.hpp"
+#include "tests/limited-io.hpp"
+
+#include "tests/test-common.hpp"
+
+namespace nfd {
+namespace tests {
+
+BOOST_FIXTURE_TEST_SUITE(FwNccStrategy, UnitTestTimeFixture)
+
+// NccStrategy is fairly complex.
+// The most important property is:
+// it remembers which upstream is the fastest to return Data,
+// and favors this upstream in subsequent Interests.
+BOOST_AUTO_TEST_CASE(FavorRespondingUpstream)
+{
+  LimitedIo limitedIo(this);
+  Forwarder forwarder;
+  typedef StrategyTester<fw::NccStrategy> NccStrategyTester;
+  shared_ptr<NccStrategyTester> strategy = make_shared<NccStrategyTester>(ref(forwarder));
+  strategy->onAction.connect(bind(&LimitedIo::afterOp, &limitedIo));
+
+  shared_ptr<DummyFace> face1 = make_shared<DummyFace>();
+  shared_ptr<DummyFace> face2 = make_shared<DummyFace>();
+  shared_ptr<DummyFace> face3 = make_shared<DummyFace>();
+  forwarder.addFace(face1);
+  forwarder.addFace(face2);
+  forwarder.addFace(face3);
+
+  Fib& fib = forwarder.getFib();
+  shared_ptr<fib::Entry> fibEntry = fib.insert(Name()).first;
+  fibEntry->addNextHop(face1, 10);
+  fibEntry->addNextHop(face2, 20);
+
+  StrategyChoice& strategyChoice = forwarder.getStrategyChoice();
+  strategyChoice.install(strategy);
+  strategyChoice.insert(Name(), strategy->getName());
+
+  Pit& pit = forwarder.getPit();
+
+  // first Interest: strategy knows nothing and follows routing
+  shared_ptr<Interest> interest1p = makeInterest("ndn:/0Jm1ajrW/%00");
+  Interest& interest1 = *interest1p;
+  interest1.setInterestLifetime(time::milliseconds(8000));
+  shared_ptr<pit::Entry> pitEntry1 = pit.insert(interest1).first;
+
+  pitEntry1->insertOrUpdateInRecord(face3, interest1);
+  strategy->afterReceiveInterest(*face3, interest1, fibEntry, pitEntry1);
+
+  // forwards to face1 because routing says it's best
+  // (no io run here: afterReceiveInterest has already sent the Interest)
+  BOOST_REQUIRE_EQUAL(strategy->m_sendInterestHistory.size(), 1);
+  BOOST_CHECK_EQUAL(strategy->m_sendInterestHistory[0].get<1>(), face1);
+
+  // forwards to face2 because face1 doesn't respond
+  limitedIo.run(1, time::milliseconds(500), time::milliseconds(10));
+  BOOST_REQUIRE_EQUAL(strategy->m_sendInterestHistory.size(), 2);
+  BOOST_CHECK_EQUAL(strategy->m_sendInterestHistory[1].get<1>(), face2);
+
+  // face2 responds
+  shared_ptr<Data> data1p = makeData("ndn:/0Jm1ajrW/%00");
+  Data& data1 = *data1p;
+  strategy->beforeSatisfyInterest(pitEntry1, *face2, data1);
+  this->advanceClocks(time::milliseconds(10), time::milliseconds(500));
+
+  // second Interest: strategy knows face2 is best
+  shared_ptr<Interest> interest2p = makeInterest("ndn:/0Jm1ajrW/%00%01");
+  Interest& interest2 = *interest2p;
+  interest2.setInterestLifetime(time::milliseconds(8000));
+  shared_ptr<pit::Entry> pitEntry2 = pit.insert(interest2).first;
+
+  pitEntry2->insertOrUpdateInRecord(face3, interest2);
+  strategy->afterReceiveInterest(*face3, interest2, fibEntry, pitEntry2);
+
+  // forwards to face2 because it responds previously
+  this->advanceClocks(time::milliseconds(1));
+  BOOST_REQUIRE_GE(strategy->m_sendInterestHistory.size(), 3);
+  BOOST_CHECK_EQUAL(strategy->m_sendInterestHistory[2].get<1>(), face2);
+}
+
+BOOST_AUTO_TEST_CASE(Bug1853)
+{
+  Forwarder forwarder;
+  typedef StrategyTester<fw::NccStrategy> NccStrategyTester;
+  shared_ptr<NccStrategyTester> strategy = make_shared<NccStrategyTester>(ref(forwarder));
+
+  shared_ptr<DummyFace> face1 = make_shared<DummyFace>();
+  shared_ptr<DummyFace> face2 = make_shared<DummyFace>();
+  shared_ptr<DummyFace> face3 = make_shared<DummyFace>();
+  forwarder.addFace(face1);
+  forwarder.addFace(face2);
+  forwarder.addFace(face3);
+
+  Fib& fib = forwarder.getFib();
+  shared_ptr<fib::Entry> fibEntry = fib.insert(Name()).first;
+  fibEntry->addNextHop(face1, 10);
+
+  StrategyChoice& strategyChoice = forwarder.getStrategyChoice();
+  strategyChoice.install(strategy);
+  strategyChoice.insert(Name(), strategy->getName());
+
+  Pit& pit = forwarder.getPit();
+
+  // first Interest: strategy follows routing and forwards to face1
+  shared_ptr<Interest> interest1 = makeInterest("ndn:/nztwIvHX/%00");
+  interest1->setInterestLifetime(time::milliseconds(8000));
+  shared_ptr<pit::Entry> pitEntry1 = pit.insert(*interest1).first;
+
+  pitEntry1->insertOrUpdateInRecord(face3, *interest1);
+  strategy->afterReceiveInterest(*face3, *interest1, fibEntry, pitEntry1);
+
+  this->advanceClocks(time::milliseconds(1));
+  BOOST_REQUIRE_EQUAL(strategy->m_sendInterestHistory.size(), 1);
+  BOOST_CHECK_EQUAL(strategy->m_sendInterestHistory[0].get<1>(), face1);
+
+  // face1 responds
+  shared_ptr<Data> data1 = makeData("ndn:/nztwIvHX/%00");
+  strategy->beforeSatisfyInterest(pitEntry1, *face1, *data1);
+  this->advanceClocks(time::milliseconds(10), time::milliseconds(500));
+
+  // second Interest: bestFace is face1, nUpstreams becomes 0,
+  // therefore pitEntryInfo->maxInterval cannot be calculated from deferRange and nUpstreams
+  shared_ptr<Interest> interest2 = makeInterest("ndn:/nztwIvHX/%01");
+  interest2->setInterestLifetime(time::milliseconds(8000));
+  shared_ptr<pit::Entry> pitEntry2 = pit.insert(*interest2).first;
+
+  pitEntry2->insertOrUpdateInRecord(face3, *interest2);
+  strategy->afterReceiveInterest(*face3, *interest2, fibEntry, pitEntry2);
+
+  // FIB entry is changed before doPropagate executes
+  fibEntry->addNextHop(face2, 20);
+  this->advanceClocks(time::milliseconds(10), time::milliseconds(1000));// should not crash
+}
+
+BOOST_AUTO_TEST_CASE(Bug1961)
+{
+  LimitedIo limitedIo(this);
+  Forwarder forwarder;
+  typedef StrategyTester<fw::NccStrategy> NccStrategyTester;
+  shared_ptr<NccStrategyTester> strategy = make_shared<NccStrategyTester>(ref(forwarder));
+  strategy->onAction.connect(bind(&LimitedIo::afterOp, &limitedIo));
+
+  shared_ptr<DummyFace> face1 = make_shared<DummyFace>();
+  shared_ptr<DummyFace> face2 = make_shared<DummyFace>();
+  shared_ptr<DummyFace> face3 = make_shared<DummyFace>();
+  forwarder.addFace(face1);
+  forwarder.addFace(face2);
+  forwarder.addFace(face3);
+
+  Fib& fib = forwarder.getFib();
+  shared_ptr<fib::Entry> fibEntry = fib.insert(Name()).first;
+  fibEntry->addNextHop(face1, 10);
+  fibEntry->addNextHop(face2, 20);
+
+  StrategyChoice& strategyChoice = forwarder.getStrategyChoice();
+  strategyChoice.install(strategy);
+  strategyChoice.insert(Name(), strategy->getName());
+
+  Pit& pit = forwarder.getPit();
+
+  // first Interest: strategy forwards to face1 and face2
+  shared_ptr<Interest> interest1 = makeInterest("ndn:/seRMz5a6/%00");
+  interest1->setInterestLifetime(time::milliseconds(2000));
+  shared_ptr<pit::Entry> pitEntry1 = pit.insert(*interest1).first;
+
+  pitEntry1->insertOrUpdateInRecord(face3, *interest1);
+  strategy->afterReceiveInterest(*face3, *interest1, fibEntry, pitEntry1);
+  limitedIo.run(2 - strategy->m_sendInterestHistory.size(),
+                time::milliseconds(2000), time::milliseconds(10));
+  BOOST_REQUIRE_EQUAL(strategy->m_sendInterestHistory.size(), 2);
+  BOOST_CHECK_EQUAL(strategy->m_sendInterestHistory[0].get<1>(), face1);
+  BOOST_CHECK_EQUAL(strategy->m_sendInterestHistory[1].get<1>(), face2);
+
+  // face1 responds
+  shared_ptr<Data> data1 = makeData("ndn:/seRMz5a6/%00");
+  strategy->beforeSatisfyInterest(pitEntry1, *face1, *data1);
+  pitEntry1->deleteInRecords();
+  this->advanceClocks(time::milliseconds(10));
+  // face2 also responds
+  strategy->beforeSatisfyInterest(pitEntry1, *face2, *data1);
+  this->advanceClocks(time::milliseconds(10));
+
+  // second Interest: bestFace should be face 1
+  shared_ptr<Interest> interest2 = makeInterest("ndn:/seRMz5a6/%01");
+  interest2->setInterestLifetime(time::milliseconds(2000));
+  shared_ptr<pit::Entry> pitEntry2 = pit.insert(*interest2).first;
+
+  pitEntry2->insertOrUpdateInRecord(face3, *interest2);
+  strategy->afterReceiveInterest(*face3, *interest2, fibEntry, pitEntry2);
+  limitedIo.run(3 - strategy->m_sendInterestHistory.size(),
+                time::milliseconds(2000), time::milliseconds(10));
+
+  BOOST_REQUIRE_GE(strategy->m_sendInterestHistory.size(), 3);
+  BOOST_CHECK_EQUAL(strategy->m_sendInterestHistory[2].get<1>(), face1);
+}
+
+BOOST_AUTO_TEST_CASE(Bug1971)
+{
+  LimitedIo limitedIo(this);
+  Forwarder forwarder;
+  typedef StrategyTester<fw::NccStrategy> NccStrategyTester;
+  shared_ptr<NccStrategyTester> strategy = make_shared<NccStrategyTester>(ref(forwarder));
+  strategy->onAction.connect(bind(&LimitedIo::afterOp, &limitedIo));
+
+  shared_ptr<DummyFace> face1 = make_shared<DummyFace>();
+  shared_ptr<DummyFace> face2 = make_shared<DummyFace>();
+  forwarder.addFace(face1);
+  forwarder.addFace(face2);
+
+  Fib& fib = forwarder.getFib();
+  shared_ptr<fib::Entry> fibEntry = fib.insert(Name()).first;
+  fibEntry->addNextHop(face2, 10);
+
+  StrategyChoice& strategyChoice = forwarder.getStrategyChoice();
+  strategyChoice.install(strategy);
+  strategyChoice.insert(Name(), strategy->getName());
+
+  Pit& pit = forwarder.getPit();
+
+  // first Interest: strategy forwards to face2
+  shared_ptr<Interest> interest1 = makeInterest("ndn:/M4mBXCsd");
+  interest1->setInterestLifetime(time::milliseconds(2000));
+  shared_ptr<pit::Entry> pitEntry1 = pit.insert(*interest1).first;
+
+  pitEntry1->insertOrUpdateInRecord(face1, *interest1);
+  strategy->afterReceiveInterest(*face1, *interest1, fibEntry, pitEntry1);
+  limitedIo.run(1 - strategy->m_sendInterestHistory.size(),
+                time::milliseconds(2000), time::milliseconds(10));
+  BOOST_REQUIRE_EQUAL(strategy->m_sendInterestHistory.size(), 1);
+  BOOST_CHECK_EQUAL(strategy->m_sendInterestHistory[0].get<1>(), face2);
+
+  // face2 responds
+  shared_ptr<Data> data1 = makeData("ndn:/M4mBXCsd");
+  data1->setFreshnessPeriod(time::milliseconds(5));
+  strategy->beforeSatisfyInterest(pitEntry1, *face2, *data1);
+  pitEntry1->deleteOutRecord(*face2);
+  pitEntry1->deleteInRecords();
+  this->advanceClocks(time::milliseconds(10));
+
+  // similar Interest: strategy should still forward it
+  pitEntry1->insertOrUpdateInRecord(face1, *interest1);
+  strategy->afterReceiveInterest(*face1, *interest1, fibEntry, pitEntry1);
+  limitedIo.run(2 - strategy->m_sendInterestHistory.size(),
+                time::milliseconds(2000), time::milliseconds(10));
+  BOOST_REQUIRE_EQUAL(strategy->m_sendInterestHistory.size(), 2);
+  BOOST_CHECK_EQUAL(strategy->m_sendInterestHistory[1].get<1>(), face2);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/NFD/tests/daemon/fw/rtt-estimator.cpp b/NFD/tests/daemon/fw/rtt-estimator.cpp
new file mode 100644
index 0000000..b5af0ab
--- /dev/null
+++ b/NFD/tests/daemon/fw/rtt-estimator.cpp
@@ -0,0 +1,80 @@
+/* -*- 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/>.
+ **/
+
+#include "fw/rtt-estimator.hpp"
+
+#include "tests/test-common.hpp"
+
+namespace nfd {
+namespace tests {
+
+BOOST_FIXTURE_TEST_SUITE(FwRttEstimator, BaseFixture)
+
+static inline double
+computeRtoAsFloatSeconds(RttEstimator& rtt)
+{
+  typedef time::duration<double, time::seconds::period> FloatSeconds;
+
+  RttEstimator::Duration rto = rtt.computeRto();
+  return time::duration_cast<FloatSeconds>(rto).count();
+}
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+  RttEstimator rtt;
+
+  for (int i = 0; i < 100; ++i) {
+    rtt.addMeasurement(time::seconds(5));
+  }
+  double rto1 = computeRtoAsFloatSeconds(rtt);
+  BOOST_CHECK_CLOSE(rto1, 5.0, 0.1);
+
+  rtt.doubleMultiplier();
+  double rto2 = computeRtoAsFloatSeconds(rtt);
+  BOOST_CHECK_CLOSE(rto2, 10.0, 0.1);
+
+  rtt.doubleMultiplier();
+  double rto3 = computeRtoAsFloatSeconds(rtt);
+  BOOST_CHECK_CLOSE(rto3, 20.0, 0.1);
+
+  rtt.addMeasurement(time::seconds(5)); // reset multiplier
+  double rto4 = computeRtoAsFloatSeconds(rtt);
+  BOOST_CHECK_CLOSE(rto4, 5.0, 0.1);
+
+  rtt.incrementMultiplier();
+  double rto5 = computeRtoAsFloatSeconds(rtt);
+  BOOST_CHECK_CLOSE(rto5, 10.0, 0.1);
+
+  for (int i = 0; i < 5; ++i) {
+    rtt.addMeasurement(time::seconds(6));
+  } // increased variance
+  double rto6 = computeRtoAsFloatSeconds(rtt);
+  BOOST_CHECK_GT(rto6, rto1);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/NFD/tests/daemon/fw/strategy-tester.hpp b/NFD/tests/daemon/fw/strategy-tester.hpp
new file mode 100644
index 0000000..980144d
--- /dev/null
+++ b/NFD/tests/daemon/fw/strategy-tester.hpp
@@ -0,0 +1,93 @@
+/* -*- 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_TESTS_NFD_FW_STRATEGY_TESTER_HPP
+#define NFD_TESTS_NFD_FW_STRATEGY_TESTER_HPP
+
+#include <boost/tuple/tuple_comparison.hpp>
+#include "fw/strategy.hpp"
+
+namespace nfd {
+namespace tests {
+
+/** \class StrategyTester
+ *  \brief extends strategy S for unit testing
+ *
+ *  Actions invoked by S are recorded but not passed to forwarder
+ */
+template<typename S>
+class StrategyTester : public S
+{
+public:
+  explicit
+  StrategyTester(Forwarder& forwarder)
+    : S(forwarder, Name(S::STRATEGY_NAME).append("tester"))
+  {
+  }
+
+  /// fires after each Action
+  signal::Signal<StrategyTester<S>> onAction;
+
+protected:
+  virtual void
+  sendInterest(shared_ptr<pit::Entry> pitEntry,
+               shared_ptr<Face> outFace,
+               bool wantNewNonce = false) DECL_OVERRIDE;
+
+  virtual void
+  rejectPendingInterest(shared_ptr<pit::Entry> pitEntry) DECL_OVERRIDE;
+
+public:
+  typedef boost::tuple<shared_ptr<pit::Entry>, shared_ptr<Face>> SendInterestArgs;
+  std::vector<SendInterestArgs> m_sendInterestHistory;
+
+  typedef boost::tuple<shared_ptr<pit::Entry>> RejectPendingInterestArgs;
+  std::vector<RejectPendingInterestArgs> m_rejectPendingInterestHistory;
+};
+
+
+template<typename S>
+inline void
+StrategyTester<S>::sendInterest(shared_ptr<pit::Entry> pitEntry,
+                                shared_ptr<Face> outFace,
+                                bool wantNewNonce)
+{
+  m_sendInterestHistory.push_back(SendInterestArgs(pitEntry, outFace));
+  pitEntry->insertOrUpdateOutRecord(outFace, pitEntry->getInterest());
+  onAction();
+}
+
+template<typename S>
+inline void
+StrategyTester<S>::rejectPendingInterest(shared_ptr<pit::Entry> pitEntry)
+{
+  m_rejectPendingInterestHistory.push_back(RejectPendingInterestArgs(pitEntry));
+  onAction();
+}
+
+} // namespace tests
+} // namespace nfd
+
+#endif // NFD_TESTS_NFD_FW_STRATEGY_TESTER_HPP
diff --git a/NFD/tests/daemon/fw/strategy.cpp b/NFD/tests/daemon/fw/strategy.cpp
new file mode 100644
index 0000000..066b280
--- /dev/null
+++ b/NFD/tests/daemon/fw/strategy.cpp
@@ -0,0 +1,97 @@
+/* -*- 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/>.
+ */
+
+#include "fw/strategy.hpp"
+#include "dummy-strategy.hpp"
+#include "tests/daemon/face/dummy-face.hpp"
+#include <boost/range/adaptor/filtered.hpp>
+
+#include "tests/test-common.hpp"
+
+namespace nfd {
+namespace tests {
+
+BOOST_FIXTURE_TEST_SUITE(FwStrategy, BaseFixture)
+
+class FaceTableAccessTestStrategy : public DummyStrategy
+{
+public:
+  explicit
+  FaceTableAccessTestStrategy(Forwarder& forwarder)
+    : DummyStrategy(forwarder, Name("ndn:/strategy"))
+  {
+    this->afterAddFace.connect([this] (shared_ptr<Face> face) {
+      this->addedFaces.push_back(face->getId());
+    });
+    this->beforeRemoveFace.connect([this] (shared_ptr<Face> face) {
+      this->removedFaces.push_back(face->getId());
+    });
+  }
+
+  std::vector<FaceId>
+  getLocalFaces()
+  {
+    auto enumerable = this->getFaceTable() |
+                      boost::adaptors::filtered([] (shared_ptr<Face> face) {
+                        return face->isLocal();
+                      });
+
+    std::vector<FaceId> results;
+    for (shared_ptr<Face> face : enumerable) {
+      results.push_back(face->getId());
+    }
+    return results;
+  }
+
+public:
+  std::vector<FaceId> addedFaces;
+  std::vector<FaceId> removedFaces;
+};
+
+BOOST_AUTO_TEST_CASE(FaceTableAccess)
+{
+  Forwarder forwarder;
+  FaceTableAccessTestStrategy strategy(forwarder);
+
+  auto face1 = make_shared<DummyFace>();
+  auto face2 = make_shared<DummyLocalFace>();
+  forwarder.addFace(face1);
+  forwarder.addFace(face2);
+  FaceId id1 = face1->getId();
+  FaceId id2 = face2->getId();
+
+  BOOST_CHECK(strategy.getLocalFaces() == std::vector<FaceId>{id2});
+
+  face2->close();
+  face1->close();
+
+  BOOST_CHECK((strategy.addedFaces   == std::vector<FaceId>{id1, id2}));
+  BOOST_CHECK((strategy.removedFaces == std::vector<FaceId>{id2, id1}));
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/NFD/tests/daemon/mgmt/channel-status-common.hpp b/NFD/tests/daemon/mgmt/channel-status-common.hpp
new file mode 100644
index 0000000..4954cc3
--- /dev/null
+++ b/NFD/tests/daemon/mgmt/channel-status-common.hpp
@@ -0,0 +1,97 @@
+/* -*- 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_TESTS_NFD_MGMT_CHANNEL_STATUS_COMMON_HPP
+#define NFD_TESTS_NFD_MGMT_CHANNEL_STATUS_COMMON_HPP
+
+#include "face/protocol-factory.hpp"
+#include "face/channel.hpp"
+
+#include "tests/test-common.hpp"
+
+#include <ndn-cxx/management/nfd-channel-status.hpp>
+
+
+
+namespace nfd {
+namespace tests {
+
+class DummyChannel : public Channel
+{
+public:
+
+  DummyChannel(const std::string& uri)
+  {
+    setUri(FaceUri(uri));
+  }
+
+  virtual
+  ~DummyChannel()
+  {
+  }
+};
+
+class DummyProtocolFactory : public ProtocolFactory
+{
+public:
+
+  DummyProtocolFactory()
+  {
+
+  }
+
+  virtual void
+  createFace(const FaceUri& uri,
+             const FaceCreatedCallback& onCreated,
+             const FaceConnectFailedCallback& onConnectFailed)
+  {
+  }
+
+  virtual void
+  addChannel(const std::string& channelUri)
+  {
+    m_channels.push_back(make_shared<DummyChannel>(channelUri));
+  }
+
+  virtual std::list<shared_ptr<const Channel> >
+  getChannels() const
+  {
+    return m_channels;
+  }
+
+  virtual size_t
+  getNChannels() const
+  {
+    return m_channels.size();
+  }
+
+private:
+  std::list<shared_ptr<const Channel> > m_channels;
+};
+
+} // namespace tests
+} // namespace nfd
+
+#endif // NFD_TESTS_NFD_MGMT_CHANNEL_STATUS_COMMON_HPP
diff --git a/NFD/tests/daemon/mgmt/channel-status-publisher.cpp b/NFD/tests/daemon/mgmt/channel-status-publisher.cpp
new file mode 100644
index 0000000..370efe1
--- /dev/null
+++ b/NFD/tests/daemon/mgmt/channel-status-publisher.cpp
@@ -0,0 +1,187 @@
+/* -*- 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/>.
+ */
+
+#include "mgmt/channel-status-publisher.hpp"
+#include "mgmt/internal-face.hpp"
+
+#include "channel-status-common.hpp"
+
+#include "core/logger.hpp"
+NFD_LOG_INIT("TestChannelStatusPublisher");
+
+namespace nfd {
+namespace tests {
+
+class ChannelStatusPublisherFixture : BaseFixture
+{
+public:
+  ChannelStatusPublisherFixture()
+    : m_face(make_shared<InternalFace>())
+    , m_publisher(m_factories, *m_face, "/localhost/nfd/faces/channels", m_keyChain)
+    , m_finished(false)
+  {
+  }
+
+  virtual
+  ~ChannelStatusPublisherFixture()
+  {
+  }
+
+  // virtual shared_ptr<DummyProtocolFactory>
+  // addProtocolFactory(const std::string& protocol)
+  // {
+  //   shared_ptr<DummyProtocolFactory> factory(make_shared<DummyProtocolFactory>());
+  //   m_factories[protocol] = factory;
+
+  //   return factory;
+  // }
+
+  void
+  validatePublish(const Data& data)
+  {
+    Block payload = data.getContent();
+
+    m_buffer.appendByteArray(payload.value(), payload.value_size());
+
+    BOOST_CHECK_NO_THROW(data.getName()[-1].toSegment());
+    if (data.getFinalBlockId() != data.getName()[-1])
+      {
+        return;
+      }
+
+    // wrap the Channel Status entries in a single Content TLV for easy parsing
+    m_buffer.prependVarNumber(m_buffer.size());
+    m_buffer.prependVarNumber(tlv::Content);
+
+    ndn::Block parser(m_buffer.buf(), m_buffer.size());
+    parser.parse();
+
+    BOOST_REQUIRE_EQUAL(parser.elements_size(), m_expectedEntries.size());
+
+    for (Block::element_const_iterator i = parser.elements_begin();
+         i != parser.elements_end();
+         ++i)
+      {
+        if (i->type() != ndn::tlv::nfd::ChannelStatus)
+          {
+            BOOST_FAIL("expected ChannelStatus, got type #" << i->type());
+          }
+
+        ndn::nfd::ChannelStatus entry(*i);
+
+        NFD_LOG_DEBUG("looking for channelstatus " << entry.getLocalUri());
+
+        std::map<std::string, ndn::nfd::ChannelStatus>::const_iterator expectedEntryPos =
+          m_expectedEntries.find(entry.getLocalUri());
+
+        BOOST_REQUIRE(expectedEntryPos != m_expectedEntries.end());
+        const ndn::nfd::ChannelStatus& expectedEntry = expectedEntryPos->second;
+
+        BOOST_CHECK_EQUAL(entry.getLocalUri(), expectedEntry.getLocalUri());
+
+        m_matchedEntries.insert(entry.getLocalUri());
+      }
+
+    BOOST_CHECK_EQUAL(m_matchedEntries.size(), m_expectedEntries.size());
+
+    m_finished = true;
+  }
+
+protected:
+  ChannelStatusPublisher::FactoryMap m_factories;
+  shared_ptr<InternalFace> m_face;
+  ChannelStatusPublisher m_publisher;
+
+  ndn::EncodingBuffer m_buffer;
+
+  std::map<std::string, ndn::nfd::ChannelStatus> m_expectedEntries;
+  std::set<std::string> m_matchedEntries;
+
+  bool m_finished;
+
+  ndn::KeyChain m_keyChain;
+};
+
+BOOST_FIXTURE_TEST_SUITE(MgmtChannelStatusPublisher, ChannelStatusPublisherFixture)
+
+BOOST_AUTO_TEST_CASE(Publish)
+{
+  const std::string protocol = "dummy";
+
+  shared_ptr<DummyProtocolFactory> factory(make_shared<DummyProtocolFactory>());
+  m_factories[protocol] = factory;
+
+  for (int i = 0; i < 10; ++i)
+    {
+      const std::string uri = protocol + "://path" + boost::lexical_cast<std::string>(i);
+      factory->addChannel(uri);
+
+      ndn::nfd::ChannelStatus expectedEntry;
+      expectedEntry.setLocalUri(DummyChannel(uri).getUri().toString());
+
+      m_expectedEntries[expectedEntry.getLocalUri()] = expectedEntry;
+    }
+
+  m_face->onReceiveData +=
+    bind(&ChannelStatusPublisherFixture::validatePublish, this, _1);
+
+  m_publisher.publish();
+  BOOST_REQUIRE(m_finished);
+}
+
+BOOST_AUTO_TEST_CASE(DuplicateFactories)
+{
+  const std::string protocol1 = "dummy1";
+  const std::string protocol2 = "dummy2";
+
+  shared_ptr<DummyProtocolFactory> factory(make_shared<DummyProtocolFactory>());
+  m_factories[protocol1] = factory;
+  m_factories[protocol2] = factory;
+
+  for (int i = 0; i < 10; ++i)
+    {
+      ndn::nfd::ChannelStatus expectedEntry;
+      const std::string uri = protocol1 + "://path" + boost::lexical_cast<std::string>(i);
+
+      factory->addChannel(uri);
+
+      expectedEntry.setLocalUri(DummyChannel(uri).getUri().toString());
+      m_expectedEntries[expectedEntry.getLocalUri()] = expectedEntry;
+    }
+
+  m_face->onReceiveData +=
+    bind(&ChannelStatusPublisherFixture::validatePublish, this, _1);
+
+  m_publisher.publish();
+  BOOST_REQUIRE(m_finished);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+
+
+
+} // namespace nfd
diff --git a/NFD/tests/daemon/mgmt/command-validator.cpp b/NFD/tests/daemon/mgmt/command-validator.cpp
new file mode 100644
index 0000000..ac32a6e
--- /dev/null
+++ b/NFD/tests/daemon/mgmt/command-validator.cpp
@@ -0,0 +1,657 @@
+/* -*- 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
+ *
+ * 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 "mgmt/command-validator.hpp"
+#include "core/config-file.hpp"
+
+#include "tests/test-common.hpp"
+
+#include <ndn-cxx/util/command-interest-generator.hpp>
+#include <ndn-cxx/util/io.hpp>
+#include <boost/filesystem.hpp>
+#include <fstream>
+
+namespace nfd {
+
+namespace tests {
+
+NFD_LOG_INIT("CommandValidatorTest");
+
+BOOST_FIXTURE_TEST_SUITE(MgmtCommandValidator, BaseFixture)
+
+// authorizations
+// {
+//   authorize
+//   {
+//     certfile "tests/daemon/mgmt/cert1.ndncert"
+//     privileges
+//     {
+//       fib
+//       stats
+//     }
+//   }
+
+//   authorize
+//   {
+//     certfile "tests/daemon/mgmt/cert2.ndncert"
+//     privileges
+//     {
+//       faces
+//     }
+//   }
+// }
+
+const std::string CONFIG =
+"authorizations\n"
+"{\n"
+"  authorize\n"
+"  {\n"
+"    certfile \"tests/daemon/mgmt/cert1.ndncert\"\n"
+"    privileges\n"
+"    {\n"
+"      fib\n"
+"      stats\n"
+"    }\n"
+"  }\n"
+"  authorize\n"
+"  {\n"
+"    certfile \"tests/daemon/mgmt/cert2.ndncert\"\n"
+"    privileges\n"
+"    {\n"
+"      faces\n"
+"    }\n"
+"  }\n"
+  "}\n";
+
+const boost::filesystem::path CONFIG_PATH =
+  boost::filesystem::current_path() /= std::string("unit-test-nfd.conf");
+
+class CommandValidatorTester
+{
+public:
+
+  CommandValidatorTester()
+    : m_validated(false),
+      m_validationFailed(false)
+  {
+
+  }
+
+  void
+  generateIdentity(const Name& prefix)
+  {
+    m_identityName = prefix;
+    m_identityName.appendVersion();
+
+    const Name certName = m_keys.createIdentity(m_identityName);
+
+    m_certificate = m_keys.getCertificate(certName);
+  }
+
+  void
+  saveIdentityToFile(const char* filename)
+  {
+    std::ofstream out;
+    out.open(filename);
+
+    BOOST_REQUIRE(out.is_open());
+    BOOST_REQUIRE(static_cast<bool>(m_certificate));
+
+    ndn::io::save<ndn::IdentityCertificate>(*m_certificate, out);
+
+    out.close();
+  }
+
+  const Name&
+  getIdentityName() const
+  {
+    BOOST_REQUIRE_NE(m_identityName, Name());
+    return m_identityName;
+  }
+
+  const Name&
+  getPublicKeyName() const
+  {
+    BOOST_REQUIRE(static_cast<bool>(m_certificate));
+    return m_certificate->getPublicKeyName();
+  }
+
+  void
+  onValidated(const shared_ptr<const Interest>& interest)
+  {
+    // NFD_LOG_DEBUG("validated command");
+    m_validated = true;
+  }
+
+  void
+  onValidationFailed(const shared_ptr<const Interest>& interest, const std::string& info)
+  {
+    NFD_LOG_DEBUG("validation failed: " << info);
+    m_validationFailed = true;
+  }
+
+  bool
+  commandValidated() const
+  {
+    return m_validated;
+  }
+
+  bool
+  commandValidationFailed() const
+  {
+    return m_validationFailed;
+  }
+
+  void
+  resetValidation()
+  {
+    m_validated = false;
+    m_validationFailed = false;
+  }
+
+  ~CommandValidatorTester()
+  {
+    m_keys.deleteIdentity(m_identityName);
+  }
+
+private:
+  bool m_validated;
+  bool m_validationFailed;
+
+  ndn::KeyChain m_keys;
+  Name m_identityName;
+  shared_ptr<ndn::IdentityCertificate> m_certificate;
+};
+
+class TwoValidatorFixture : public BaseFixture
+{
+public:
+  TwoValidatorFixture()
+  {
+    m_tester1.generateIdentity("/test/CommandValidator/TwoKeys/id1");
+    m_tester1.saveIdentityToFile("tests/daemon/mgmt/cert1.ndncert");
+
+    m_tester2.generateIdentity("/test/CommandValidator/TwoKeys/id2");
+    m_tester2.saveIdentityToFile("tests/daemon/mgmt/cert2.ndncert");
+  }
+
+  ~TwoValidatorFixture()
+  {
+    boost::system::error_code error;
+    boost::filesystem::remove("tests/daemon/mgmt/cert1.ndncert", error);
+    boost::filesystem::remove("tests/daemon/mgmt/cert2.ndncert", error);
+  }
+
+protected:
+  CommandValidatorTester m_tester1;
+  CommandValidatorTester m_tester2;
+};
+
+BOOST_FIXTURE_TEST_CASE(TwoKeys, TwoValidatorFixture)
+{
+  shared_ptr<Interest> fibCommand = make_shared<Interest>("/localhost/nfd/fib/insert");
+  shared_ptr<Interest> statsCommand = make_shared<Interest>("/localhost/nfd/stats/dosomething");
+  shared_ptr<Interest> facesCommand = make_shared<Interest>("/localhost/nfd/faces/create");
+
+  ndn::CommandInterestGenerator generator;
+  generator.generateWithIdentity(*fibCommand, m_tester1.getIdentityName());
+  generator.generateWithIdentity(*statsCommand, m_tester1.getIdentityName());
+  generator.generateWithIdentity(*facesCommand, m_tester2.getIdentityName());
+
+  ConfigFile config;
+  CommandValidator validator;
+  validator.addSupportedPrivilege("faces");
+  validator.addSupportedPrivilege("fib");
+  validator.addSupportedPrivilege("stats");
+
+  validator.setConfigFile(config);
+
+  config.parse(CONFIG, false, CONFIG_PATH.native());
+
+  validator.validate(*fibCommand,
+                     bind(&CommandValidatorTester::onValidated, &m_tester1, _1),
+                     bind(&CommandValidatorTester::onValidationFailed, &m_tester1, _1, _2));
+
+  BOOST_REQUIRE(m_tester1.commandValidated());
+  m_tester1.resetValidation();
+
+  validator.validate(*statsCommand,
+                     bind(&CommandValidatorTester::onValidated, &m_tester1, _1),
+                     bind(&CommandValidatorTester::onValidationFailed, &m_tester1, _1, _2));
+
+  BOOST_REQUIRE(m_tester1.commandValidated());
+
+  validator.validate(*facesCommand,
+                     bind(&CommandValidatorTester::onValidated, &m_tester2, _1),
+                     bind(&CommandValidatorTester::onValidationFailed, &m_tester2, _1, _2));
+
+  BOOST_REQUIRE(m_tester2.commandValidated());
+  m_tester2.resetValidation();
+
+  // use cert2 for fib command (authorized for cert1 only)
+  shared_ptr<Interest> unauthorizedFibCommand = make_shared<Interest>("/localhost/nfd/fib/insert");
+  generator.generateWithIdentity(*unauthorizedFibCommand, m_tester2.getIdentityName());
+
+  validator.validate(*unauthorizedFibCommand,
+                     bind(&CommandValidatorTester::onValidated, &m_tester2, _1),
+                     bind(&CommandValidatorTester::onValidationFailed, &m_tester2, _1, _2));
+
+  BOOST_REQUIRE(m_tester2.commandValidationFailed());
+}
+
+BOOST_FIXTURE_TEST_CASE(TwoKeysDryRun, TwoValidatorFixture)
+{
+  CommandValidatorTester tester1;
+  tester1.generateIdentity("/test/CommandValidator/TwoKeys/id1");
+  tester1.saveIdentityToFile("tests/daemon/mgmt/cert1.ndncert");
+
+  CommandValidatorTester tester2;
+  tester2.generateIdentity("/test/CommandValidator/TwoKeys/id2");
+  tester2.saveIdentityToFile("tests/daemon/mgmt/cert2.ndncert");
+
+  shared_ptr<Interest> fibCommand = make_shared<Interest>("/localhost/nfd/fib/insert");
+  shared_ptr<Interest> statsCommand = make_shared<Interest>("/localhost/nfd/stats/dosomething");
+  shared_ptr<Interest> facesCommand = make_shared<Interest>("/localhost/nfd/faces/create");
+
+  ndn::CommandInterestGenerator generator;
+  generator.generateWithIdentity(*fibCommand, m_tester1.getIdentityName());
+  generator.generateWithIdentity(*statsCommand, m_tester1.getIdentityName());
+  generator.generateWithIdentity(*facesCommand, m_tester2.getIdentityName());
+
+  ConfigFile config;
+  CommandValidator validator;
+  validator.addSupportedPrivilege("faces");
+  validator.addSupportedPrivilege("fib");
+  validator.addSupportedPrivilege("stats");
+
+  validator.setConfigFile(config);
+
+  config.parse(CONFIG, true, CONFIG_PATH.native());
+
+  validator.validate(*fibCommand,
+                     bind(&CommandValidatorTester::onValidated, &m_tester1, _1),
+                     bind(&CommandValidatorTester::onValidationFailed, &m_tester1, _1, _2));
+
+  BOOST_REQUIRE(m_tester1.commandValidationFailed());
+  m_tester1.resetValidation();
+
+  validator.validate(*statsCommand,
+                     bind(&CommandValidatorTester::onValidated, &m_tester1, _1),
+                     bind(&CommandValidatorTester::onValidationFailed, &m_tester1, _1, _2));
+
+  BOOST_REQUIRE(m_tester1.commandValidationFailed());
+
+  validator.validate(*facesCommand,
+                     bind(&CommandValidatorTester::onValidated, &m_tester2, _1),
+                     bind(&CommandValidatorTester::onValidationFailed, &m_tester2, _1, _2));
+
+  BOOST_REQUIRE(m_tester2.commandValidationFailed());
+  m_tester2.resetValidation();
+
+  // use cert2 for fib command (authorized for cert1 only)
+  shared_ptr<Interest> unauthorizedFibCommand = make_shared<Interest>("/localhost/nfd/fib/insert");
+  generator.generateWithIdentity(*unauthorizedFibCommand, m_tester2.getIdentityName());
+
+  validator.validate(*unauthorizedFibCommand,
+                     bind(&CommandValidatorTester::onValidated, &m_tester2, _1),
+                     bind(&CommandValidatorTester::onValidationFailed, &m_tester2, _1, _2));
+
+  BOOST_REQUIRE(m_tester2.commandValidationFailed());
+}
+
+BOOST_AUTO_TEST_CASE(NoAuthorizeSections)
+{
+  const std::string NO_AUTHORIZE_CONFIG =
+    "authorizations\n"
+    "{\n"
+    "}\n";
+
+  ConfigFile config;
+  CommandValidator validator;
+
+  validator.setConfigFile(config);
+  BOOST_CHECK_THROW(config.parse(NO_AUTHORIZE_CONFIG, false, CONFIG_PATH.native()), ConfigFile::Error);
+}
+
+BOOST_AUTO_TEST_CASE(NoPrivilegesSections)
+{
+  const std::string NO_PRIVILEGES_CONFIG =
+    "authorizations\n"
+    "{\n"
+    "  authorize\n"
+    "  {\n"
+    "    certfile \"tests/daemon/mgmt/cert1.ndncert\"\n"
+    "  }\n"
+    "}\n";
+
+  ConfigFile config;
+  CommandValidator validator;
+
+  validator.setConfigFile(config);
+
+  BOOST_CHECK_THROW(config.parse(NO_PRIVILEGES_CONFIG, false, CONFIG_PATH.native()), ConfigFile::Error);
+}
+
+BOOST_AUTO_TEST_CASE(InvalidCertfile)
+{
+  const std::string INVALID_CERT_CONFIG =
+    "authorizations\n"
+    "{\n"
+    "  authorize\n"
+    "  {\n"
+    "    certfile \"tests/daemon/mgmt/notacertfile.ndncert\"\n"
+    "    privileges\n"
+    "    {\n"
+    "      fib\n"
+    "      stats\n"
+    "    }\n"
+    "  }\n"
+    "}\n";
+
+  ConfigFile config;
+  CommandValidator validator;
+
+  validator.setConfigFile(config);
+  BOOST_CHECK_THROW(config.parse(INVALID_CERT_CONFIG, false, CONFIG_PATH.native()), ConfigFile::Error);
+}
+
+BOOST_AUTO_TEST_CASE(NoCertfile)
+{
+  const std::string NO_CERT_CONFIG =
+    "authorizations\n"
+    "{\n"
+    "  authorize\n"
+    "  {\n"
+    "    privileges\n"
+    "    {\n"
+    "      fib\n"
+    "      stats\n"
+    "    }\n"
+    "  }\n"
+    "}\n";
+
+
+  ConfigFile config;
+  CommandValidator validator;
+
+  validator.setConfigFile(config);
+  BOOST_CHECK_THROW(config.parse(NO_CERT_CONFIG, false, CONFIG_PATH.native()), ConfigFile::Error);
+}
+
+BOOST_AUTO_TEST_CASE(MalformedCert)
+{
+    const std::string MALFORMED_CERT_CONFIG =
+    "authorizations\n"
+    "{\n"
+    "  authorize\n"
+    "  {\n"
+    "    certfile \"tests/daemon/mgmt/malformed.ndncert\"\n"
+    "    privileges\n"
+    "    {\n"
+    "      fib\n"
+    "      stats\n"
+    "    }\n"
+    "  }\n"
+    "}\n";
+
+
+  ConfigFile config;
+  CommandValidator validator;
+
+  validator.setConfigFile(config);
+  BOOST_CHECK_THROW(config.parse(MALFORMED_CERT_CONFIG, false, CONFIG_PATH.native()), ConfigFile::Error);
+}
+
+bool
+validateErrorMessage(const std::string& expectedMessage, const ConfigFile::Error& error)
+{
+  bool gotExpected = error.what() == expectedMessage;
+  if (!gotExpected)
+    {
+      NFD_LOG_WARN("\ncaught exception: " << error.what()
+                    << "\n\nexpected exception: " << expectedMessage);
+    }
+  return gotExpected;
+}
+
+BOOST_AUTO_TEST_CASE(NoAuthorizeSectionsDryRun)
+{
+  const std::string NO_AUTHORIZE_CONFIG =
+    "authorizations\n"
+    "{\n"
+    "}\n";
+
+  ConfigFile config;
+  CommandValidator validator;
+
+  validator.setConfigFile(config);
+  BOOST_CHECK_EXCEPTION(config.parse(NO_AUTHORIZE_CONFIG, true, CONFIG_PATH.native()),
+                        ConfigFile::Error,
+                        bind(&validateErrorMessage,
+                             "No authorize sections found", _1));
+}
+
+BOOST_FIXTURE_TEST_CASE(NoPrivilegesSectionsDryRun, TwoValidatorFixture)
+{
+  const std::string NO_PRIVILEGES_CONFIG =
+    "authorizations\n"
+    "{\n"
+    "  authorize\n"
+    "  {\n"
+    "    certfile \"tests/daemon/mgmt/cert1.ndncert\"\n"
+    "  }\n"
+    "  authorize\n"
+    "  {\n"
+    "    certfile \"tests/daemon/mgmt/cert2.ndncert\"\n"
+    "  }\n"
+    "}\n";
+
+  ConfigFile config;
+  CommandValidator validator;
+
+  validator.setConfigFile(config);
+
+  std::stringstream expectedError;
+  expectedError << "No privileges section found for certificate file tests/daemon/mgmt/cert1.ndncert "
+                << "(" << m_tester1.getPublicKeyName().toUri() << ")\n"
+                << "No privileges section found for certificate file tests/daemon/mgmt/cert2.ndncert "
+                << "(" << m_tester2.getPublicKeyName().toUri() << ")";
+
+  BOOST_CHECK_EXCEPTION(config.parse(NO_PRIVILEGES_CONFIG, true, CONFIG_PATH.native()),
+                        ConfigFile::Error,
+                        bind(&validateErrorMessage, expectedError.str(), _1));
+}
+
+BOOST_AUTO_TEST_CASE(InvalidCertfileDryRun)
+{
+  using namespace boost::filesystem;
+
+  const std::string INVALID_KEY_CONFIG =
+    "authorizations\n"
+    "{\n"
+    "  authorize\n"
+    "  {\n"
+    "    certfile \"tests/daemon/mgmt/notacertfile.ndncert\"\n"
+    "    privileges\n"
+    "    {\n"
+    "      fib\n"
+    "      stats\n"
+    "    }\n"
+    "  }\n"
+    "  authorize\n"
+    "  {\n"
+    "    certfile \"tests/daemon/mgmt/stillnotacertfile.ndncert\"\n"
+    "    privileges\n"
+    "    {\n"
+    "    }\n"
+    "  }\n"
+    "}\n";
+
+  ConfigFile config;
+  CommandValidator validator;
+
+  validator.setConfigFile(config);
+
+  std::stringstream error;
+  error << "Unable to open certificate file "
+        << absolute("tests/daemon/mgmt/notacertfile.ndncert").native() << "\n"
+        << "Unable to open certificate file "
+        << absolute("tests/daemon/mgmt/stillnotacertfile.ndncert").native();
+
+  BOOST_CHECK_EXCEPTION(config.parse(INVALID_KEY_CONFIG, true, CONFIG_PATH.native()),
+                        ConfigFile::Error,
+                        bind(&validateErrorMessage, error.str(), _1));
+}
+
+BOOST_AUTO_TEST_CASE(NoCertfileDryRun)
+{
+  const std::string NO_CERT_CONFIG =
+    "authorizations\n"
+    "{\n"
+    "  authorize\n"
+    "  {\n"
+    "    privileges\n"
+    "    {\n"
+    "      fib\n"
+    "      stats\n"
+    "    }\n"
+    "  }\n"
+    "  authorize\n"
+    "  {\n"
+    "  }\n"
+    "}\n";
+
+
+  ConfigFile config;
+  CommandValidator validator;
+
+  validator.setConfigFile(config);
+  BOOST_CHECK_EXCEPTION(config.parse(NO_CERT_CONFIG, true, CONFIG_PATH.native()),
+                        ConfigFile::Error,
+                        bind(&validateErrorMessage,
+                             "No certfile specified\n"
+                             "No certfile specified", _1));
+}
+
+BOOST_AUTO_TEST_CASE(MalformedCertDryRun)
+{
+  using namespace boost::filesystem;
+
+  const std::string MALFORMED_CERT_CONFIG =
+    "authorizations\n"
+    "{\n"
+    "  authorize\n"
+    "  {\n"
+    "    certfile \"tests/daemon/mgmt/malformed.ndncert\"\n"
+    "    privileges\n"
+    "    {\n"
+    "      fib\n"
+    "      stats\n"
+    "    }\n"
+    "  }\n"
+    "  authorize\n"
+    "  {\n"
+    "    certfile \"tests/daemon/mgmt/malformed.ndncert\"\n"
+    "  }\n"
+    "}\n";
+
+
+  ConfigFile config;
+  CommandValidator validator;
+
+  validator.setConfigFile(config);
+
+  std::stringstream error;
+  error << "Malformed certificate file "
+        << absolute("tests/daemon/mgmt/malformed.ndncert").native() << "\n"
+        << "Malformed certificate file "
+        << absolute("tests/daemon/mgmt/malformed.ndncert").native();
+
+  BOOST_CHECK_EXCEPTION(config.parse(MALFORMED_CERT_CONFIG, true, CONFIG_PATH.native()),
+                        ConfigFile::Error,
+                        bind(&validateErrorMessage, error.str(), _1));
+}
+
+BOOST_FIXTURE_TEST_CASE(Wildcard, TwoValidatorFixture)
+{
+  const std::string WILDCARD_CERT_CONFIG =
+    "authorizations\n"
+    "{\n"
+    "  authorize\n"
+    "  {\n"
+    "    certfile any\n"
+    "    privileges\n"
+    "    {\n"
+    "      faces\n"
+    "      stats\n"
+    "    }\n"
+    "  }\n"
+    "}\n";
+
+  shared_ptr<Interest> fibCommand = make_shared<Interest>("/localhost/nfd/fib/insert");
+  shared_ptr<Interest> statsCommand = make_shared<Interest>("/localhost/nfd/stats/dosomething");
+  shared_ptr<Interest> facesCommand = make_shared<Interest>("/localhost/nfd/faces/create");
+
+  ndn::CommandInterestGenerator generator;
+  generator.generateWithIdentity(*fibCommand, m_tester1.getIdentityName());
+  generator.generateWithIdentity(*statsCommand, m_tester1.getIdentityName());
+  generator.generateWithIdentity(*facesCommand, m_tester1.getIdentityName());
+
+  ConfigFile config;
+  CommandValidator validator;
+  validator.addSupportedPrivilege("faces");
+  validator.addSupportedPrivilege("fib");
+  validator.addSupportedPrivilege("stats");
+
+  validator.setConfigFile(config);
+
+  config.parse(WILDCARD_CERT_CONFIG, false, CONFIG_PATH.native());
+
+  validator.validate(*fibCommand,
+                     bind(&CommandValidatorTester::onValidated, &m_tester1, _1),
+                     bind(&CommandValidatorTester::onValidationFailed, &m_tester1, _1, _2));
+
+  BOOST_REQUIRE(m_tester1.commandValidationFailed());
+  m_tester1.resetValidation();
+
+  validator.validate(*statsCommand,
+                     bind(&CommandValidatorTester::onValidated, &m_tester1, _1),
+                     bind(&CommandValidatorTester::onValidationFailed, &m_tester1, _1, _2));
+
+  BOOST_REQUIRE(m_tester1.commandValidated());
+  m_tester1.resetValidation();
+
+  validator.validate(*facesCommand,
+                     bind(&CommandValidatorTester::onValidated, &m_tester1, _1),
+                     bind(&CommandValidatorTester::onValidationFailed, &m_tester1, _1, _2));
+
+  BOOST_REQUIRE(m_tester1.commandValidated());
+  m_tester1.resetValidation();
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+
+} // namespace nfd
diff --git a/NFD/tests/daemon/mgmt/face-manager.cpp b/NFD/tests/daemon/mgmt/face-manager.cpp
new file mode 100644
index 0000000..047f00c
--- /dev/null
+++ b/NFD/tests/daemon/mgmt/face-manager.cpp
@@ -0,0 +1,1859 @@
+/* -*- 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/>.
+ */
+
+#include "mgmt/face-manager.hpp"
+#include "mgmt/internal-face.hpp"
+#include "mgmt/face-status-publisher.hpp"
+
+#include "face/face.hpp"
+#include "../face/dummy-face.hpp"
+#include "fw/face-table.hpp"
+#include "fw/forwarder.hpp"
+#include "face/udp-factory.hpp"
+
+#ifdef HAVE_LIBPCAP
+#include "face/ethernet-factory.hpp"
+#endif // HAVE_LIBPCAP
+
+#include "common.hpp"
+#include "tests/test-common.hpp"
+#include "validation-common.hpp"
+#include "face-status-publisher-common.hpp"
+#include "face-query-status-publisher-common.hpp"
+#include "channel-status-common.hpp"
+
+#include <ndn-cxx/encoding/tlv.hpp>
+#include <ndn-cxx/management/nfd-face-event-notification.hpp>
+
+namespace nfd {
+namespace tests {
+
+NFD_LOG_INIT("FaceManagerTest");
+
+class FaceManagerTestFace : public DummyFace
+{
+public:
+
+  FaceManagerTestFace()
+    : m_closeFired(false)
+  {
+
+  }
+
+  virtual
+  ~FaceManagerTestFace()
+  {
+
+  }
+
+  virtual void
+  close()
+  {
+    m_closeFired = true;
+  }
+
+  bool
+  didCloseFire() const
+  {
+    return m_closeFired;
+  }
+
+private:
+  bool m_closeFired;
+};
+
+class TestFaceTable : public FaceTable
+{
+public:
+  TestFaceTable(Forwarder& forwarder)
+    : FaceTable(forwarder),
+      m_addFired(false),
+      m_getFired(false),
+      m_dummy(make_shared<FaceManagerTestFace>())
+  {
+
+  }
+
+  virtual
+  ~TestFaceTable()
+  {
+
+  }
+
+  virtual void
+  add(shared_ptr<Face> face)
+  {
+    m_addFired = true;
+  }
+
+  virtual shared_ptr<Face>
+  get(FaceId id) const
+  {
+    m_getFired = true;
+    return m_dummy;
+  }
+
+  bool
+  didAddFire() const
+  {
+    return m_addFired;
+  }
+
+  bool
+  didGetFire() const
+  {
+    return m_getFired;
+  }
+
+  void
+  reset()
+  {
+    m_addFired = false;
+    m_getFired = false;
+  }
+
+  shared_ptr<FaceManagerTestFace>&
+  getDummyFace()
+  {
+    return m_dummy;
+  }
+
+private:
+  bool m_addFired;
+  mutable bool m_getFired;
+  shared_ptr<FaceManagerTestFace> m_dummy;
+};
+
+
+class TestFaceTableFixture : public BaseFixture
+{
+public:
+  TestFaceTableFixture()
+    : m_faceTable(m_forwarder)
+  {
+
+  }
+
+  virtual
+  ~TestFaceTableFixture()
+  {
+
+  }
+
+protected:
+  Forwarder m_forwarder;
+  TestFaceTable m_faceTable;
+};
+
+class TestFaceManagerCommon
+{
+public:
+  TestFaceManagerCommon()
+    : m_face(make_shared<InternalFace>()),
+      m_callbackFired(false)
+  {
+
+  }
+
+  virtual
+  ~TestFaceManagerCommon()
+  {
+
+  }
+
+  shared_ptr<InternalFace>&
+  getFace()
+  {
+    return m_face;
+  }
+
+  void
+  validateControlResponseCommon(const Data& response,
+                                const Name& expectedName,
+                                uint32_t expectedCode,
+                                const std::string& expectedText,
+                                ControlResponse& control)
+  {
+    m_callbackFired = true;
+    Block controlRaw = response.getContent().blockFromValue();
+
+    control.wireDecode(controlRaw);
+
+    // NFD_LOG_DEBUG("received control response"
+    //               << " Name: " << response.getName()
+    //               << " code: " << control.getCode()
+    //               << " text: " << control.getText());
+
+    BOOST_CHECK_EQUAL(response.getName(), expectedName);
+    BOOST_CHECK_EQUAL(control.getCode(), expectedCode);
+    BOOST_CHECK_EQUAL(control.getText(), expectedText);
+  }
+
+  void
+  validateControlResponse(const Data& response,
+                          const Name& expectedName,
+                          uint32_t expectedCode,
+                          const std::string& expectedText)
+  {
+    ControlResponse control;
+    validateControlResponseCommon(response, expectedName,
+                                  expectedCode, expectedText, control);
+
+    if (!control.getBody().empty())
+      {
+        BOOST_FAIL("found unexpected control response body");
+      }
+  }
+
+  void
+  validateControlResponse(const Data& response,
+                          const Name& expectedName,
+                          uint32_t expectedCode,
+                          const std::string& expectedText,
+                          const Block& expectedBody)
+  {
+    ControlResponse control;
+    validateControlResponseCommon(response, expectedName,
+                                  expectedCode, expectedText, control);
+
+    BOOST_REQUIRE(!control.getBody().empty());
+    BOOST_REQUIRE(control.getBody().value_size() == expectedBody.value_size());
+
+    BOOST_CHECK(memcmp(control.getBody().value(), expectedBody.value(),
+                       expectedBody.value_size()) == 0);
+
+  }
+
+  bool
+  didCallbackFire() const
+  {
+    return m_callbackFired;
+  }
+
+  void
+  resetCallbackFired()
+  {
+    m_callbackFired = false;
+  }
+
+protected:
+  shared_ptr<InternalFace> m_face;
+  bool m_callbackFired;
+  ndn::KeyChain m_testKeyChain;
+};
+
+class FaceManagerFixture : public TestFaceTableFixture, public TestFaceManagerCommon
+{
+public:
+  FaceManagerFixture()
+    : m_manager(m_faceTable, m_face, m_testKeyChain)
+  {
+    m_manager.setConfigFile(m_config);
+  }
+
+  virtual
+  ~FaceManagerFixture()
+  {
+
+  }
+
+  void
+  parseConfig(const std::string configuration, bool isDryRun)
+  {
+    m_config.parse(configuration, isDryRun, "dummy-config");
+  }
+
+  FaceManager&
+  getManager()
+  {
+    return m_manager;
+  }
+
+  void
+  addInterestRule(const std::string& regex,
+                  ndn::IdentityCertificate& certificate)
+  {
+    m_manager.addInterestRule(regex, certificate);
+  }
+
+  bool
+  didFaceTableAddFire() const
+  {
+    return m_faceTable.didAddFire();
+  }
+
+  bool
+  didFaceTableGetFire() const
+  {
+    return m_faceTable.didGetFire();
+  }
+
+  void
+  resetFaceTable()
+  {
+    m_faceTable.reset();
+  }
+
+protected:
+  FaceManager m_manager;
+  ConfigFile m_config;
+};
+
+BOOST_FIXTURE_TEST_SUITE(MgmtFaceManager, FaceManagerFixture)
+
+bool
+isExpectedException(const ConfigFile::Error& error, const std::string& expectedMessage)
+{
+  if (error.what() != expectedMessage)
+    {
+      NFD_LOG_ERROR("expected: " << expectedMessage << "\tgot: " << error.what());
+    }
+  return error.what() == expectedMessage;
+}
+
+#ifdef HAVE_UNIX_SOCKETS
+
+BOOST_AUTO_TEST_CASE(TestProcessSectionUnix)
+{
+  const std::string CONFIG =
+    "face_system\n"
+    "{\n"
+    "  unix\n"
+    "  {\n"
+    "    path /tmp/nfd.sock\n"
+    "  }\n"
+    "}\n";
+  BOOST_TEST_CHECKPOINT("Calling parse");
+  BOOST_CHECK_NO_THROW(parseConfig(CONFIG, false));
+}
+
+BOOST_AUTO_TEST_CASE(TestProcessSectionUnixDryRun)
+{
+  const std::string CONFIG =
+    "face_system\n"
+    "{\n"
+    "  unix\n"
+    "  {\n"
+    "    path /var/run/nfd.sock\n"
+    "  }\n"
+    "}\n";
+
+  BOOST_CHECK_NO_THROW(parseConfig(CONFIG, true));
+}
+
+BOOST_AUTO_TEST_CASE(TestProcessSectionUnixUnknownOption)
+{
+  const std::string CONFIG =
+    "face_system\n"
+    "{\n"
+    "  unix\n"
+    "  {\n"
+    "    hello\n"
+    "  }\n"
+    "}\n";
+  BOOST_CHECK_EXCEPTION(parseConfig(CONFIG, false), ConfigFile::Error,
+                        bind(&isExpectedException, _1,
+                             "Unrecognized option \"hello\" in \"unix\" section"));
+}
+
+#endif // HAVE_UNIX_SOCKETS
+
+
+BOOST_AUTO_TEST_CASE(TestProcessSectionTcp)
+{
+  const std::string CONFIG =
+    "face_system\n"
+    "{\n"
+    "  tcp\n"
+    "  {\n"
+    "    listen yes\n"
+    "    port 6363\n"
+    "    enable_v4 yes\n"
+    "    enable_v6 yes\n"
+    "  }\n"
+    "}\n";
+  try
+    {
+      parseConfig(CONFIG, false);
+    }
+  catch (const std::runtime_error& e)
+    {
+      const std::string reason = e.what();
+      if (reason.find("Address in use") != std::string::npos)
+        {
+          BOOST_FAIL(reason);
+        }
+    }
+}
+
+BOOST_AUTO_TEST_CASE(TestProcessSectionTcpDryRun)
+{
+  const std::string CONFIG =
+    "face_system\n"
+    "{\n"
+    "  tcp\n"
+    "  {\n"
+    "    listen yes\n"
+    "    port 6363\n"
+    "    enable_v4 yes\n"
+    "    enable_v6 yes\n"
+    "  }\n"
+    "}\n";
+  BOOST_CHECK_NO_THROW(parseConfig(CONFIG, true));
+}
+
+BOOST_AUTO_TEST_CASE(TestProcessSectionTcpBadListen)
+{
+  const std::string CONFIG =
+    "face_system\n"
+    "{\n"
+    "  tcp\n"
+    "  {\n"
+    "    listen hello\n"
+    "  }\n"
+    "}\n";
+  BOOST_CHECK_EXCEPTION(parseConfig(CONFIG, false), ConfigFile::Error,
+                        bind(&isExpectedException, _1,
+                             "Invalid value for option \"listen\" in \"tcp\" section"));
+}
+
+BOOST_AUTO_TEST_CASE(TestProcessSectionTcpChannelsDisabled)
+{
+  const std::string CONFIG =
+    "face_system\n"
+    "{\n"
+    "  tcp\n"
+    "  {\n"
+    "    port 6363\n"
+    "    enable_v4 no\n"
+    "    enable_v6 no\n"
+    "  }\n"
+    "}\n";
+  BOOST_CHECK_EXCEPTION(parseConfig(CONFIG, false), ConfigFile::Error,
+                        bind(&isExpectedException, _1,
+                             "IPv4 and IPv6 channels have been disabled."
+                             " Remove \"tcp\" section to disable TCP channels or"
+                             " re-enable at least one channel type."));
+}
+
+BOOST_AUTO_TEST_CASE(TestProcessSectionTcpUnknownOption)
+{
+  const std::string CONFIG =
+    "face_system\n"
+    "{\n"
+    "  tcp\n"
+    "  {\n"
+    "    hello\n"
+    "  }\n"
+    "}\n";
+  BOOST_CHECK_EXCEPTION(parseConfig(CONFIG, false), ConfigFile::Error,
+                        bind(&isExpectedException, _1,
+                             "Unrecognized option \"hello\" in \"tcp\" section"));
+}
+
+BOOST_AUTO_TEST_CASE(TestProcessSectionUdp)
+{
+  const std::string CONFIG =
+    "face_system\n"
+    "{\n"
+    "  udp\n"
+    "  {\n"
+    "    port 6363\n"
+    "    enable_v4 yes\n"
+    "    enable_v6 yes\n"
+    "    idle_timeout 30\n"
+    "    keep_alive_interval 25\n"
+    "    mcast yes\n"
+    "    mcast_port 56363\n"
+    "    mcast_group 224.0.23.170\n"
+    "  }\n"
+    "}\n";
+  BOOST_CHECK_NO_THROW(parseConfig(CONFIG, false));
+}
+
+BOOST_AUTO_TEST_CASE(TestProcessSectionUdpDryRun)
+{
+  const std::string CONFIG =
+    "face_system\n"
+    "{\n"
+    "  udp\n"
+    "  {\n"
+    "    port 6363\n"
+    "    idle_timeout 30\n"
+    "    keep_alive_interval 25\n"
+    "    mcast yes\n"
+    "    mcast_port 56363\n"
+    "    mcast_group 224.0.23.170\n"
+    "  }\n"
+    "}\n";
+  BOOST_CHECK_NO_THROW(parseConfig(CONFIG, true));
+}
+
+BOOST_AUTO_TEST_CASE(TestProcessSectionUdpBadIdleTimeout)
+{
+  const std::string CONFIG =
+    "face_system\n"
+    "{\n"
+    "  udp\n"
+    "  {\n"
+    "    idle_timeout hello\n"
+    "  }\n"
+    "}\n";
+
+  BOOST_CHECK_EXCEPTION(parseConfig(CONFIG, false), ConfigFile::Error,
+                        bind(&isExpectedException, _1,
+                             "Invalid value for option \"idle_timeout\" in \"udp\" section"));
+}
+
+BOOST_AUTO_TEST_CASE(TestProcessSectionUdpBadMcast)
+{
+  const std::string CONFIG =
+    "face_system\n"
+    "{\n"
+    "  udp\n"
+    "  {\n"
+    "    mcast hello\n"
+    "  }\n"
+    "}\n";
+
+  BOOST_CHECK_EXCEPTION(parseConfig(CONFIG, false), ConfigFile::Error,
+                        bind(&isExpectedException, _1,
+                             "Invalid value for option \"mcast\" in \"udp\" section"));
+}
+
+BOOST_AUTO_TEST_CASE(TestProcessSectionUdpBadMcastGroup)
+{
+  const std::string CONFIG =
+    "face_system\n"
+    "{\n"
+    "  udp\n"
+    "  {\n"
+    "    mcast no\n"
+    "    mcast_port 50\n"
+    "    mcast_group hello\n"
+    "  }\n"
+    "}\n";
+
+  BOOST_CHECK_EXCEPTION(parseConfig(CONFIG, false), ConfigFile::Error,
+                        bind(&isExpectedException, _1,
+                             "Invalid value for option \"mcast_group\" in \"udp\" section"));
+}
+
+BOOST_AUTO_TEST_CASE(TestProcessSectionUdpBadMcastGroupV6)
+{
+  const std::string CONFIG =
+    "face_system\n"
+    "{\n"
+    "  udp\n"
+    "  {\n"
+    "    mcast no\n"
+    "    mcast_port 50\n"
+    "    mcast_group ::1\n"
+    "  }\n"
+    "}\n";
+
+  BOOST_CHECK_EXCEPTION(parseConfig(CONFIG, false), ConfigFile::Error,
+                        bind(&isExpectedException, _1,
+                             "Invalid value for option \"mcast_group\" in \"udp\" section"));
+}
+
+BOOST_AUTO_TEST_CASE(TestProcessSectionUdpChannelsDisabled)
+{
+  const std::string CONFIG =
+    "face_system\n"
+    "{\n"
+    "  udp\n"
+    "  {\n"
+    "    port 6363\n"
+    "    enable_v4 no\n"
+    "    enable_v6 no\n"
+    "    idle_timeout 30\n"
+    "    keep_alive_interval 25\n"
+    "    mcast yes\n"
+    "    mcast_port 56363\n"
+    "    mcast_group 224.0.23.170\n"
+    "  }\n"
+    "}\n";
+  BOOST_CHECK_EXCEPTION(parseConfig(CONFIG, false), ConfigFile::Error,
+                        bind(&isExpectedException, _1,
+                             "IPv4 and IPv6 channels have been disabled."
+                             " Remove \"udp\" section to disable UDP channels or"
+                             " re-enable at least one channel type."));
+}
+
+BOOST_AUTO_TEST_CASE(TestProcessSectionUdpConflictingMcast)
+{
+  const std::string CONFIG =
+    "face_system\n"
+    "{\n"
+    "  udp\n"
+    "  {\n"
+    "    port 6363\n"
+    "    enable_v4 no\n"
+    "    enable_v6 yes\n"
+    "    idle_timeout 30\n"
+    "    keep_alive_interval 25\n"
+    "    mcast yes\n"
+    "    mcast_port 56363\n"
+    "    mcast_group 224.0.23.170\n"
+    "  }\n"
+    "}\n";
+  BOOST_CHECK_EXCEPTION(parseConfig(CONFIG, false), ConfigFile::Error,
+                        bind(&isExpectedException, _1,
+                             "IPv4 multicast requested, but IPv4 channels"
+                             " have been disabled (conflicting configuration options set)"));
+}
+
+
+
+BOOST_AUTO_TEST_CASE(TestProcessSectionUdpUnknownOption)
+{
+  const std::string CONFIG =
+    "face_system\n"
+    "{\n"
+    "  udp\n"
+    "  {\n"
+    "    hello\n"
+    "  }\n"
+    "}\n";
+  BOOST_CHECK_EXCEPTION(parseConfig(CONFIG, false), ConfigFile::Error,
+                        bind(&isExpectedException, _1,
+                             "Unrecognized option \"hello\" in \"udp\" section"));
+}
+
+
+BOOST_AUTO_TEST_CASE(TestProcessSectionUdpMulticastReinit)
+{
+  const std::string CONFIG_WITH_MCAST =
+    "face_system\n"
+    "{\n"
+    "  udp\n"
+    "  {\n"
+    "    mcast yes\n"
+    "  }\n"
+    "}\n";
+  BOOST_CHECK_NO_THROW(parseConfig(CONFIG_WITH_MCAST, false));
+
+  shared_ptr<UdpFactory> factory = static_pointer_cast<UdpFactory>(getManager().findFactory("udp"));
+  BOOST_REQUIRE(static_cast<bool>(factory));
+
+  if (factory->getMulticastFaces().size() == 0) {
+    BOOST_TEST_MESSAGE("Destroying multicast faces is not tested because "
+                       "no UDP multicast faces are available");
+  }
+
+  const std::string CONFIG_WITHOUT_MCAST =
+    "face_system\n"
+    "{\n"
+    "  udp\n"
+    "  {\n"
+    "    mcast no\n"
+    "  }\n"
+    "}\n";
+  BOOST_CHECK_NO_THROW(parseConfig(CONFIG_WITHOUT_MCAST, false));
+  BOOST_CHECK_EQUAL(factory->getMulticastFaces().size(), 0);
+}
+
+
+#ifdef HAVE_LIBPCAP
+
+BOOST_AUTO_TEST_CASE(TestProcessSectionEther)
+{
+
+  const std::string CONFIG =
+    "face_system\n"
+    "{\n"
+    "  ether\n"
+    "  {\n"
+    "    mcast yes\n"
+    "    mcast_group 01:00:5E:00:17:AA\n"
+    "  }\n"
+    "}\n";
+
+  BOOST_CHECK_NO_THROW(parseConfig(CONFIG, false));
+}
+
+BOOST_AUTO_TEST_CASE(TestProcessSectionEtherDryRun)
+{
+  const std::string CONFIG =
+    "face_system\n"
+    "{\n"
+    "  ether\n"
+    "  {\n"
+    "    mcast yes\n"
+    "    mcast_group 01:00:5E:00:17:AA\n"
+    "  }\n"
+    "}\n";
+
+  BOOST_CHECK_NO_THROW(parseConfig(CONFIG, true));
+}
+
+BOOST_AUTO_TEST_CASE(TestProcessSectionEtherBadMcast)
+{
+  const std::string CONFIG =
+    "face_system\n"
+    "{\n"
+    "  ether\n"
+    "  {\n"
+    "    mcast hello\n"
+    "  }\n"
+    "}\n";
+
+  BOOST_CHECK_EXCEPTION(parseConfig(CONFIG, false), ConfigFile::Error,
+                        bind(&isExpectedException, _1,
+                             "Invalid value for option \"mcast\" in \"ether\" section"));
+}
+
+BOOST_AUTO_TEST_CASE(TestProcessSectionEtherBadMcastGroup)
+{
+  const std::string CONFIG =
+    "face_system\n"
+    "{\n"
+    "  ether\n"
+    "  {\n"
+    "    mcast yes\n"
+    "    mcast_group\n"
+    "  }\n"
+    "}\n";
+
+  BOOST_CHECK_EXCEPTION(parseConfig(CONFIG, false), ConfigFile::Error,
+                        bind(&isExpectedException, _1,
+                             "Invalid value for option \"mcast_group\" in \"ether\" section"));
+}
+
+BOOST_AUTO_TEST_CASE(TestProcessSectionEtherUnknownOption)
+{
+  const std::string CONFIG =
+    "face_system\n"
+    "{\n"
+    "  ether\n"
+    "  {\n"
+    "    hello\n"
+    "  }\n"
+    "}\n";
+  BOOST_CHECK_EXCEPTION(parseConfig(CONFIG, false), ConfigFile::Error,
+                        bind(&isExpectedException, _1,
+                             "Unrecognized option \"hello\" in \"ether\" section"));
+}
+
+BOOST_AUTO_TEST_CASE(TestProcessSectionEtherMulticastReinit)
+{
+  const std::string CONFIG_WITH_MCAST =
+    "face_system\n"
+    "{\n"
+    "  ether\n"
+    "  {\n"
+    "    mcast yes\n"
+    "  }\n"
+    "}\n";
+  BOOST_CHECK_NO_THROW(parseConfig(CONFIG_WITH_MCAST, false));
+
+  shared_ptr<EthernetFactory> factory =
+    static_pointer_cast<EthernetFactory>(getManager().findFactory("ether"));
+  BOOST_REQUIRE(static_cast<bool>(factory));
+
+  if (factory->getMulticastFaces().size() == 0) {
+    BOOST_TEST_MESSAGE("Destroying multicast faces is not tested because "
+                       "no Ethernet multicast faces are available");
+  }
+
+  const std::string CONFIG_WITHOUT_MCAST =
+    "face_system\n"
+    "{\n"
+    "  ether\n"
+    "  {\n"
+    "    mcast no\n"
+    "  }\n"
+    "}\n";
+  BOOST_CHECK_NO_THROW(parseConfig(CONFIG_WITHOUT_MCAST, false));
+  BOOST_CHECK_EQUAL(factory->getMulticastFaces().size(), 0);
+}
+
+#endif // HAVE_LIBPCAP
+
+BOOST_AUTO_TEST_CASE(ShortName)
+{
+  shared_ptr<Interest> command(make_shared<Interest>("/localhost/nfd/faces"));
+
+  getFace()->onReceiveData += [this, command] (const Data& response) {
+    this->validateControlResponse(response, command->getName(), 400, "Malformed command");
+  };
+
+  getFace()->sendInterest(*command);
+  g_io.run_one();
+
+  BOOST_REQUIRE(didCallbackFire());
+}
+
+BOOST_AUTO_TEST_CASE(MalformedCommmand)
+{
+  shared_ptr<Interest> command(make_shared<Interest>("/localhost/nfd/faces"));
+
+  getFace()->onReceiveData += [this, command] (const Data& response) {
+    this->validateControlResponse(response, command->getName(), 400, "Malformed command");
+  };
+
+  getManager().onFaceRequest(*command);
+
+  BOOST_REQUIRE(didCallbackFire());
+}
+
+BOOST_AUTO_TEST_CASE(UnsignedCommand)
+{
+  ControlParameters parameters;
+  parameters.setUri("tcp4://127.0.0.1:6363");
+
+  Block encodedParameters(parameters.wireEncode());
+
+  Name commandName("/localhost/nfd/faces");
+  commandName.append("create");
+  commandName.append(encodedParameters);
+
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+
+  getFace()->onReceiveData += [this, command] (const Data& response) {
+    this->validateControlResponse(response, command->getName(), 401, "Signature required");
+  };
+
+  getManager().onFaceRequest(*command);
+
+  BOOST_REQUIRE(didCallbackFire());
+}
+
+BOOST_FIXTURE_TEST_CASE(UnauthorizedCommand, UnauthorizedCommandFixture<FaceManagerFixture>)
+{
+  ControlParameters parameters;
+  parameters.setUri("tcp4://127.0.0.1:6363");
+
+  Block encodedParameters(parameters.wireEncode());
+
+  Name commandName("/localhost/nfd/faces");
+  commandName.append("create");
+  commandName.append(encodedParameters);
+
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+  generateCommand(*command);
+
+  getFace()->onReceiveData += [this, command] (const Data& response) {
+    this->validateControlResponse(response, command->getName(), 403, "Unauthorized command");
+  };
+
+  getManager().onFaceRequest(*command);
+
+  BOOST_REQUIRE(didCallbackFire());
+}
+
+template <typename T> class AuthorizedCommandFixture : public CommandFixture<T>
+{
+public:
+  AuthorizedCommandFixture()
+  {
+    const std::string regex = "^<localhost><nfd><faces>";
+    T::addInterestRule(regex, *CommandFixture<T>::m_certificate);
+  }
+
+  virtual
+  ~AuthorizedCommandFixture()
+  {
+
+  }
+};
+
+BOOST_FIXTURE_TEST_CASE(UnsupportedCommand, AuthorizedCommandFixture<FaceManagerFixture>)
+{
+  ControlParameters parameters;
+
+  Block encodedParameters(parameters.wireEncode());
+
+  Name commandName("/localhost/nfd/faces");
+  commandName.append("unsupported");
+  commandName.append(encodedParameters);
+
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+  generateCommand(*command);
+
+  getFace()->onReceiveData += [this, command] (const Data& response) {
+    this->validateControlResponse(response, command->getName(), 501, "Unsupported command");
+  };
+
+  getManager().onFaceRequest(*command);
+
+  BOOST_REQUIRE(didCallbackFire());
+}
+
+class ValidatedFaceRequestFixture : public TestFaceTableFixture,
+                                    public TestFaceManagerCommon,
+                                    public FaceManager
+{
+public:
+
+  ValidatedFaceRequestFixture()
+    : FaceManager(TestFaceTableFixture::m_faceTable, TestFaceManagerCommon::m_face, m_testKeyChain),
+      m_createFaceFired(false),
+      m_destroyFaceFired(false)
+  {
+
+  }
+
+  virtual void
+  createFace(const Interest& request,
+             ControlParameters& parameters)
+  {
+    m_createFaceFired = true;
+  }
+
+  virtual void
+  destroyFace(const Interest& request,
+              ControlParameters& parameters)
+  {
+    m_destroyFaceFired = true;
+  }
+
+  virtual
+  ~ValidatedFaceRequestFixture()
+  {
+
+  }
+
+  bool
+  didCreateFaceFire() const
+  {
+    return m_createFaceFired;
+  }
+
+  bool
+  didDestroyFaceFire() const
+  {
+    return m_destroyFaceFired;
+  }
+
+private:
+  bool m_createFaceFired;
+  bool m_destroyFaceFired;
+};
+
+BOOST_FIXTURE_TEST_CASE(ValidatedFaceRequestBadOptionParse,
+                        AuthorizedCommandFixture<ValidatedFaceRequestFixture>)
+{
+  Name commandName("/localhost/nfd/faces");
+  commandName.append("create");
+  commandName.append("NotReallyParameters");
+
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+  generateCommand(*command);
+
+  getFace()->onReceiveData += [this, command] (const Data& response) {
+    this->validateControlResponse(response, command->getName(), 400, "Malformed command");
+  };
+
+  onValidatedFaceRequest(command);
+
+  BOOST_REQUIRE(didCallbackFire());
+}
+
+BOOST_FIXTURE_TEST_CASE(ValidatedFaceRequestCreateFace,
+                        AuthorizedCommandFixture<ValidatedFaceRequestFixture>)
+{
+  ControlParameters parameters;
+  parameters.setUri("tcp4://127.0.0.1:6363");
+
+  Block encodedParameters(parameters.wireEncode());
+
+  Name commandName("/localhost/nfd/faces");
+  commandName.append("create");
+  commandName.append(encodedParameters);
+
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+  generateCommand(*command);
+
+  onValidatedFaceRequest(command);
+  BOOST_CHECK(didCreateFaceFire());
+}
+
+BOOST_FIXTURE_TEST_CASE(ValidatedFaceRequestDestroyFace,
+                        AuthorizedCommandFixture<ValidatedFaceRequestFixture>)
+{
+  ControlParameters parameters;
+  parameters.setUri("tcp4://127.0.0.1:6363");
+
+  Block encodedParameters(parameters.wireEncode());
+
+  Name commandName("/localhost/nfd/faces");
+  commandName.append("destroy");
+  commandName.append(encodedParameters);
+
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+  generateCommand(*command);
+
+  onValidatedFaceRequest(command);
+  BOOST_CHECK(didDestroyFaceFire());
+}
+
+class FaceTableFixture
+{
+public:
+  FaceTableFixture()
+    : m_faceTable(m_forwarder)
+  {
+  }
+
+  virtual
+  ~FaceTableFixture()
+  {
+  }
+
+protected:
+  Forwarder m_forwarder;
+  FaceTable m_faceTable;
+};
+
+class LocalControlFixture : public FaceTableFixture,
+                            public TestFaceManagerCommon,
+                            public FaceManager
+{
+public:
+  LocalControlFixture()
+    : FaceManager(FaceTableFixture::m_faceTable, TestFaceManagerCommon::m_face, m_testKeyChain)
+  {
+  }
+};
+
+BOOST_FIXTURE_TEST_CASE(LocalControlInFaceId,
+                        AuthorizedCommandFixture<LocalControlFixture>)
+{
+  shared_ptr<LocalFace> dummy = make_shared<DummyLocalFace>();
+  BOOST_REQUIRE(dummy->isLocal());
+  FaceTableFixture::m_faceTable.add(dummy);
+
+  ControlParameters parameters;
+  parameters.setLocalControlFeature(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID);
+
+  Block encodedParameters(parameters.wireEncode());
+
+  Name enable("/localhost/nfd/faces/enable-local-control");
+  enable.append(encodedParameters);
+
+  shared_ptr<Interest> enableCommand(make_shared<Interest>(enable));
+  enableCommand->setIncomingFaceId(dummy->getId());
+
+  generateCommand(*enableCommand);
+
+  TestFaceManagerCommon::m_face->onReceiveData +=
+  [this, enableCommand, encodedParameters] (const Data& response) {
+    this->validateControlResponse(response, enableCommand->getName(),
+                                  200, "Success", encodedParameters);
+  };
+
+  onValidatedFaceRequest(enableCommand);
+
+  BOOST_REQUIRE(didCallbackFire());
+  BOOST_REQUIRE(dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID));
+  BOOST_CHECK(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID));
+
+  TestFaceManagerCommon::m_face->onReceiveData.clear();
+  resetCallbackFired();
+
+  Name disable("/localhost/nfd/faces/disable-local-control");
+  disable.append(encodedParameters);
+
+  shared_ptr<Interest> disableCommand(make_shared<Interest>(disable));
+  disableCommand->setIncomingFaceId(dummy->getId());
+
+  generateCommand(*disableCommand);
+
+  TestFaceManagerCommon::m_face->onReceiveData +=
+  [this, disableCommand, encodedParameters] (const Data& response) {
+    this->validateControlResponse(response, disableCommand->getName(),
+                                  200, "Success", encodedParameters);
+  };
+
+  onValidatedFaceRequest(disableCommand);
+
+  BOOST_REQUIRE(didCallbackFire());
+  BOOST_REQUIRE(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID));
+  BOOST_CHECK(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID));
+}
+
+BOOST_FIXTURE_TEST_CASE(LocalControlInFaceIdFaceNotFound,
+                        AuthorizedCommandFixture<LocalControlFixture>)
+{
+  shared_ptr<LocalFace> dummy = make_shared<DummyLocalFace>();
+  BOOST_REQUIRE(dummy->isLocal());
+  FaceTableFixture::m_faceTable.add(dummy);
+
+  ControlParameters parameters;
+  parameters.setLocalControlFeature(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID);
+
+  Block encodedParameters(parameters.wireEncode());
+
+  Name enable("/localhost/nfd/faces/enable-local-control");
+  enable.append(encodedParameters);
+
+  shared_ptr<Interest> enableCommand(make_shared<Interest>(enable));
+  enableCommand->setIncomingFaceId(dummy->getId() + 100);
+
+  generateCommand(*enableCommand);
+
+  TestFaceManagerCommon::m_face->onReceiveData += [this, enableCommand] (const Data& response) {
+    this->validateControlResponse(response, enableCommand->getName(), 410, "Face not found");
+  };
+
+  onValidatedFaceRequest(enableCommand);
+
+  BOOST_REQUIRE(didCallbackFire());
+  BOOST_REQUIRE(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID));
+  BOOST_CHECK(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID));
+
+  TestFaceManagerCommon::m_face->onReceiveData.clear();
+  resetCallbackFired();
+
+  Name disable("/localhost/nfd/faces/disable-local-control");
+  disable.append(encodedParameters);
+
+  shared_ptr<Interest> disableCommand(make_shared<Interest>(disable));
+  disableCommand->setIncomingFaceId(dummy->getId() + 100);
+
+  generateCommand(*disableCommand);
+
+  TestFaceManagerCommon::m_face->onReceiveData += [this, disableCommand] (const Data& response) {
+    this->validateControlResponse(response, disableCommand->getName(), 410, "Face not found");
+  };
+
+  onValidatedFaceRequest(disableCommand);
+
+  BOOST_REQUIRE(didCallbackFire());
+  BOOST_REQUIRE(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID));
+  BOOST_CHECK(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID));
+}
+
+BOOST_FIXTURE_TEST_CASE(LocalControlMissingFeature,
+                        AuthorizedCommandFixture<LocalControlFixture>)
+{
+  shared_ptr<LocalFace> dummy = make_shared<DummyLocalFace>();
+  BOOST_REQUIRE(dummy->isLocal());
+  FaceTableFixture::m_faceTable.add(dummy);
+
+  ControlParameters parameters;
+
+  Block encodedParameters(parameters.wireEncode());
+
+  Name enable("/localhost/nfd/faces/enable-local-control");
+  enable.append(encodedParameters);
+
+  shared_ptr<Interest> enableCommand(make_shared<Interest>(enable));
+  enableCommand->setIncomingFaceId(dummy->getId());
+
+  generateCommand(*enableCommand);
+
+  TestFaceManagerCommon::m_face->onReceiveData += [this, enableCommand] (const Data& response) {
+    this->validateControlResponse(response, enableCommand->getName(), 400, "Malformed command");
+  };
+
+  onValidatedFaceRequest(enableCommand);
+
+  BOOST_REQUIRE(didCallbackFire());
+  BOOST_REQUIRE(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID));
+  BOOST_REQUIRE(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID));
+
+  TestFaceManagerCommon::m_face->onReceiveData.clear();
+  resetCallbackFired();
+
+  Name disable("/localhost/nfd/faces/disable-local-control");
+  disable.append(encodedParameters);
+
+  shared_ptr<Interest> disableCommand(make_shared<Interest>(disable));
+  disableCommand->setIncomingFaceId(dummy->getId());
+
+  generateCommand(*disableCommand);
+
+  TestFaceManagerCommon::m_face->onReceiveData += [this, disableCommand] (const Data& response) {
+    this->validateControlResponse(response, disableCommand->getName(), 400, "Malformed command");
+  };
+
+  onValidatedFaceRequest(disableCommand);
+
+  BOOST_REQUIRE(didCallbackFire());
+  BOOST_REQUIRE(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID));
+  BOOST_REQUIRE(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID));
+}
+
+BOOST_FIXTURE_TEST_CASE(LocalControlInFaceIdNonLocal,
+                        AuthorizedCommandFixture<LocalControlFixture>)
+{
+  shared_ptr<DummyFace> dummy = make_shared<DummyFace>();
+  BOOST_REQUIRE(!dummy->isLocal());
+  FaceTableFixture::m_faceTable.add(dummy);
+
+  ControlParameters parameters;
+  parameters.setLocalControlFeature(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID);
+
+  Block encodedParameters(parameters.wireEncode());
+
+  Name enable("/localhost/nfd/faces/enable-local-control");
+  enable.append(encodedParameters);
+
+  shared_ptr<Interest> enableCommand(make_shared<Interest>(enable));
+  enableCommand->setIncomingFaceId(dummy->getId());
+
+  generateCommand(*enableCommand);
+
+  TestFaceManagerCommon::m_face->onReceiveData += [this, enableCommand] (const Data& response) {
+    this->validateControlResponse(response, enableCommand->getName(), 412, "Face is non-local");
+  };
+
+  onValidatedFaceRequest(enableCommand);
+
+  BOOST_REQUIRE(didCallbackFire());
+
+  TestFaceManagerCommon::m_face->onReceiveData.clear();
+  resetCallbackFired();
+
+  Name disable("/localhost/nfd/faces/disable-local-control");
+  enable.append(encodedParameters);
+
+  shared_ptr<Interest> disableCommand(make_shared<Interest>(enable));
+  disableCommand->setIncomingFaceId(dummy->getId());
+
+  generateCommand(*disableCommand);
+
+  TestFaceManagerCommon::m_face->onReceiveData += [this, disableCommand] (const Data& response) {
+    this->validateControlResponse(response, disableCommand->getName(), 412, "Face is non-local");
+  };
+
+  onValidatedFaceRequest(disableCommand);
+
+  BOOST_REQUIRE(didCallbackFire());
+}
+
+BOOST_FIXTURE_TEST_CASE(LocalControlNextHopFaceId,
+                        AuthorizedCommandFixture<LocalControlFixture>)
+{
+  shared_ptr<LocalFace> dummy = make_shared<DummyLocalFace>();
+  BOOST_REQUIRE(dummy->isLocal());
+  FaceTableFixture::m_faceTable.add(dummy);
+
+  ControlParameters parameters;
+  parameters.setLocalControlFeature(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID);
+
+  Block encodedParameters(parameters.wireEncode());
+
+  Name enable("/localhost/nfd/faces/enable-local-control");
+  enable.append(encodedParameters);
+
+  shared_ptr<Interest> enableCommand(make_shared<Interest>(enable));
+  enableCommand->setIncomingFaceId(dummy->getId());
+
+  generateCommand(*enableCommand);
+
+  TestFaceManagerCommon::m_face->onReceiveData +=
+  [this, enableCommand, encodedParameters] (const Data& response) {
+    this->validateControlResponse(response, enableCommand->getName(),
+                                  200, "Success", encodedParameters);
+  };
+
+  onValidatedFaceRequest(enableCommand);
+
+  BOOST_REQUIRE(didCallbackFire());
+  BOOST_REQUIRE(dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID));
+  BOOST_CHECK(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID));
+
+
+  TestFaceManagerCommon::m_face->onReceiveData.clear();
+  resetCallbackFired();
+
+  Name disable("/localhost/nfd/faces/disable-local-control");
+  disable.append(encodedParameters);
+
+  shared_ptr<Interest> disableCommand(make_shared<Interest>(disable));
+  disableCommand->setIncomingFaceId(dummy->getId());
+
+  generateCommand(*disableCommand);
+
+  TestFaceManagerCommon::m_face->onReceiveData +=
+  [this, disableCommand, encodedParameters] (const Data& response) {
+    this->validateControlResponse(response, disableCommand->getName(),
+                                  200, "Success", encodedParameters);
+  };
+
+  onValidatedFaceRequest(disableCommand);
+
+  BOOST_REQUIRE(didCallbackFire());
+  BOOST_REQUIRE(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID));
+  BOOST_CHECK(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID));
+}
+
+BOOST_FIXTURE_TEST_CASE(LocalControlNextHopFaceIdFaceNotFound,
+                        AuthorizedCommandFixture<LocalControlFixture>)
+{
+  shared_ptr<LocalFace> dummy = make_shared<DummyLocalFace>();
+  BOOST_REQUIRE(dummy->isLocal());
+  FaceTableFixture::m_faceTable.add(dummy);
+
+  ControlParameters parameters;
+  parameters.setLocalControlFeature(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID);
+
+  Block encodedParameters(parameters.wireEncode());
+
+  Name enable("/localhost/nfd/faces/enable-local-control");
+  enable.append(encodedParameters);
+
+  shared_ptr<Interest> enableCommand(make_shared<Interest>(enable));
+  enableCommand->setIncomingFaceId(dummy->getId() + 100);
+
+  generateCommand(*enableCommand);
+
+  TestFaceManagerCommon::m_face->onReceiveData += [this, enableCommand] (const Data& response) {
+    this->validateControlResponse(response, enableCommand->getName(), 410, "Face not found");
+  };
+
+  onValidatedFaceRequest(enableCommand);
+
+  BOOST_REQUIRE(didCallbackFire());
+  BOOST_REQUIRE(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID));
+  BOOST_CHECK(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID));
+
+
+  TestFaceManagerCommon::m_face->onReceiveData.clear();
+  resetCallbackFired();
+
+  Name disable("/localhost/nfd/faces/disable-local-control");
+  disable.append(encodedParameters);
+
+  shared_ptr<Interest> disableCommand(make_shared<Interest>(disable));
+  disableCommand->setIncomingFaceId(dummy->getId() + 100);
+
+  generateCommand(*disableCommand);
+
+  TestFaceManagerCommon::m_face->onReceiveData += [this, disableCommand] (const Data& response) {
+    this->validateControlResponse(response, disableCommand->getName(), 410, "Face not found");
+  };
+
+  onValidatedFaceRequest(disableCommand);
+
+  BOOST_REQUIRE(didCallbackFire());
+  BOOST_REQUIRE(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID));
+  BOOST_CHECK(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID));
+}
+
+BOOST_FIXTURE_TEST_CASE(LocalControlNextHopFaceIdNonLocal,
+                        AuthorizedCommandFixture<LocalControlFixture>)
+{
+  shared_ptr<DummyFace> dummy = make_shared<DummyFace>();
+  BOOST_REQUIRE(!dummy->isLocal());
+  FaceTableFixture::m_faceTable.add(dummy);
+
+  ControlParameters parameters;
+  parameters.setLocalControlFeature(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID);
+
+  Block encodedParameters(parameters.wireEncode());
+
+  Name enable("/localhost/nfd/faces/enable-local-control");
+  enable.append(encodedParameters);
+
+  shared_ptr<Interest> enableCommand(make_shared<Interest>(enable));
+  enableCommand->setIncomingFaceId(dummy->getId());
+
+  generateCommand(*enableCommand);
+
+  TestFaceManagerCommon::m_face->onReceiveData += [this, enableCommand] (const Data& response) {
+    this->validateControlResponse(response, enableCommand->getName(), 412, "Face is non-local");
+  };
+
+  onValidatedFaceRequest(enableCommand);
+
+  BOOST_REQUIRE(didCallbackFire());
+
+  TestFaceManagerCommon::m_face->onReceiveData.clear();
+  resetCallbackFired();
+
+  Name disable("/localhost/nfd/faces/disable-local-control");
+  disable.append(encodedParameters);
+
+  shared_ptr<Interest> disableCommand(make_shared<Interest>(disable));
+  disableCommand->setIncomingFaceId(dummy->getId());
+
+  generateCommand(*disableCommand);
+
+  TestFaceManagerCommon::m_face->onReceiveData += [this, disableCommand] (const Data& response) {
+    this->validateControlResponse(response, disableCommand->getName(), 412, "Face is non-local");
+  };
+
+  onValidatedFaceRequest(disableCommand);
+
+  BOOST_REQUIRE(didCallbackFire());
+}
+
+class FaceFixture : public FaceTableFixture,
+                    public TestFaceManagerCommon,
+                    public FaceManager
+{
+public:
+  FaceFixture()
+    : FaceManager(FaceTableFixture::m_faceTable,
+                  TestFaceManagerCommon::m_face,
+                  m_testKeyChain)
+    , m_receivedNotification(false)
+  {
+
+  }
+
+  virtual
+  ~FaceFixture()
+  {
+
+  }
+
+  void
+  callbackDispatch(const Data& response,
+                   const Name& expectedName,
+                   uint32_t expectedCode,
+                   const std::string& expectedText,
+                   const Block& expectedBody,
+                   const ndn::nfd::FaceEventNotification& expectedFaceEvent)
+  {
+    Block payload = response.getContent().blockFromValue();
+    if (payload.type() == ndn::tlv::nfd::ControlResponse)
+      {
+        validateControlResponse(response, expectedName, expectedCode,
+                                expectedText, expectedBody);
+      }
+    else if (payload.type() == ndn::tlv::nfd::FaceEventNotification)
+      {
+        validateFaceEvent(payload, expectedFaceEvent);
+      }
+    else
+      {
+        BOOST_FAIL("Received unknown message type: #" << payload.type());
+      }
+  }
+
+  void
+  callbackDispatch(const Data& response,
+                   const Name& expectedName,
+                   uint32_t expectedCode,
+                   const std::string& expectedText,
+                   const ndn::nfd::FaceEventNotification& expectedFaceEvent)
+  {
+    Block payload = response.getContent().blockFromValue();
+    if (payload.type() == ndn::tlv::nfd::ControlResponse)
+      {
+        validateControlResponse(response, expectedName,
+                                expectedCode, expectedText);
+      }
+    else if (payload.type() == ndn::tlv::nfd::FaceEventNotification)
+      {
+        validateFaceEvent(payload, expectedFaceEvent);
+      }
+    else
+      {
+        BOOST_FAIL("Received unknown message type: #" << payload.type());
+      }
+  }
+
+  void
+  validateFaceEvent(const Block& wire,
+                    const ndn::nfd::FaceEventNotification& expectedFaceEvent)
+  {
+
+    m_receivedNotification = true;
+
+    ndn::nfd::FaceEventNotification notification(wire);
+
+    BOOST_CHECK_EQUAL(notification.getKind(), expectedFaceEvent.getKind());
+    BOOST_CHECK_EQUAL(notification.getFaceId(), expectedFaceEvent.getFaceId());
+    BOOST_CHECK_EQUAL(notification.getRemoteUri(), expectedFaceEvent.getRemoteUri());
+    BOOST_CHECK_EQUAL(notification.getLocalUri(), expectedFaceEvent.getLocalUri());
+    BOOST_CHECK_EQUAL(notification.getFaceScope(), expectedFaceEvent.getFaceScope());
+    BOOST_CHECK_EQUAL(notification.getFacePersistency(), expectedFaceEvent.getFacePersistency());
+    BOOST_CHECK_EQUAL(notification.getLinkType(), expectedFaceEvent.getLinkType());
+  }
+
+  bool
+  didReceiveNotication() const
+  {
+    return m_receivedNotification;
+  }
+
+protected:
+  bool m_receivedNotification;
+};
+
+BOOST_FIXTURE_TEST_CASE(CreateFaceBadUri, AuthorizedCommandFixture<FaceFixture>)
+{
+  ControlParameters parameters;
+  parameters.setUri("tcp4:/127.0.0.1:6363");
+
+  Block encodedParameters(parameters.wireEncode());
+
+  Name commandName("/localhost/nfd/faces");
+  commandName.append("create");
+  commandName.append(encodedParameters);
+
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+  generateCommand(*command);
+
+  getFace()->onReceiveData += [this, command] (const Data& response) {
+    this->validateControlResponse(response, command->getName(), 400, "Malformed command");
+  };
+
+  createFace(*command, parameters);
+
+  BOOST_REQUIRE(didCallbackFire());
+}
+BOOST_FIXTURE_TEST_CASE(CreateFaceNoncanonicalUri, AuthorizedCommandFixture<FaceFixture>)
+{
+  ControlParameters parameters;
+  parameters.setUri("tcp://127.0.0.1");
+
+  Block encodedParameters(parameters.wireEncode());
+
+  Name commandName("/localhost/nfd/faces");
+  commandName.append("create");
+  commandName.append(encodedParameters);
+
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+  generateCommand(*command);
+
+  getFace()->onReceiveData += [this, command] (const Data& response) {
+    this->validateControlResponse(response, command->getName(), 400, "Non-canonical URI");
+  };
+
+  createFace(*command, parameters);
+
+  BOOST_REQUIRE(didCallbackFire());
+}
+
+BOOST_FIXTURE_TEST_CASE(CreateFaceMissingUri, AuthorizedCommandFixture<FaceFixture>)
+{
+  ControlParameters parameters;
+
+  Block encodedParameters(parameters.wireEncode());
+
+  Name commandName("/localhost/nfd/faces");
+  commandName.append("create");
+  commandName.append(encodedParameters);
+
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+  generateCommand(*command);
+
+  getFace()->onReceiveData += [this, command] (const Data& response) {
+    this->validateControlResponse(response, command->getName(), 400, "Malformed command");
+  };
+
+  createFace(*command, parameters);
+
+  BOOST_REQUIRE(didCallbackFire());
+}
+
+BOOST_FIXTURE_TEST_CASE(CreateFaceUnknownScheme, AuthorizedCommandFixture<FaceFixture>)
+{
+  ControlParameters parameters;
+  // this will be an unsupported protocol because no factories have been
+  // added to the face manager
+  parameters.setUri("tcp4://127.0.0.1:6363");
+
+  Block encodedParameters(parameters.wireEncode());
+
+  Name commandName("/localhost/nfd/faces");
+  commandName.append("create");
+  commandName.append(encodedParameters);
+
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+  generateCommand(*command);
+
+  getFace()->onReceiveData += [this, command] (const Data& response) {
+    this->validateControlResponse(response, command->getName(), 501, "Unsupported protocol");
+  };
+
+  createFace(*command, parameters);
+
+  BOOST_REQUIRE(didCallbackFire());
+}
+
+BOOST_FIXTURE_TEST_CASE(OnCreated, AuthorizedCommandFixture<FaceFixture>)
+{
+  ControlParameters parameters;
+  parameters.setUri("tcp4://127.0.0.1:6363");
+
+  Block encodedParameters(parameters.wireEncode());
+
+  Name commandName("/localhost/nfd/faces");
+  commandName.append("create");
+  commandName.append(encodedParameters);
+
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+  generateCommand(*command);
+
+  ControlParameters resultParameters;
+  resultParameters.setUri("dummy://");
+  resultParameters.setFaceId(FACEID_RESERVED_MAX + 1);
+
+  shared_ptr<DummyFace> dummy(make_shared<DummyFace>());
+
+  ndn::nfd::FaceEventNotification expectedFaceEvent;
+  expectedFaceEvent.setKind(ndn::nfd::FACE_EVENT_CREATED)
+                   .setFaceId(FACEID_RESERVED_MAX + 1)
+                   .setRemoteUri(dummy->getRemoteUri().toString())
+                   .setLocalUri(dummy->getLocalUri().toString())
+                   .setFaceScope(ndn::nfd::FACE_SCOPE_NON_LOCAL)
+                   .setFacePersistency(ndn::nfd::FACE_PERSISTENCY_PERSISTENT);
+
+  Block encodedResultParameters(resultParameters.wireEncode());
+
+  getFace()->onReceiveData +=
+  [this, command, encodedResultParameters, expectedFaceEvent] (const Data& response) {
+    this->callbackDispatch(response,command->getName(), 200, "Success",
+                           encodedResultParameters, expectedFaceEvent);
+  };
+
+  onCreated(command->getName(), parameters, dummy);
+
+  BOOST_REQUIRE(didCallbackFire());
+  BOOST_REQUIRE(didReceiveNotication());
+}
+
+BOOST_FIXTURE_TEST_CASE(OnConnectFailed, AuthorizedCommandFixture<FaceFixture>)
+{
+  ControlParameters parameters;
+  parameters.setUri("tcp4://127.0.0.1:6363");
+
+  Block encodedParameters(parameters.wireEncode());
+
+  Name commandName("/localhost/nfd/faces");
+  commandName.append("create");
+  commandName.append(encodedParameters);
+
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+  generateCommand(*command);
+
+  getFace()->onReceiveData += [this, command] (const Data& response) {
+    this->validateControlResponse(response, command->getName(), 408, "unit-test-reason");
+  };
+
+  onConnectFailed(command->getName(), "unit-test-reason");
+
+  BOOST_REQUIRE(didCallbackFire());
+  BOOST_CHECK_EQUAL(didReceiveNotication(), false);
+}
+
+
+BOOST_FIXTURE_TEST_CASE(DestroyFace, AuthorizedCommandFixture<FaceFixture>)
+{
+  shared_ptr<DummyFace> dummy(make_shared<DummyFace>());
+  FaceTableFixture::m_faceTable.add(dummy);
+
+  ControlParameters parameters;
+  parameters.setFaceId(dummy->getId());
+
+  Block encodedParameters(parameters.wireEncode());
+
+  Name commandName("/localhost/nfd/faces");
+  commandName.append("destroy");
+  commandName.append(encodedParameters);
+
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+  generateCommand(*command);
+
+  ndn::nfd::FaceEventNotification expectedFaceEvent;
+  expectedFaceEvent.setKind(ndn::nfd::FACE_EVENT_DESTROYED)
+                   .setFaceId(dummy->getId())
+                   .setRemoteUri(dummy->getRemoteUri().toString())
+                   .setLocalUri(dummy->getLocalUri().toString())
+                   .setFaceScope(ndn::nfd::FACE_SCOPE_NON_LOCAL)
+                   .setFacePersistency(ndn::nfd::FACE_PERSISTENCY_PERSISTENT);
+
+  getFace()->onReceiveData +=
+  [this, command, encodedParameters, expectedFaceEvent] (const Data& response) {
+    this->callbackDispatch(response,command->getName(), 200, "Success",
+                           encodedParameters, expectedFaceEvent);
+  };
+
+  destroyFace(*command, parameters);
+
+  BOOST_REQUIRE(didCallbackFire());
+  BOOST_REQUIRE(didReceiveNotication());
+}
+
+class FaceListFixture : public FaceStatusPublisherFixture
+{
+public:
+  FaceListFixture()
+    : m_manager(m_table, m_face, m_testKeyChain)
+  {
+
+  }
+
+  virtual
+  ~FaceListFixture()
+  {
+
+  }
+
+protected:
+  FaceManager m_manager;
+  ndn::KeyChain m_testKeyChain;
+};
+
+BOOST_FIXTURE_TEST_CASE(TestFaceList, FaceListFixture)
+{
+  Name commandName("/localhost/nfd/faces/list");
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+
+  // MAX_SEGMENT_SIZE == 4400, FaceStatus size with filler counters is 75
+  // use 59 FaceStatuses to force a FaceStatus to span Data packets
+  for (int i = 0; i < 59; i++)
+    {
+      shared_ptr<TestCountersFace> dummy(make_shared<TestCountersFace>());
+
+      uint64_t filler = std::numeric_limits<uint64_t>::max() - 1;
+      dummy->setCounters(filler, filler, filler, filler, filler, filler);
+
+      m_referenceFaces.push_back(dummy);
+
+      add(dummy);
+    }
+
+  ndn::EncodingBuffer buffer;
+
+  m_face->onReceiveData +=
+    bind(&FaceStatusPublisherFixture::decodeFaceStatusBlock, this, _1);
+
+  m_manager.listFaces(*command);
+  BOOST_REQUIRE(m_finished);
+}
+
+class ChannelStatusFixture : public FaceManagerFixture
+{
+public:
+  void
+  validatePublish(const Data& data, const ndn::nfd::ChannelStatus& expectedEntry)
+  {
+    m_callbackFired = true;
+    Block b = data.getContent().blockFromValue();
+    ndn::nfd::ChannelStatus entry(b);
+    BOOST_CHECK_EQUAL(entry.getLocalUri(), expectedEntry.getLocalUri());
+  }
+
+  virtual shared_ptr<DummyProtocolFactory>
+  addProtocolFactory(const std::string& protocol)
+  {
+    shared_ptr<DummyProtocolFactory> factory(make_shared<DummyProtocolFactory>());
+    m_manager.m_factories[protocol] = factory;
+
+    return factory;
+  }
+};
+
+BOOST_FIXTURE_TEST_CASE(TestChannelStatus, ChannelStatusFixture)
+{
+  shared_ptr<DummyProtocolFactory> factory(addProtocolFactory("dummy"));
+  factory->addChannel("dummy://");
+
+  Name requestName("/localhost/nfd/faces/channels");
+  shared_ptr<Interest> request(make_shared<Interest>(requestName));
+
+  ndn::nfd::ChannelStatus expectedEntry;
+  expectedEntry.setLocalUri(DummyChannel("dummy://").getUri().toString());
+
+  m_face->onReceiveData +=
+    bind(&ChannelStatusFixture::validatePublish, this, _1, expectedEntry);
+
+  m_manager.listChannels(*request);
+  BOOST_REQUIRE(m_callbackFired);
+}
+
+class FaceQueryListFixture : public FaceQueryStatusPublisherFixture
+{
+public:
+  FaceQueryListFixture()
+    : m_manager(m_table, m_face, m_testKeyChain)
+  {
+
+  }
+
+  virtual
+  ~FaceQueryListFixture()
+  {
+
+  }
+
+protected:
+  FaceManager m_manager;
+  ndn::KeyChain m_testKeyChain;
+};
+
+BOOST_FIXTURE_TEST_CASE(TestValidQueryFilter, FaceQueryListFixture)
+{
+  Name queryName("/localhost/nfd/faces/query");
+  ndn::nfd::FaceQueryFilter queryFilter;
+  queryFilter.setUriScheme("dummy");
+  queryName.append(queryFilter.wireEncode());
+
+  shared_ptr<Interest> query(make_shared<Interest>(queryName));
+
+  // add expected faces
+  shared_ptr<DummyLocalFace> expectedFace1(make_shared<DummyLocalFace>());
+  m_referenceFaces.push_back(expectedFace1);
+  add(expectedFace1);
+
+  shared_ptr<DummyFace> expectedFace2(make_shared<DummyFace>());
+  m_referenceFaces.push_back(expectedFace2);
+  add(expectedFace2);
+
+  // add other faces
+  shared_ptr<DummyFace> face1(make_shared<DummyFace>("udp://", "udp://"));
+  add(face1);
+  shared_ptr<DummyLocalFace> face2(make_shared<DummyLocalFace>("tcp://", "tcp://"));
+  add(face2);
+
+  m_face->onReceiveData +=
+    bind(&FaceQueryStatusPublisherFixture::decodeFaceStatusBlock, this, _1);
+
+  m_manager.listQueriedFaces(*query);
+  BOOST_REQUIRE(m_finished);
+}
+
+BOOST_FIXTURE_TEST_CASE(TestInvalidQueryFilter, FaceQueryListFixture)
+{
+  Name queryName("/localhost/nfd/faces/query");
+  ndn::nfd::FaceStatus queryFilter;
+  queryName.append(queryFilter.wireEncode());
+
+  shared_ptr<Interest> query(make_shared<Interest>(queryName));
+
+  shared_ptr<DummyLocalFace> face(make_shared<DummyLocalFace>());
+  add(face);
+
+  m_face->onReceiveData +=
+    bind(&FaceQueryStatusPublisherFixture::decodeNackBlock, this, _1);
+
+  m_manager.listQueriedFaces(*query);
+  BOOST_REQUIRE(m_finished);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/NFD/tests/daemon/mgmt/face-query-status-publisher-common.hpp b/NFD/tests/daemon/mgmt/face-query-status-publisher-common.hpp
new file mode 100644
index 0000000..a274c52
--- /dev/null
+++ b/NFD/tests/daemon/mgmt/face-query-status-publisher-common.hpp
@@ -0,0 +1,163 @@
+/* -*- 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_TESTS_NFD_MGMT_FACE_QUERY_STATUS_PUBLISHER_COMMON_HPP
+#define NFD_TESTS_NFD_MGMT_FACE_QUERY_STATUS_PUBLISHER_COMMON_HPP
+
+#include "mgmt/face-query-status-publisher.hpp"
+#include "mgmt/app-face.hpp"
+#include "mgmt/internal-face.hpp"
+#include "fw/forwarder.hpp"
+#include "face/udp-factory.hpp"
+
+#include "tests/test-common.hpp"
+#include "tests/daemon/face/dummy-face.hpp"
+
+#include <ndn-cxx/management/nfd-face-status.hpp>
+
+namespace nfd {
+namespace tests {
+
+class FaceQueryStatusPublisherFixture : public BaseFixture
+{
+public:
+
+  FaceQueryStatusPublisherFixture()
+    : m_table(m_forwarder)
+    , m_face(make_shared<InternalFace>())
+    , m_dummyFace(make_shared<DummyFace>())
+    , m_dummyLocalFace(make_shared<DummyLocalFace>())
+    , m_dummyUri(make_shared<DummyFace>("dummy://remoteUri", "dummy://localUri"))
+    , m_factory(UdpFactory())
+    , m_finished(false)
+  {
+  }
+
+  virtual
+  ~FaceQueryStatusPublisherFixture()
+  {
+  }
+
+  void
+  add(shared_ptr<Face> face)
+  {
+    m_table.add(face);
+  }
+
+  void
+  validateFaceStatus(const Block& statusBlock, const shared_ptr<Face>& reference)
+  {
+    ndn::nfd::FaceStatus status;
+    BOOST_REQUIRE_NO_THROW(status.wireDecode(statusBlock));
+
+    BOOST_CHECK_EQUAL(status.getFaceId(), reference->getId());
+    BOOST_CHECK_EQUAL(status.getRemoteUri(), reference->getRemoteUri().toString());
+    BOOST_CHECK_EQUAL(status.getLocalUri(), reference->getLocalUri().toString());
+
+    if (reference->isLocal()) {
+      BOOST_CHECK_EQUAL(status.getFaceScope(), ndn::nfd::FACE_SCOPE_LOCAL);
+    }
+    else {
+      BOOST_CHECK_EQUAL(status.getFaceScope(), ndn::nfd::FACE_SCOPE_NON_LOCAL);
+    }
+
+    if (reference->isOnDemand()) {
+      BOOST_CHECK_EQUAL(status.getFacePersistency(), ndn::nfd::FACE_PERSISTENCY_ON_DEMAND);
+    }
+    else {
+      BOOST_CHECK_EQUAL(status.getFacePersistency(), ndn::nfd::FACE_PERSISTENCY_PERSISTENT);
+    }
+
+    if (reference->isMultiAccess()) {
+      BOOST_CHECK_EQUAL(status.getLinkType(), ndn::nfd::LINK_TYPE_MULTI_ACCESS);
+    }
+    else {
+      BOOST_CHECK_EQUAL(status.getLinkType(), ndn::nfd::LINK_TYPE_POINT_TO_POINT);
+    }
+  }
+
+  void
+  decodeFaceStatusBlock(const Data& data)
+  {
+    BOOST_REQUIRE_EQUAL(data.getContentType(), tlv::ContentType_Blob);
+
+    Block payload = data.getContent();
+    m_buffer.appendByteArray(payload.value(), payload.value_size());
+
+    BOOST_CHECK_NO_THROW(data.getName()[-1].toSegment());
+    if (data.getFinalBlockId() != data.getName()[-1]) {
+        return;
+    }
+
+    // wrap the Face Statuses in a single Content TLV for easy parsing
+    m_buffer.prependVarNumber(m_buffer.size());
+    m_buffer.prependVarNumber(tlv::Content);
+
+    ndn::Block parser(m_buffer.buf(), m_buffer.size());
+    parser.parse();
+
+    BOOST_REQUIRE_EQUAL(parser.elements_size(), m_referenceFaces.size());
+
+    std::list<shared_ptr<Face> >::const_iterator iReference = m_referenceFaces.begin();
+    for (Block::element_const_iterator i = parser.elements_begin();
+         i != parser.elements_end();
+         ++i) {
+      if (i->type() != ndn::tlv::nfd::FaceStatus) {
+          BOOST_FAIL("expected face status, got type #" << i->type());
+      }
+      validateFaceStatus(*i, *iReference);
+      ++iReference;
+    }
+    m_finished = true;
+  }
+
+  void
+  decodeNackBlock(const Data& data)
+  {
+    BOOST_REQUIRE_EQUAL(data.getContentType(), tlv::ContentType_Nack);
+
+    m_finished = true;
+  }
+
+protected:
+  Forwarder m_forwarder;
+  FaceTable m_table;
+  shared_ptr<InternalFace> m_face;
+  ndn::EncodingBuffer m_buffer;
+  std::list<shared_ptr<Face> > m_referenceFaces;
+  ndn::KeyChain m_keyChain;
+  shared_ptr<DummyFace> m_dummyFace;
+  shared_ptr<DummyLocalFace> m_dummyLocalFace;
+  shared_ptr<DummyFace> m_dummyUri;
+  UdpFactory m_factory;
+
+protected:
+  bool m_finished;
+};
+
+} // namespace tests
+} // namespace nfd
+
+#endif // NFD_TESTS_NFD_MGMT_FACE_QUERY_STATUS_PUBLISHER_COMMON_HPP
diff --git a/NFD/tests/daemon/mgmt/face-query-status-publisher.cpp b/NFD/tests/daemon/mgmt/face-query-status-publisher.cpp
new file mode 100644
index 0000000..948ee88
--- /dev/null
+++ b/NFD/tests/daemon/mgmt/face-query-status-publisher.cpp
@@ -0,0 +1,131 @@
+/* -*- 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/>.
+ */
+
+#include "face-query-status-publisher-common.hpp"
+
+namespace nfd {
+namespace tests {
+
+NFD_LOG_INIT("FaceQueryStatusPublisherTest");
+
+BOOST_FIXTURE_TEST_SUITE(MgmtFaceQuerySatusPublisher, FaceQueryStatusPublisherFixture)
+
+BOOST_AUTO_TEST_CASE(NoConditionFilter)
+{
+  // filter without conditions matches all faces
+  ndn::nfd::FaceQueryFilter filter;
+  FaceQueryStatusPublisher faceQueryStatusPublisher(m_table, *m_face,
+                                                    "/localhost/nfd/FaceStatusPublisherFixture",
+                                                    filter, m_keyChain);
+
+  BOOST_CHECK_EQUAL(faceQueryStatusPublisher.doesMatchFilter(m_dummyFace), true);
+  BOOST_CHECK_EQUAL(faceQueryStatusPublisher.doesMatchFilter(m_dummyLocalFace), true);
+}
+
+BOOST_AUTO_TEST_CASE(AllConditionFilter)
+{
+  ndn::nfd::FaceQueryFilter filter;
+  filter.setUriScheme("dummy")
+        .setRemoteUri("dummy://")
+        .setLocalUri("dummy://")
+        .setFaceScope(ndn::nfd::FACE_SCOPE_NON_LOCAL)
+        .setFacePersistency(ndn::nfd::FACE_PERSISTENCY_PERSISTENT)
+        .setLinkType(ndn::nfd::LINK_TYPE_POINT_TO_POINT);
+
+  FaceQueryStatusPublisher faceQueryStatusPublisher(m_table, *m_face,
+                                                    "/localhost/nfd/FaceStatusPublisherFixture",
+                                                    filter, m_keyChain);
+  BOOST_CHECK_EQUAL(faceQueryStatusPublisher.doesMatchFilter(m_dummyFace), true);
+  BOOST_CHECK_EQUAL(faceQueryStatusPublisher.doesMatchFilter(m_dummyLocalFace), false);
+}
+
+BOOST_AUTO_TEST_CASE(UriSchemeFilter)
+{
+  ndn::nfd::FaceQueryFilter filter;
+  filter.setUriScheme("dummyurischeme");
+  FaceQueryStatusPublisher faceQueryStatusPublisher(m_table, *m_face,
+                                                    "/localhost/nfd/FaceStatusPublisherFixture",
+                                                    filter, m_keyChain);
+  BOOST_CHECK_EQUAL(faceQueryStatusPublisher.doesMatchFilter(m_dummyFace), false);
+  auto dummyUriScheme = make_shared<DummyFace>("dummyurischeme://", "dummyurischeme://");
+  BOOST_CHECK_EQUAL(faceQueryStatusPublisher.doesMatchFilter(dummyUriScheme), true);
+}
+
+BOOST_AUTO_TEST_CASE(RemoteUriFilter)
+{
+  ndn::nfd::FaceQueryFilter filter;
+  filter.setRemoteUri("dummy://remoteUri");
+  FaceQueryStatusPublisher faceQueryStatusPublisher(m_table, *m_face,
+                                                    "/localhost/nfd/FaceStatusPublisherFixture",
+                                                    filter, m_keyChain);
+  BOOST_CHECK_EQUAL(faceQueryStatusPublisher.doesMatchFilter(m_dummyFace), false);
+  BOOST_CHECK_EQUAL(faceQueryStatusPublisher.doesMatchFilter(m_dummyUri), true);
+}
+
+BOOST_AUTO_TEST_CASE(LocalUriFilter)
+{
+  ndn::nfd::FaceQueryFilter filter;
+  filter.setLocalUri("dummy://localUri");
+  FaceQueryStatusPublisher faceQueryStatusPublisher(m_table, *m_face,
+                                                    "/localhost/nfd/FaceStatusPublisherFixture",
+                                                    filter, m_keyChain);
+  BOOST_CHECK_EQUAL(faceQueryStatusPublisher.doesMatchFilter(m_dummyFace), false);
+  BOOST_CHECK_EQUAL(faceQueryStatusPublisher.doesMatchFilter(m_dummyUri), true);
+}
+
+
+BOOST_AUTO_TEST_CASE(LinkTypeFilter)
+{
+  shared_ptr<MulticastUdpFace> multicastFace = m_factory.createMulticastFace("0.0.0.0",
+                                                                             "224.0.0.1",
+                                                                             "20070");
+  ndn::nfd::FaceQueryFilter filter;
+  filter.setLinkType(ndn::nfd::LINK_TYPE_MULTI_ACCESS);
+  FaceQueryStatusPublisher faceQueryStatusPublisher(m_table, *m_face,
+                                                    "/localhost/nfd/FaceStatusPublisherFixture",
+                                                    filter, m_keyChain);
+  BOOST_CHECK_EQUAL(faceQueryStatusPublisher.doesMatchFilter(m_dummyFace), false);
+  BOOST_CHECK_EQUAL(faceQueryStatusPublisher.doesMatchFilter(multicastFace), true);
+}
+
+BOOST_AUTO_TEST_CASE(PersistencyFilter)
+{
+  shared_ptr<MulticastUdpFace> multicastFace = m_factory.createMulticastFace("0.0.0.0",
+                                                                             "224.0.0.1",
+                                                                             "20070");
+  ndn::nfd::FaceQueryFilter filter;
+  filter.setFacePersistency(ndn::nfd::FACE_PERSISTENCY_ON_DEMAND);
+  FaceQueryStatusPublisher faceQueryStatusPublisher(m_table, *m_face,
+                                                    "/localhost/nfd/FaceStatusPublisherFixture",
+                                                    filter, m_keyChain);
+  BOOST_CHECK_EQUAL(faceQueryStatusPublisher.doesMatchFilter(m_dummyFace), false);
+  multicastFace->setOnDemand(true);
+  BOOST_CHECK_EQUAL(faceQueryStatusPublisher.doesMatchFilter(multicastFace), true);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/NFD/tests/daemon/mgmt/face-status-publisher-common.hpp b/NFD/tests/daemon/mgmt/face-status-publisher-common.hpp
new file mode 100644
index 0000000..11f4913
--- /dev/null
+++ b/NFD/tests/daemon/mgmt/face-status-publisher-common.hpp
@@ -0,0 +1,220 @@
+/* -*- 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_TESTS_NFD_MGMT_FACE_STATUS_PUBLISHER_COMMON_HPP
+#define NFD_TESTS_NFD_MGMT_FACE_STATUS_PUBLISHER_COMMON_HPP
+
+#include "mgmt/face-status-publisher.hpp"
+#include "mgmt/app-face.hpp"
+#include "mgmt/internal-face.hpp"
+#include "fw/forwarder.hpp"
+
+#include "tests/test-common.hpp"
+#include "tests/daemon/face/dummy-face.hpp"
+
+#include <ndn-cxx/management/nfd-face-status.hpp>
+
+namespace nfd {
+namespace tests {
+
+class TestCountersFace : public DummyFace
+{
+public:
+
+  TestCountersFace()
+  {
+  }
+
+  virtual
+  ~TestCountersFace()
+  {
+  }
+
+  void
+  setCounters(PacketCounter::rep nInInterests,
+              PacketCounter::rep nInDatas,
+              PacketCounter::rep nOutInterests,
+              PacketCounter::rep nOutDatas,
+              ByteCounter::rep nInBytes,
+              ByteCounter::rep nOutBytes)
+  {
+    FaceCounters& counters = getMutableCounters();
+    counters.getNInInterests().set(nInInterests);
+    counters.getNInDatas().set(nInDatas);
+    counters.getNOutInterests().set(nOutInterests);
+    counters.getNOutDatas().set(nOutDatas);
+    counters.getNInBytes().set(nInBytes);
+    counters.getNOutBytes().set(nOutBytes);
+  }
+
+
+};
+
+static inline uint64_t
+readNonNegativeIntegerType(const Block& block,
+                           uint32_t type)
+{
+  if (block.type() == type)
+    {
+      return readNonNegativeInteger(block);
+    }
+  std::stringstream error;
+  error << "expected type " << type << " got " << block.type();
+  throw tlv::Error(error.str());
+}
+
+static inline uint64_t
+checkedReadNonNegativeIntegerType(Block::element_const_iterator& i,
+                                  Block::element_const_iterator end,
+                                  uint32_t type)
+{
+  if (i != end)
+    {
+      const Block& block = *i;
+      ++i;
+      return readNonNegativeIntegerType(block, type);
+    }
+  throw tlv::Error("Unexpected end of FaceStatus");
+}
+
+class FaceStatusPublisherFixture : public BaseFixture
+{
+public:
+
+  FaceStatusPublisherFixture()
+    : m_table(m_forwarder)
+    , m_face(make_shared<InternalFace>())
+    , m_publisher(m_table, *m_face, "/localhost/nfd/FaceStatusPublisherFixture", m_keyChain)
+    , m_finished(false)
+  {
+
+  }
+
+  virtual
+  ~FaceStatusPublisherFixture()
+  {
+
+  }
+
+  void
+  add(shared_ptr<Face> face)
+  {
+    m_table.add(face);
+  }
+
+  void
+  validateFaceStatus(const Block& statusBlock, const shared_ptr<Face>& reference)
+  {
+    ndn::nfd::FaceStatus status;
+    BOOST_REQUIRE_NO_THROW(status.wireDecode(statusBlock));
+    const FaceCounters& counters = reference->getCounters();
+
+    BOOST_CHECK_EQUAL(status.getFaceId(), reference->getId());
+    BOOST_CHECK_EQUAL(status.getRemoteUri(), reference->getRemoteUri().toString());
+    BOOST_CHECK_EQUAL(status.getLocalUri(), reference->getLocalUri().toString());
+
+    if (reference->isLocal()) {
+      BOOST_CHECK_EQUAL(status.getFaceScope(), ndn::nfd::FACE_SCOPE_LOCAL);
+    }
+    else {
+      BOOST_CHECK_EQUAL(status.getFaceScope(), ndn::nfd::FACE_SCOPE_NON_LOCAL);
+    }
+
+    if (reference->isOnDemand()) {
+      BOOST_CHECK_EQUAL(status.getFacePersistency(), ndn::nfd::FACE_PERSISTENCY_ON_DEMAND);
+    }
+    else {
+      BOOST_CHECK_EQUAL(status.getFacePersistency(), ndn::nfd::FACE_PERSISTENCY_PERSISTENT);
+    }
+
+    if (reference->isMultiAccess()) {
+      BOOST_CHECK_EQUAL(status.getLinkType(), ndn::nfd::LINK_TYPE_MULTI_ACCESS);
+    }
+    else {
+      BOOST_CHECK_EQUAL(status.getLinkType(), ndn::nfd::LINK_TYPE_POINT_TO_POINT);
+    }
+
+    BOOST_CHECK_EQUAL(status.getNInInterests(), counters.getNInInterests());
+    BOOST_CHECK_EQUAL(status.getNInDatas(), counters.getNInDatas());
+    BOOST_CHECK_EQUAL(status.getNOutInterests(), counters.getNOutInterests());
+    BOOST_CHECK_EQUAL(status.getNOutDatas(), counters.getNOutDatas());
+    BOOST_CHECK_EQUAL(status.getNInBytes(), counters.getNInBytes());
+    BOOST_CHECK_EQUAL(status.getNOutBytes(), counters.getNOutBytes());
+  }
+
+  void
+  decodeFaceStatusBlock(const Data& data)
+  {
+    Block payload = data.getContent();
+
+    m_buffer.appendByteArray(payload.value(), payload.value_size());
+
+    BOOST_CHECK_NO_THROW(data.getName()[-1].toSegment());
+    if (data.getFinalBlockId() != data.getName()[-1])
+      {
+        return;
+      }
+
+    // wrap the Face Statuses in a single Content TLV for easy parsing
+    m_buffer.prependVarNumber(m_buffer.size());
+    m_buffer.prependVarNumber(tlv::Content);
+
+    ndn::Block parser(m_buffer.buf(), m_buffer.size());
+    parser.parse();
+
+    BOOST_REQUIRE_EQUAL(parser.elements_size(), m_referenceFaces.size());
+
+    std::list<shared_ptr<Face> >::const_iterator iReference = m_referenceFaces.begin();
+    for (Block::element_const_iterator i = parser.elements_begin();
+         i != parser.elements_end();
+         ++i)
+      {
+        if (i->type() != ndn::tlv::nfd::FaceStatus)
+          {
+            BOOST_FAIL("expected face status, got type #" << i->type());
+          }
+        validateFaceStatus(*i, *iReference);
+        ++iReference;
+      }
+    m_finished = true;
+  }
+
+protected:
+  Forwarder m_forwarder;
+  FaceTable m_table;
+  shared_ptr<InternalFace> m_face;
+  FaceStatusPublisher m_publisher;
+  ndn::EncodingBuffer m_buffer;
+  std::list<shared_ptr<Face> > m_referenceFaces;
+  ndn::KeyChain m_keyChain;
+
+protected:
+  bool m_finished;
+};
+
+} // namespace tests
+} // namespace nfd
+
+#endif // NFD_TESTS_NFD_MGMT_FACE_STATUS_PUBLISHER_COMMON_HPP
diff --git a/NFD/tests/daemon/mgmt/face-status-publisher.cpp b/NFD/tests/daemon/mgmt/face-status-publisher.cpp
new file mode 100644
index 0000000..145fe0e
--- /dev/null
+++ b/NFD/tests/daemon/mgmt/face-status-publisher.cpp
@@ -0,0 +1,66 @@
+/* -*- 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/>.
+ */
+
+#include "face-status-publisher-common.hpp"
+
+namespace nfd {
+namespace tests {
+
+NFD_LOG_INIT("FaceStatusPublisherTest");
+
+BOOST_FIXTURE_TEST_SUITE(MgmtFaceSatusPublisher, FaceStatusPublisherFixture)
+
+BOOST_AUTO_TEST_CASE(EncodingDecoding)
+{
+  Name commandName("/localhost/nfd/faces/list");
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+
+  // MAX_SEGMENT_SIZE == 4400, FaceStatus size with filler counters is 75
+  // use 59 FaceStatuses to force a FaceStatus to span Data packets
+  for (int i = 0; i < 59; i++)
+    {
+      shared_ptr<TestCountersFace> dummy(make_shared<TestCountersFace>());
+
+      uint64_t filler = std::numeric_limits<uint64_t>::max() - 1;
+      dummy->setCounters(filler, filler, filler, filler, filler, filler);
+
+      m_referenceFaces.push_back(dummy);
+
+      add(dummy);
+    }
+
+  ndn::EncodingBuffer buffer;
+
+  m_face->onReceiveData +=
+    bind(&FaceStatusPublisherFixture::decodeFaceStatusBlock, this, _1);
+
+  m_publisher.publish();
+  BOOST_REQUIRE(m_finished);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/NFD/tests/daemon/mgmt/fib-enumeration-publisher-common.hpp b/NFD/tests/daemon/mgmt/fib-enumeration-publisher-common.hpp
new file mode 100644
index 0000000..cbb7280
--- /dev/null
+++ b/NFD/tests/daemon/mgmt/fib-enumeration-publisher-common.hpp
@@ -0,0 +1,221 @@
+/* -*- 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_TESTS_NFD_MGMT_FIB_ENUMERATION_PUBLISHER_COMMON_HPP
+#define NFD_TESTS_NFD_MGMT_FIB_ENUMERATION_PUBLISHER_COMMON_HPP
+
+#include "mgmt/fib-enumeration-publisher.hpp"
+
+#include "mgmt/app-face.hpp"
+#include "mgmt/internal-face.hpp"
+#include "table/fib.hpp"
+#include "table/name-tree.hpp"
+
+#include "tests/test-common.hpp"
+#include "../face/dummy-face.hpp"
+
+#include <ndn-cxx/encoding/tlv.hpp>
+
+namespace nfd {
+namespace tests {
+
+static inline uint64_t
+readNonNegativeIntegerType(const Block& block,
+                           uint32_t type)
+{
+  if (block.type() == type)
+    {
+      return readNonNegativeInteger(block);
+    }
+  std::stringstream error;
+  error << "Expected type " << type << " got " << block.type();
+  throw tlv::Error(error.str());
+}
+
+static inline uint64_t
+checkedReadNonNegativeIntegerType(Block::element_const_iterator& i,
+                                  Block::element_const_iterator end,
+                                  uint32_t type)
+{
+  if (i != end)
+    {
+      const Block& block = *i;
+      ++i;
+      return readNonNegativeIntegerType(block, type);
+    }
+  std::stringstream error;
+  error << "Unexpected end of Block while attempting to read type #"
+        << type;
+  throw tlv::Error(error.str());
+}
+
+class FibEnumerationPublisherFixture : public BaseFixture
+{
+public:
+
+  FibEnumerationPublisherFixture()
+    : m_fib(m_nameTree)
+    , m_face(make_shared<InternalFace>())
+    , m_publisher(m_fib, *m_face, "/localhost/nfd/FibEnumerationPublisherFixture", m_keyChain)
+    , m_finished(false)
+  {
+  }
+
+  virtual
+  ~FibEnumerationPublisherFixture()
+  {
+  }
+
+  bool
+  hasNextHopWithCost(const fib::NextHopList& nextHops,
+                     FaceId faceId,
+                     uint64_t cost)
+  {
+    for (fib::NextHopList::const_iterator i = nextHops.begin();
+         i != nextHops.end();
+         ++i)
+      {
+        if (i->getFace()->getId() == faceId && i->getCost() == cost)
+          {
+            return true;
+          }
+      }
+    return false;
+  }
+
+  bool
+  entryHasPrefix(const shared_ptr<fib::Entry> entry, const Name& prefix)
+  {
+    return entry->getPrefix() == prefix;
+  }
+
+  void
+  validateFibEntry(const Block& entry)
+  {
+    entry.parse();
+
+    Block::element_const_iterator i = entry.elements_begin();
+    BOOST_REQUIRE(i != entry.elements_end());
+
+
+    BOOST_REQUIRE(i->type() == tlv::Name);
+    Name prefix(*i);
+    ++i;
+
+    std::set<shared_ptr<fib::Entry> >::const_iterator referenceIter =
+      std::find_if(m_referenceEntries.begin(), m_referenceEntries.end(),
+                   bind(&FibEnumerationPublisherFixture::entryHasPrefix,
+                        this, _1, prefix));
+
+    BOOST_REQUIRE(referenceIter != m_referenceEntries.end());
+
+    const shared_ptr<fib::Entry>& reference = *referenceIter;
+    BOOST_REQUIRE_EQUAL(prefix, reference->getPrefix());
+
+    // 0 or more next hop records
+    size_t nRecords = 0;
+    const fib::NextHopList& referenceNextHops = reference->getNextHops();
+    for (; i != entry.elements_end(); ++i)
+      {
+        const ndn::Block& nextHopRecord = *i;
+        BOOST_REQUIRE(nextHopRecord.type() == ndn::tlv::nfd::NextHopRecord);
+        nextHopRecord.parse();
+
+        Block::element_const_iterator j = nextHopRecord.elements_begin();
+
+        FaceId faceId =
+          checkedReadNonNegativeIntegerType(j,
+                                            entry.elements_end(),
+                                            ndn::tlv::nfd::FaceId);
+
+        uint64_t cost =
+          checkedReadNonNegativeIntegerType(j,
+                                            entry.elements_end(),
+                                            ndn::tlv::nfd::Cost);
+
+        BOOST_REQUIRE(hasNextHopWithCost(referenceNextHops, faceId, cost));
+
+        BOOST_REQUIRE(j == nextHopRecord.elements_end());
+        nRecords++;
+      }
+    BOOST_REQUIRE_EQUAL(nRecords, referenceNextHops.size());
+
+    BOOST_REQUIRE(i == entry.elements_end());
+    m_referenceEntries.erase(referenceIter);
+  }
+
+  void
+  decodeFibEntryBlock(const Data& data)
+  {
+    Block payload = data.getContent();
+
+    m_buffer.appendByteArray(payload.value(), payload.value_size());
+
+    BOOST_CHECK_NO_THROW(data.getName()[-1].toSegment());
+    if (data.getFinalBlockId() != data.getName()[-1])
+      {
+        return;
+      }
+
+    // wrap the FIB Entry blocks in a single Content TLV for easy parsing
+    m_buffer.prependVarNumber(m_buffer.size());
+    m_buffer.prependVarNumber(tlv::Content);
+
+    ndn::Block parser(m_buffer.buf(), m_buffer.size());
+    parser.parse();
+
+    BOOST_REQUIRE_EQUAL(parser.elements_size(), m_referenceEntries.size());
+
+    for (Block::element_const_iterator i = parser.elements_begin();
+         i != parser.elements_end();
+         ++i)
+      {
+        if (i->type() != ndn::tlv::nfd::FibEntry)
+          {
+            BOOST_FAIL("expected fib entry, got type #" << i->type());
+          }
+
+        validateFibEntry(*i);
+      }
+    m_finished = true;
+  }
+
+protected:
+  NameTree m_nameTree;
+  Fib m_fib;
+  shared_ptr<InternalFace> m_face;
+  FibEnumerationPublisher m_publisher;
+  ndn::EncodingBuffer m_buffer;
+  std::set<shared_ptr<fib::Entry> > m_referenceEntries;
+  ndn::KeyChain m_keyChain;
+
+protected:
+  bool m_finished;
+};
+
+} // namespace tests
+} // namespace nfd
+
+#endif // NFD_TESTS_NFD_MGMT_FIB_ENUMERATION_PUBLISHER_COMMON_HPP
diff --git a/NFD/tests/daemon/mgmt/fib-enumeration-publisher.cpp b/NFD/tests/daemon/mgmt/fib-enumeration-publisher.cpp
new file mode 100644
index 0000000..ed330aa
--- /dev/null
+++ b/NFD/tests/daemon/mgmt/fib-enumeration-publisher.cpp
@@ -0,0 +1,90 @@
+/* -*- 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/>.
+ */
+
+#include "mgmt/fib-enumeration-publisher.hpp"
+
+#include "mgmt/app-face.hpp"
+#include "mgmt/internal-face.hpp"
+
+#include "tests/test-common.hpp"
+#include "../face/dummy-face.hpp"
+
+#include "fib-enumeration-publisher-common.hpp"
+
+#include <ndn-cxx/encoding/tlv.hpp>
+
+namespace nfd {
+namespace tests {
+
+NFD_LOG_INIT("TestFibEnumerationPublisher");
+
+
+
+BOOST_FIXTURE_TEST_SUITE(MgmtFibEnumeration, FibEnumerationPublisherFixture)
+
+BOOST_AUTO_TEST_CASE(TestFibEnumerationPublisher)
+{
+  for (int i = 0; i < 87; i++)
+    {
+      Name prefix("/test");
+      prefix.appendSegment(i);
+
+      shared_ptr<DummyFace> dummy1(make_shared<DummyFace>());
+      shared_ptr<DummyFace> dummy2(make_shared<DummyFace>());
+
+      shared_ptr<fib::Entry> entry = m_fib.insert(prefix).first;
+      entry->addNextHop(dummy1, std::numeric_limits<uint64_t>::max() - 1);
+      entry->addNextHop(dummy2, std::numeric_limits<uint64_t>::max() - 2);
+
+      m_referenceEntries.insert(entry);
+    }
+  for (int i = 0; i < 2; i++)
+    {
+      Name prefix("/test2");
+      prefix.appendSegment(i);
+
+      shared_ptr<DummyFace> dummy1(make_shared<DummyFace>());
+      shared_ptr<DummyFace> dummy2(make_shared<DummyFace>());
+
+      shared_ptr<fib::Entry> entry = m_fib.insert(prefix).first;
+      entry->addNextHop(dummy1, std::numeric_limits<uint8_t>::max() - 1);
+      entry->addNextHop(dummy2, std::numeric_limits<uint8_t>::max() - 2);
+
+      m_referenceEntries.insert(entry);
+    }
+
+  ndn::EncodingBuffer buffer;
+
+  m_face->onReceiveData +=
+    bind(&FibEnumerationPublisherFixture::decodeFibEntryBlock, this, _1);
+
+  m_publisher.publish();
+  BOOST_REQUIRE(m_finished);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/NFD/tests/daemon/mgmt/fib-manager.cpp b/NFD/tests/daemon/mgmt/fib-manager.cpp
new file mode 100644
index 0000000..525dfc8
--- /dev/null
+++ b/NFD/tests/daemon/mgmt/fib-manager.cpp
@@ -0,0 +1,920 @@
+/* -*- 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/>.
+ */
+
+#include "mgmt/fib-manager.hpp"
+#include "table/fib.hpp"
+#include "table/fib-nexthop.hpp"
+#include "face/face.hpp"
+#include "mgmt/internal-face.hpp"
+#include "tests/daemon/face/dummy-face.hpp"
+
+#include "validation-common.hpp"
+#include "tests/test-common.hpp"
+
+#include "fib-enumeration-publisher-common.hpp"
+
+namespace nfd {
+namespace tests {
+
+NFD_LOG_INIT("FibManagerTest");
+
+class FibManagerFixture : public FibEnumerationPublisherFixture
+{
+public:
+
+  virtual
+  ~FibManagerFixture()
+  {
+  }
+
+  shared_ptr<Face>
+  getFace(FaceId id)
+  {
+    if (id > 0 && static_cast<size_t>(id) <= m_faces.size())
+      {
+        return m_faces[id - 1];
+      }
+    NFD_LOG_DEBUG("No face found returning NULL");
+    return shared_ptr<DummyFace>();
+  }
+
+  void
+  addFace(shared_ptr<Face> face)
+  {
+    m_faces.push_back(face);
+  }
+
+  void
+  validateControlResponseCommon(const Data& response,
+                                const Name& expectedName,
+                                uint32_t expectedCode,
+                                const std::string& expectedText,
+                                ControlResponse& control)
+  {
+    m_callbackFired = true;
+    Block controlRaw = response.getContent().blockFromValue();
+
+    control.wireDecode(controlRaw);
+
+    // NFD_LOG_DEBUG("received control response"
+    //               << " Name: " << response.getName()
+    //               << " code: " << control.getCode()
+    //               << " text: " << control.getText());
+
+    BOOST_CHECK_EQUAL(response.getName(), expectedName);
+    BOOST_CHECK_EQUAL(control.getCode(), expectedCode);
+    BOOST_CHECK_EQUAL(control.getText(), expectedText);
+  }
+
+  void
+  validateControlResponse(const Data& response,
+                          const Name& expectedName,
+                          uint32_t expectedCode,
+                          const std::string& expectedText)
+  {
+    ControlResponse control;
+    validateControlResponseCommon(response, expectedName,
+                                  expectedCode, expectedText, control);
+
+    if (!control.getBody().empty())
+      {
+        BOOST_FAIL("found unexpected control response body");
+      }
+  }
+
+  void
+  validateControlResponse(const Data& response,
+                          const Name& expectedName,
+                          uint32_t expectedCode,
+                          const std::string& expectedText,
+                          const Block& expectedBody)
+  {
+    ControlResponse control;
+    validateControlResponseCommon(response, expectedName,
+                                  expectedCode, expectedText, control);
+
+    BOOST_REQUIRE(!control.getBody().empty());
+    BOOST_REQUIRE_EQUAL(control.getBody().value_size(), expectedBody.value_size());
+
+    BOOST_CHECK(memcmp(control.getBody().value(), expectedBody.value(),
+                       expectedBody.value_size()) == 0);
+
+  }
+
+  bool
+  didCallbackFire()
+  {
+    return m_callbackFired;
+  }
+
+  void
+  resetCallbackFired()
+  {
+    m_callbackFired = false;
+  }
+
+  shared_ptr<InternalFace>
+  getInternalFace()
+  {
+    return m_face;
+  }
+
+  FibManager&
+  getFibManager()
+  {
+    return m_manager;
+  }
+
+  Fib&
+  getFib()
+  {
+    return m_fib;
+  }
+
+  void
+  addInterestRule(const std::string& regex,
+                  ndn::IdentityCertificate& certificate)
+  {
+    m_manager.addInterestRule(regex, certificate);
+  }
+
+protected:
+  FibManagerFixture()
+    : m_manager(ref(m_fib), bind(&FibManagerFixture::getFace, this, _1), m_face, m_keyChain)
+    , m_callbackFired(false)
+  {
+  }
+
+protected:
+  FibManager m_manager;
+
+  std::vector<shared_ptr<Face> > m_faces;
+  bool m_callbackFired;
+  ndn::KeyChain m_keyChain;
+};
+
+template <typename T>
+class AuthorizedCommandFixture : public CommandFixture<T>
+{
+public:
+  AuthorizedCommandFixture()
+  {
+    const std::string regex = "^<localhost><nfd><fib>";
+    T::addInterestRule(regex, *CommandFixture<T>::m_certificate);
+  }
+
+  virtual
+  ~AuthorizedCommandFixture()
+  {
+  }
+};
+
+BOOST_FIXTURE_TEST_SUITE(MgmtFibManager, AuthorizedCommandFixture<FibManagerFixture>)
+
+bool
+foundNextHop(FaceId id, uint32_t cost, const fib::NextHop& next)
+{
+  return id == next.getFace()->getId() && next.getCost() == cost;
+}
+
+bool
+addedNextHopWithCost(const Fib& fib, const Name& prefix, size_t oldSize, uint32_t cost)
+{
+  shared_ptr<fib::Entry> entry = fib.findExactMatch(prefix);
+
+  if (static_cast<bool>(entry))
+    {
+      const fib::NextHopList& hops = entry->getNextHops();
+      return hops.size() == oldSize + 1 &&
+        std::find_if(hops.begin(), hops.end(), bind(&foundNextHop, -1, cost, _1)) != hops.end();
+    }
+  return false;
+}
+
+bool
+foundNextHopWithFace(FaceId id, uint32_t cost,
+                     shared_ptr<Face> face, const fib::NextHop& next)
+{
+  return id == next.getFace()->getId() && next.getCost() == cost && face == next.getFace();
+}
+
+bool
+addedNextHopWithFace(const Fib& fib, const Name& prefix, size_t oldSize,
+                     uint32_t cost, shared_ptr<Face> face)
+{
+  shared_ptr<fib::Entry> entry = fib.findExactMatch(prefix);
+
+  if (static_cast<bool>(entry))
+    {
+      const fib::NextHopList& hops = entry->getNextHops();
+      return hops.size() == oldSize + 1 &&
+        std::find_if(hops.begin(), hops.end(), bind(&foundNextHop, -1, cost, _1)) != hops.end();
+    }
+  return false;
+}
+
+BOOST_AUTO_TEST_CASE(ShortName)
+{
+  shared_ptr<InternalFace> face = getInternalFace();
+
+  shared_ptr<Interest> command = makeInterest("/localhost/nfd/fib");
+
+  face->onReceiveData += [this, command] (const Data& response) {
+    this->validateControlResponse(response, command->getName(), 400, "Malformed command");
+  };
+
+  face->sendInterest(*command);
+  g_io.run_one();
+
+  BOOST_REQUIRE(didCallbackFire());
+}
+
+BOOST_AUTO_TEST_CASE(MalformedCommmand)
+{
+  shared_ptr<InternalFace> face = getInternalFace();
+
+  BOOST_REQUIRE(didCallbackFire() == false);
+
+  Interest command("/localhost/nfd/fib");
+
+  face->onReceiveData += [this, command] (const Data& response) {
+    this->validateControlResponse(response, command.getName(), 400, "Malformed command");
+  };
+
+  getFibManager().onFibRequest(command);
+
+  BOOST_REQUIRE(didCallbackFire());
+}
+
+BOOST_AUTO_TEST_CASE(UnsupportedVerb)
+{
+  shared_ptr<InternalFace> face = getInternalFace();
+
+  ControlParameters parameters;
+  parameters.setName("/hello");
+  parameters.setFaceId(1);
+  parameters.setCost(1);
+
+  Block encodedParameters(parameters.wireEncode());
+
+  Name commandName("/localhost/nfd/fib");
+  commandName.append("unsupported");
+  commandName.append(encodedParameters);
+
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+  generateCommand(*command);
+
+  face->onReceiveData += [this, command] (const Data& response) {
+    this->validateControlResponse(response, command->getName(), 501, "Unsupported command");
+  };
+
+  getFibManager().onFibRequest(*command);
+
+  BOOST_REQUIRE(didCallbackFire());
+}
+
+BOOST_AUTO_TEST_CASE(UnsignedCommand)
+{
+  addFace(make_shared<DummyFace>());
+
+  shared_ptr<InternalFace> face = getInternalFace();
+
+  ControlParameters parameters;
+  parameters.setName("/hello");
+  parameters.setFaceId(1);
+  parameters.setCost(101);
+
+  Block encodedParameters(parameters.wireEncode());
+
+  Name commandName("/localhost/nfd/fib");
+  commandName.append("add-nexthop");
+  commandName.append(encodedParameters);
+
+  Interest command(commandName);
+
+  face->onReceiveData += [this, command] (const Data& response) {
+    this->validateControlResponse(response, command.getName(), 401, "Signature required");
+  };
+
+  getFibManager().onFibRequest(command);
+
+  BOOST_REQUIRE(didCallbackFire());
+  BOOST_REQUIRE(!addedNextHopWithCost(getFib(), "/hello", 0, 101));
+}
+
+BOOST_FIXTURE_TEST_CASE(UnauthorizedCommand, UnauthorizedCommandFixture<FibManagerFixture>)
+{
+  addFace(make_shared<DummyFace>());
+
+  shared_ptr<InternalFace> face = getInternalFace();
+
+  ControlParameters parameters;
+  parameters.setName("/hello");
+  parameters.setFaceId(1);
+  parameters.setCost(101);
+
+  Block encodedParameters(parameters.wireEncode());
+
+  Name commandName("/localhost/nfd/fib");
+  commandName.append("add-nexthop");
+  commandName.append(encodedParameters);
+
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+  generateCommand(*command);
+
+  face->onReceiveData += [this, command] (const Data& response) {
+    this->validateControlResponse(response, command->getName(), 403, "Unauthorized command");
+  };
+
+  getFibManager().onFibRequest(*command);
+
+  BOOST_REQUIRE(didCallbackFire());
+  BOOST_REQUIRE(!addedNextHopWithCost(getFib(), "/hello", 0, 101));
+}
+
+BOOST_AUTO_TEST_CASE(BadOptionParse)
+{
+  addFace(make_shared<DummyFace>());
+
+  shared_ptr<InternalFace> face = getInternalFace();
+
+  Name commandName("/localhost/nfd/fib");
+  commandName.append("add-nexthop");
+  commandName.append("NotReallyParameters");
+
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+  generateCommand(*command);
+
+  face->onReceiveData += [this, command] (const Data& response) {
+    this->validateControlResponse(response, command->getName(), 400, "Malformed command");
+  };
+
+  getFibManager().onFibRequest(*command);
+
+  BOOST_REQUIRE(didCallbackFire());
+}
+
+BOOST_AUTO_TEST_CASE(UnknownFaceId)
+{
+  addFace(make_shared<DummyFace>());
+
+  shared_ptr<InternalFace> face = getInternalFace();
+
+  ControlParameters parameters;
+  parameters.setName("/hello");
+  parameters.setFaceId(1000);
+  parameters.setCost(101);
+
+  Block encodedParameters(parameters.wireEncode());
+
+  Name commandName("/localhost/nfd/fib");
+  commandName.append("add-nexthop");
+  commandName.append(encodedParameters);
+
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+  generateCommand(*command);
+
+  face->onReceiveData += [this, command] (const Data& response) {
+    this->validateControlResponse(response, command->getName(), 410, "Face not found");
+  };
+
+  getFibManager().onFibRequest(*command);
+
+  BOOST_REQUIRE(didCallbackFire());
+  BOOST_REQUIRE(addedNextHopWithCost(getFib(), "/hello", 0, 101) == false);
+}
+
+BOOST_AUTO_TEST_CASE(AddNextHopVerbImplicitFaceId)
+{
+  addFace(make_shared<DummyFace>());
+
+  shared_ptr<InternalFace> face = getInternalFace();
+
+  std::vector<ControlParameters> testedParameters;
+  testedParameters.push_back(ControlParameters().setName("/hello").setCost(101).setFaceId(0));
+  testedParameters.push_back(ControlParameters().setName("/hello").setCost(101));
+
+  for (std::vector<ControlParameters>::iterator parameters = testedParameters.begin();
+       parameters != testedParameters.end(); ++parameters) {
+
+    Block encodedParameters(parameters->wireEncode());
+
+    Name commandName("/localhost/nfd/fib");
+    commandName.append("add-nexthop");
+    commandName.append(encodedParameters);
+
+    ControlParameters expectedParameters;
+    expectedParameters.setName("/hello");
+    expectedParameters.setFaceId(1);
+    expectedParameters.setCost(101);
+
+    Block encodedExpectedParameters(expectedParameters.wireEncode());
+
+    shared_ptr<Interest> command(make_shared<Interest>(commandName));
+    command->setIncomingFaceId(1);
+    generateCommand(*command);
+
+    face->onReceiveData += [this, command, encodedExpectedParameters] (const Data& response) {
+      this->validateControlResponse(response, command->getName(),
+                                    200, "Success", encodedExpectedParameters);
+    };
+
+    getFibManager().onFibRequest(*command);
+
+    BOOST_REQUIRE(didCallbackFire());
+    BOOST_REQUIRE(addedNextHopWithFace(getFib(), "/hello", 0, 101, getFace(1)));
+
+    face->onReceiveData.clear();
+    getFib().erase("/hello");
+    BOOST_REQUIRE_EQUAL(getFib().size(), 0);
+  }
+}
+
+BOOST_AUTO_TEST_CASE(AddNextHopVerbInitialAdd)
+{
+  addFace(make_shared<DummyFace>());
+
+  shared_ptr<InternalFace> face = getInternalFace();
+
+  ControlParameters parameters;
+  parameters.setName("/hello");
+  parameters.setFaceId(1);
+  parameters.setCost(101);
+
+  Block encodedParameters(parameters.wireEncode());
+
+  Name commandName("/localhost/nfd/fib");
+  commandName.append("add-nexthop");
+  commandName.append(encodedParameters);
+
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+  generateCommand(*command);
+
+  face->onReceiveData += [this, command, encodedParameters] (const Data& response) {
+    this->validateControlResponse(response, command->getName(),
+                                  200, "Success", encodedParameters);
+  };
+
+  getFibManager().onFibRequest(*command);
+
+  BOOST_REQUIRE(didCallbackFire());
+  BOOST_REQUIRE(addedNextHopWithCost(getFib(), "/hello", 0, 101));
+}
+
+BOOST_AUTO_TEST_CASE(AddNextHopVerbImplicitCost)
+{
+  addFace(make_shared<DummyFace>());
+
+  shared_ptr<InternalFace> face = getInternalFace();
+
+  ControlParameters parameters;
+  parameters.setName("/hello");
+  parameters.setFaceId(1);
+
+  Block encodedParameters(parameters.wireEncode());
+
+  Name commandName("/localhost/nfd/fib");
+  commandName.append("add-nexthop");
+  commandName.append(encodedParameters);
+
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+  generateCommand(*command);
+
+  ControlParameters resultParameters;
+  resultParameters.setName("/hello");
+  resultParameters.setFaceId(1);
+  resultParameters.setCost(0);
+
+  face->onReceiveData += [this, command, resultParameters] (const Data& response) {
+    this->validateControlResponse(response, command->getName(),
+                                  200, "Success", resultParameters.wireEncode());
+  };
+
+  getFibManager().onFibRequest(*command);
+
+  BOOST_REQUIRE(didCallbackFire());
+  BOOST_REQUIRE(addedNextHopWithCost(getFib(), "/hello", 0, 0));
+}
+
+BOOST_AUTO_TEST_CASE(AddNextHopVerbAddToExisting)
+{
+  addFace(make_shared<DummyFace>());
+  shared_ptr<InternalFace> face = getInternalFace();
+
+  for (int i = 1; i <= 2; i++)
+    {
+
+      ControlParameters parameters;
+      parameters.setName("/hello");
+      parameters.setFaceId(1);
+      parameters.setCost(100 + i);
+
+      Block encodedParameters(parameters.wireEncode());
+
+      Name commandName("/localhost/nfd/fib");
+      commandName.append("add-nexthop");
+      commandName.append(encodedParameters);
+
+      shared_ptr<Interest> command(make_shared<Interest>(commandName));
+      generateCommand(*command);
+
+      face->onReceiveData += [this, command, encodedParameters] (const Data& response) {
+        this->validateControlResponse(response, command->getName(),
+                                      200, "Success", encodedParameters);
+      };
+
+      getFibManager().onFibRequest(*command);
+      BOOST_REQUIRE(didCallbackFire());
+      resetCallbackFired();
+
+      shared_ptr<fib::Entry> entry = getFib().findExactMatch("/hello");
+
+      if (static_cast<bool>(entry))
+        {
+          const fib::NextHopList& hops = entry->getNextHops();
+          BOOST_REQUIRE(hops.size() == 1);
+          BOOST_REQUIRE(std::find_if(hops.begin(), hops.end(),
+                                     bind(&foundNextHop, -1, 100 + i, _1)) != hops.end());
+
+        }
+      else
+        {
+          BOOST_FAIL("Failed to find expected fib entry");
+        }
+
+      face->onReceiveData.clear();
+    }
+}
+
+BOOST_AUTO_TEST_CASE(AddNextHopVerbUpdateFaceCost)
+{
+  addFace(make_shared<DummyFace>());
+  shared_ptr<InternalFace> face = getInternalFace();
+
+  ControlParameters parameters;
+  parameters.setName("/hello");
+  parameters.setFaceId(1);
+
+  {
+    parameters.setCost(1);
+
+    Block encodedParameters(parameters.wireEncode());
+
+    Name commandName("/localhost/nfd/fib");
+    commandName.append("add-nexthop");
+    commandName.append(encodedParameters);
+
+    shared_ptr<Interest> command(make_shared<Interest>(commandName));
+    generateCommand(*command);
+
+    face->onReceiveData += [this, command, encodedParameters] (const Data& response) {
+      this->validateControlResponse(response, command->getName(),
+                                    200, "Success", encodedParameters);
+    };
+
+    getFibManager().onFibRequest(*command);
+
+    BOOST_REQUIRE(didCallbackFire());
+  }
+
+  resetCallbackFired();
+  face->onReceiveData.clear();
+
+  {
+    parameters.setCost(102);
+
+    Block encodedParameters(parameters.wireEncode());
+
+    Name commandName("/localhost/nfd/fib");
+    commandName.append("add-nexthop");
+    commandName.append(encodedParameters);
+
+    shared_ptr<Interest> command(make_shared<Interest>(commandName));
+    generateCommand(*command);
+
+    face->onReceiveData += [this, command, encodedParameters] (const Data& response) {
+      this->validateControlResponse(response, command->getName(),
+                                    200, "Success", encodedParameters);
+    };
+
+    getFibManager().onFibRequest(*command);
+
+    BOOST_REQUIRE(didCallbackFire());
+  }
+
+  shared_ptr<fib::Entry> entry = getFib().findExactMatch("/hello");
+
+  // Add faces with cost == FaceID for the name /hello
+  // This test assumes:
+  //   FaceIDs are -1 because we don't add them to a forwarder
+  if (static_cast<bool>(entry))
+    {
+      const fib::NextHopList& hops = entry->getNextHops();
+      BOOST_REQUIRE(hops.size() == 1);
+      BOOST_REQUIRE(std::find_if(hops.begin(),
+                                 hops.end(),
+                                 bind(&foundNextHop, -1, 102, _1)) != hops.end());
+    }
+  else
+    {
+      BOOST_FAIL("Failed to find expected fib entry");
+    }
+}
+
+BOOST_AUTO_TEST_CASE(AddNextHopVerbMissingPrefix)
+{
+  addFace(make_shared<DummyFace>());
+
+  shared_ptr<InternalFace> face = getInternalFace();
+
+  ControlParameters parameters;
+  parameters.setFaceId(1);
+
+  Block encodedParameters(parameters.wireEncode());
+
+  Name commandName("/localhost/nfd/fib");
+  commandName.append("add-nexthop");
+  commandName.append(encodedParameters);
+
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+  generateCommand(*command);
+
+  face->onReceiveData += [this, command] (const Data& response) {
+    this->validateControlResponse(response, command->getName(), 400, "Malformed command");
+  };
+
+  getFibManager().onFibRequest(*command);
+
+  BOOST_REQUIRE(didCallbackFire());
+}
+
+bool
+removedNextHopWithCost(const Fib& fib, const Name& prefix, size_t oldSize, uint32_t cost)
+{
+  shared_ptr<fib::Entry> entry = fib.findExactMatch(prefix);
+
+  if (static_cast<bool>(entry))
+    {
+      const fib::NextHopList& hops = entry->getNextHops();
+      return hops.size() == oldSize - 1 &&
+        std::find_if(hops.begin(), hops.end(), bind(&foundNextHop, -1, cost, _1)) == hops.end();
+    }
+  return false;
+}
+
+void
+testRemoveNextHop(CommandFixture<FibManagerFixture>* fixture,
+                  FibManager& manager,
+                  Fib& fib,
+                  shared_ptr<Face> face,
+                  const Name& targetName,
+                  FaceId targetFace)
+{
+  ControlParameters parameters;
+  parameters.setName(targetName);
+  parameters.setFaceId(targetFace);
+
+  Block encodedParameters(parameters.wireEncode());
+
+  Name commandName("/localhost/nfd/fib");
+  commandName.append("remove-nexthop");
+  commandName.append(encodedParameters);
+
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+  fixture->generateCommand(*command);
+
+  face->onReceiveData += [fixture, command, encodedParameters] (const Data& response) {
+    fixture->validateControlResponse(response, command->getName(),
+                                     200, "Success", encodedParameters);
+  };
+
+  manager.onFibRequest(*command);
+
+  BOOST_REQUIRE(fixture->didCallbackFire());
+
+  fixture->resetCallbackFired();
+  face->onReceiveData.clear();
+}
+
+BOOST_AUTO_TEST_CASE(RemoveNextHop)
+{
+  shared_ptr<Face> face1 = make_shared<DummyFace>();
+  shared_ptr<Face> face2 = make_shared<DummyFace>();
+  shared_ptr<Face> face3 = make_shared<DummyFace>();
+
+  addFace(face1);
+  addFace(face2);
+  addFace(face3);
+
+  shared_ptr<InternalFace> face = getInternalFace();
+  FibManager& manager = getFibManager();
+  Fib& fib = getFib();
+
+  shared_ptr<fib::Entry> entry = fib.insert("/hello").first;
+
+  entry->addNextHop(face1, 101);
+  entry->addNextHop(face2, 202);
+  entry->addNextHop(face3, 303);
+
+  testRemoveNextHop(this, manager, fib, face, "/hello", 2);
+  BOOST_REQUIRE(removedNextHopWithCost(fib, "/hello", 3, 202));
+
+  testRemoveNextHop(this, manager, fib, face, "/hello", 3);
+  BOOST_REQUIRE(removedNextHopWithCost(fib, "/hello", 2, 303));
+
+  testRemoveNextHop(this, manager, fib, face, "/hello", 1);
+  // BOOST_REQUIRE(removedNextHopWithCost(fib, "/hello", 1, 101));
+
+  BOOST_CHECK(!static_cast<bool>(getFib().findExactMatch("/hello")));
+}
+
+BOOST_AUTO_TEST_CASE(RemoveFaceNotFound)
+{
+  shared_ptr<InternalFace> face = getInternalFace();
+
+  ControlParameters parameters;
+  parameters.setName("/hello");
+  parameters.setFaceId(1);
+
+  Block encodedParameters(parameters.wireEncode());
+
+  Name commandName("/localhost/nfd/fib");
+  commandName.append("remove-nexthop");
+  commandName.append(encodedParameters);
+
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+  generateCommand(*command);
+
+  face->onReceiveData += [this, command, encodedParameters] (const Data& response) {
+    this->validateControlResponse(response, command->getName(),
+                                  200, "Success", encodedParameters);
+  };
+
+  getFibManager().onFibRequest(*command);
+
+  BOOST_REQUIRE(didCallbackFire());
+}
+
+BOOST_AUTO_TEST_CASE(RemovePrefixNotFound)
+{
+  addFace(make_shared<DummyFace>());
+
+  shared_ptr<InternalFace> face = getInternalFace();
+
+  ControlParameters parameters;
+  parameters.setName("/hello");
+  parameters.setFaceId(1);
+
+  Block encodedParameters(parameters.wireEncode());
+
+  Name commandName("/localhost/nfd/fib");
+  commandName.append("remove-nexthop");
+  commandName.append(encodedParameters);
+
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+  generateCommand(*command);
+
+  face->onReceiveData += [this, command, encodedParameters] (const Data& response) {
+    this->validateControlResponse(response, command->getName(),
+                                  200, "Success", encodedParameters);
+  };
+
+  getFibManager().onFibRequest(*command);
+
+  BOOST_REQUIRE(didCallbackFire());
+}
+
+BOOST_AUTO_TEST_CASE(RemoveMissingPrefix)
+{
+  addFace(make_shared<DummyFace>());
+
+  shared_ptr<InternalFace> face = getInternalFace();
+
+  ControlParameters parameters;
+  parameters.setFaceId(1);
+
+  Block encodedParameters(parameters.wireEncode());
+
+  Name commandName("/localhost/nfd/fib");
+  commandName.append("remove-nexthop");
+  commandName.append(encodedParameters);
+
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+  generateCommand(*command);
+
+  face->onReceiveData += [this, command] (const Data& response) {
+    this->validateControlResponse(response, command->getName(), 400, "Malformed command");
+  };
+
+  getFibManager().onFibRequest(*command);
+
+  BOOST_REQUIRE(didCallbackFire());
+}
+
+BOOST_AUTO_TEST_CASE(RemoveImplicitFaceId)
+{
+  addFace(make_shared<DummyFace>());
+
+  shared_ptr<InternalFace> face = getInternalFace();
+
+  std::vector<ControlParameters> testedParameters;
+  testedParameters.push_back(ControlParameters().setName("/hello").setFaceId(0));
+  testedParameters.push_back(ControlParameters().setName("/hello"));
+
+  for (std::vector<ControlParameters>::iterator parameters = testedParameters.begin();
+       parameters != testedParameters.end(); ++parameters) {
+    Block encodedParameters(parameters->wireEncode());
+
+    Name commandName("/localhost/nfd/fib");
+    commandName.append("remove-nexthop");
+    commandName.append(encodedParameters);
+
+    shared_ptr<Interest> command(make_shared<Interest>(commandName));
+    command->setIncomingFaceId(1);
+    generateCommand(*command);
+
+    ControlParameters resultParameters;
+    resultParameters.setFaceId(1);
+    resultParameters.setName("/hello");
+
+    face->onReceiveData += [this, command, resultParameters] (const Data& response) {
+      this->validateControlResponse(response, command->getName(),
+                                    200, "Success", resultParameters.wireEncode());
+    };
+
+    getFibManager().onFibRequest(*command);
+
+    BOOST_REQUIRE(didCallbackFire());
+
+    face->onReceiveData.clear();
+  }
+}
+
+BOOST_FIXTURE_TEST_CASE(TestFibEnumerationRequest, FibManagerFixture)
+{
+  for (int i = 0; i < 87; i++)
+    {
+      Name prefix("/test");
+      prefix.appendSegment(i);
+
+      shared_ptr<DummyFace> dummy1(make_shared<DummyFace>());
+      shared_ptr<DummyFace> dummy2(make_shared<DummyFace>());
+
+      shared_ptr<fib::Entry> entry = m_fib.insert(prefix).first;
+      entry->addNextHop(dummy1, std::numeric_limits<uint64_t>::max() - 1);
+      entry->addNextHop(dummy2, std::numeric_limits<uint64_t>::max() - 2);
+
+      m_referenceEntries.insert(entry);
+    }
+  for (int i = 0; i < 2; i++)
+    {
+      Name prefix("/test2");
+      prefix.appendSegment(i);
+
+      shared_ptr<DummyFace> dummy1(make_shared<DummyFace>());
+      shared_ptr<DummyFace> dummy2(make_shared<DummyFace>());
+
+      shared_ptr<fib::Entry> entry = m_fib.insert(prefix).first;
+      entry->addNextHop(dummy1, std::numeric_limits<uint8_t>::max() - 1);
+      entry->addNextHop(dummy2, std::numeric_limits<uint8_t>::max() - 2);
+
+      m_referenceEntries.insert(entry);
+    }
+
+  ndn::EncodingBuffer buffer;
+
+  m_face->onReceiveData +=
+    bind(&FibEnumerationPublisherFixture::decodeFibEntryBlock, this, _1);
+
+  shared_ptr<Interest> command(make_shared<Interest>("/localhost/nfd/fib/list"));
+
+  m_manager.onFibRequest(*command);
+  BOOST_REQUIRE(m_finished);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/NFD/tests/daemon/mgmt/general-config-section.cpp b/NFD/tests/daemon/mgmt/general-config-section.cpp
new file mode 100644
index 0000000..29e5e7a
--- /dev/null
+++ b/NFD/tests/daemon/mgmt/general-config-section.cpp
@@ -0,0 +1,136 @@
+/* -*- 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/>.
+ */
+
+#include "mgmt/general-config-section.hpp"
+
+#include "tests/test-common.hpp"
+
+namespace nfd {
+namespace tests {
+
+BOOST_FIXTURE_TEST_SUITE(MgmtGeneralConfigSection, BaseFixture)
+
+BOOST_AUTO_TEST_CASE(UserAndGroupConfig)
+{
+  const std::string CONFIG =
+    "general\n"
+    "{\n"
+    "  user nobody\n"
+    "  group nogroup\n"
+    "}\n";
+
+  ConfigFile configFile;
+
+  general::setConfigFile(configFile);
+  BOOST_CHECK_NO_THROW(configFile.parse(CONFIG, true, "test-general-config-section"));
+
+}
+
+BOOST_AUTO_TEST_CASE(DefaultConfig)
+{
+  const std::string CONFIG =
+    "general\n"
+    "{\n"
+    "}\n";
+
+  ConfigFile configFile;
+
+  general::setConfigFile(configFile);
+  BOOST_CHECK_NO_THROW(configFile.parse(CONFIG, true, "test-general-config-section"));
+}
+
+BOOST_AUTO_TEST_CASE(NoUserConfig)
+{
+  const std::string CONFIG =
+    "general\n"
+    "{\n"
+    "  group nogroup\n"
+    "}\n";
+
+  ConfigFile configFile;
+
+  general::setConfigFile(configFile);
+  BOOST_CHECK_NO_THROW(configFile.parse(CONFIG, true, "test-general-config-section"));
+}
+
+BOOST_AUTO_TEST_CASE(NoGroupConfig)
+{
+  const std::string CONFIG =
+    "general\n"
+    "{\n"
+    "  user nobody\n"
+    "}\n";
+
+  ConfigFile configFile;
+
+  general::setConfigFile(configFile);
+  BOOST_CHECK_NO_THROW(configFile.parse(CONFIG, true, "test-general-config-section"));
+}
+
+static bool
+checkExceptionMessage(const ConfigFile::Error& error, const std::string& expected)
+{
+  return error.what() == expected;
+}
+
+BOOST_AUTO_TEST_CASE(InvalidUserConfig)
+{
+  const std::string CONFIG =
+    "general\n"
+    "{\n"
+    "  user\n"
+    "}\n";
+
+  ConfigFile configFile;
+  general::setConfigFile(configFile);
+
+  const std::string expected = "Invalid value for \"user\" in \"general\" section";
+  BOOST_REQUIRE_EXCEPTION(configFile.parse(CONFIG, true, "test-general-config-section"),
+                          ConfigFile::Error,
+                          bind(&checkExceptionMessage, _1, expected));
+}
+
+BOOST_AUTO_TEST_CASE(InvalidGroupConfig)
+{
+  const std::string CONFIG =
+    "general\n"
+    "{\n"
+    "  group\n"
+    "}\n";
+
+  ConfigFile configFile;
+  general::setConfigFile(configFile);
+
+  const std::string expected = "Invalid value for \"group\" in \"general\" section";
+  BOOST_REQUIRE_EXCEPTION(configFile.parse(CONFIG, true, "test-general-config-section"),
+                          ConfigFile::Error,
+                          bind(&checkExceptionMessage, _1, expected));
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+
+} // namespace nfd
diff --git a/NFD/tests/daemon/mgmt/internal-face.cpp b/NFD/tests/daemon/mgmt/internal-face.cpp
new file mode 100644
index 0000000..2215c0e
--- /dev/null
+++ b/NFD/tests/daemon/mgmt/internal-face.cpp
@@ -0,0 +1,240 @@
+/* -*- 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/>.
+ */
+
+#include "mgmt/internal-face.hpp"
+#include "tests/daemon/face/dummy-face.hpp"
+
+#include "tests/test-common.hpp"
+
+namespace nfd {
+namespace tests {
+
+NFD_LOG_INIT("InternalFaceTest");
+
+class InternalFaceFixture : protected BaseFixture
+{
+public:
+
+  InternalFaceFixture()
+    : m_onInterestFired(false),
+      m_noOnInterestFired(false)
+  {
+
+  }
+
+  void
+  validateOnInterestCallback(const Name& name, const Interest& interest)
+  {
+    m_onInterestFired = true;
+  }
+
+  void
+  validateNoOnInterestCallback(const Name& name, const Interest& interest)
+  {
+    m_noOnInterestFired = true;
+  }
+
+  void
+  addFace(shared_ptr<Face> face)
+  {
+    m_faces.push_back(face);
+  }
+
+  bool
+  didOnInterestFire()
+  {
+    return m_onInterestFired;
+  }
+
+  bool
+  didNoOnInterestFire()
+  {
+    return m_noOnInterestFired;
+  }
+
+  void
+  resetOnInterestFired()
+  {
+    m_onInterestFired = false;
+  }
+
+  void
+  resetNoOnInterestFired()
+  {
+    m_noOnInterestFired = false;
+  }
+
+protected:
+  ndn::KeyChain m_keyChain;
+
+private:
+  std::vector<shared_ptr<Face> > m_faces;
+  bool m_onInterestFired;
+  bool m_noOnInterestFired;
+};
+
+BOOST_FIXTURE_TEST_SUITE(MgmtInternalFace, InternalFaceFixture)
+
+void
+validatePutData(bool& called, const Name& expectedName, const Data& data)
+{
+  called = true;
+  BOOST_CHECK_EQUAL(expectedName, data.getName());
+}
+
+BOOST_AUTO_TEST_CASE(PutData)
+{
+  addFace(make_shared<DummyFace>());
+
+  shared_ptr<InternalFace> face(new InternalFace);
+
+  bool didPutData = false;
+  Name dataName("/hello");
+  face->onReceiveData += bind(&validatePutData, ref(didPutData), dataName, _1);
+
+  Data testData(dataName);
+  m_keyChain.sign(testData);
+  face->put(testData);
+
+  BOOST_REQUIRE(didPutData);
+
+  BOOST_CHECK_THROW(face->close(), InternalFace::Error);
+}
+
+BOOST_AUTO_TEST_CASE(SendInterestHitEnd)
+{
+  addFace(make_shared<DummyFace>());
+
+  shared_ptr<InternalFace> face(new InternalFace);
+
+  face->setInterestFilter("/localhost/nfd/fib",
+                          bind(&InternalFaceFixture::validateOnInterestCallback,
+                               this, _1, _2));
+
+  // generate command whose name is canonically
+  // ordered after /localhost/nfd/fib so that
+  // we hit the end of the std::map
+
+  Name commandName("/localhost/nfd/fib/end");
+  shared_ptr<Interest> command = makeInterest(commandName);
+  face->sendInterest(*command);
+  g_io.run_one();
+
+  BOOST_REQUIRE(didOnInterestFire());
+  BOOST_REQUIRE(didNoOnInterestFire() == false);
+}
+
+BOOST_AUTO_TEST_CASE(SendInterestHitBegin)
+{
+  addFace(make_shared<DummyFace>());
+
+  shared_ptr<InternalFace> face(new InternalFace);
+
+  face->setInterestFilter("/localhost/nfd/fib",
+                          bind(&InternalFaceFixture::validateNoOnInterestCallback,
+                               this, _1, _2));
+
+  // generate command whose name is canonically
+  // ordered before /localhost/nfd/fib so that
+  // we hit the beginning of the std::map
+
+  Name commandName("/localhost/nfd");
+  shared_ptr<Interest> command = makeInterest(commandName);
+  face->sendInterest(*command);
+  g_io.run_one();
+
+  BOOST_REQUIRE(didNoOnInterestFire() == false);
+}
+
+BOOST_AUTO_TEST_CASE(SendInterestHitExact)
+{
+  addFace(make_shared<DummyFace>());
+
+  shared_ptr<InternalFace> face(new InternalFace);
+
+  face->setInterestFilter("/localhost/nfd/eib",
+                          bind(&InternalFaceFixture::validateNoOnInterestCallback,
+                               this, _1, _2));
+
+  face->setInterestFilter("/localhost/nfd/fib",
+                          bind(&InternalFaceFixture::validateOnInterestCallback,
+                           this, _1, _2));
+
+  face->setInterestFilter("/localhost/nfd/gib",
+                          bind(&InternalFaceFixture::validateNoOnInterestCallback,
+                               this, _1, _2));
+
+  // generate command whose name exactly matches
+  // /localhost/nfd/fib
+
+  Name commandName("/localhost/nfd/fib");
+  shared_ptr<Interest> command = makeInterest(commandName);
+  face->sendInterest(*command);
+  g_io.run_one();
+
+  BOOST_REQUIRE(didOnInterestFire());
+  BOOST_REQUIRE(didNoOnInterestFire() == false);
+}
+
+BOOST_AUTO_TEST_CASE(SendInterestHitPrevious)
+{
+  addFace(make_shared<DummyFace>());
+
+  shared_ptr<InternalFace> face(new InternalFace);
+
+  face->setInterestFilter("/localhost/nfd/fib",
+                          bind(&InternalFaceFixture::validateOnInterestCallback,
+                               this, _1, _2));
+
+  face->setInterestFilter("/localhost/nfd/fib/zzzzzzzzzzzzz/",
+                          bind(&InternalFaceFixture::validateNoOnInterestCallback,
+                               this, _1, _2));
+
+  // generate command whose name exactly matches
+  // an Interest filter
+
+  Name commandName("/localhost/nfd/fib/previous");
+  shared_ptr<Interest> command = makeInterest(commandName);
+  face->sendInterest(*command);
+  g_io.run_one();
+
+  BOOST_REQUIRE(didOnInterestFire());
+  BOOST_REQUIRE(didNoOnInterestFire() == false);
+}
+
+BOOST_AUTO_TEST_CASE(InterestGone)
+{
+  shared_ptr<InternalFace> face = make_shared<InternalFace>();
+  shared_ptr<Interest> interest = makeInterest("ndn:/localhost/nfd/gone");
+  face->sendInterest(*interest);
+
+  interest.reset();
+  BOOST_CHECK_NO_THROW(g_io.poll());
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/NFD/tests/daemon/mgmt/malformed.ndncert b/NFD/tests/daemon/mgmt/malformed.ndncert
new file mode 100644
index 0000000..38b2fbb
--- /dev/null
+++ b/NFD/tests/daemon/mgmt/malformed.ndncert
@@ -0,0 +1 @@
+definitely not a key
\ No newline at end of file
diff --git a/NFD/tests/daemon/mgmt/manager-base.cpp b/NFD/tests/daemon/mgmt/manager-base.cpp
new file mode 100644
index 0000000..0aa7077
--- /dev/null
+++ b/NFD/tests/daemon/mgmt/manager-base.cpp
@@ -0,0 +1,203 @@
+/* -*- 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/>.
+ */
+
+#include "mgmt/manager-base.hpp"
+#include "mgmt/internal-face.hpp"
+
+#include "tests/test-common.hpp"
+
+namespace nfd {
+namespace tests {
+
+NFD_LOG_INIT("ManagerBaseTest");
+
+class ManagerBaseTest : public ManagerBase, protected BaseFixture
+{
+
+public:
+
+  ManagerBaseTest()
+    : ManagerBase(make_shared<InternalFace>(), "TEST-PRIVILEGE", m_keyChain),
+      m_callbackFired(false)
+  {
+
+  }
+
+  void
+  testSetResponse(ControlResponse& response,
+                  uint32_t code,
+                  const std::string& text)
+  {
+    setResponse(response, code, text);
+  }
+
+  void
+  testSendResponse(const Name& name,
+                   uint32_t code,
+                   const std::string& text,
+                   const Block& body)
+  {
+    sendResponse(name, code, text, body);
+  }
+
+  void
+  testSendResponse(const Name& name,
+                   uint32_t code,
+                   const std::string& text)
+  {
+    sendResponse(name, code, text);
+  }
+
+  void
+  testSendResponse(const Name& name,
+                   const ControlResponse& response)
+  {
+    sendResponse(name, response);
+  }
+
+  shared_ptr<InternalFace>
+  getInternalFace()
+  {
+    return static_pointer_cast<InternalFace>(m_face);
+  }
+
+  void
+  validateControlResponse(const Data& response,
+                          const Name& expectedName,
+                          uint32_t expectedCode,
+                          const std::string& expectedText)
+  {
+    m_callbackFired = true;
+    Block controlRaw = response.getContent().blockFromValue();
+
+    ControlResponse control;
+    control.wireDecode(controlRaw);
+
+    NFD_LOG_DEBUG("received control response"
+                  << " name: " << response.getName()
+                  << " code: " << control.getCode()
+                  << " text: " << control.getText());
+
+    BOOST_REQUIRE(response.getName() == expectedName);
+    BOOST_REQUIRE(control.getCode() == expectedCode);
+    BOOST_REQUIRE(control.getText() == expectedText);
+  }
+
+  void
+  validateControlResponse(const Data& response,
+                          const Name& expectedName,
+                          uint32_t expectedCode,
+                          const std::string& expectedText,
+                          const Block& expectedBody)
+  {
+    m_callbackFired = true;
+    Block controlRaw = response.getContent().blockFromValue();
+
+    ControlResponse control;
+    control.wireDecode(controlRaw);
+
+    NFD_LOG_DEBUG("received control response"
+                  << " name: " << response.getName()
+                  << " code: " << control.getCode()
+                  << " text: " << control.getText());
+
+    BOOST_REQUIRE(response.getName() == expectedName);
+    BOOST_REQUIRE(control.getCode() == expectedCode);
+    BOOST_REQUIRE(control.getText() == expectedText);
+
+    BOOST_REQUIRE(control.getBody().value_size() == expectedBody.value_size());
+
+    BOOST_CHECK(memcmp(control.getBody().value(), expectedBody.value(),
+                       expectedBody.value_size()) == 0);
+  }
+
+  bool
+  didCallbackFire()
+  {
+    return m_callbackFired;
+  }
+
+private:
+
+  bool m_callbackFired;
+  ndn::KeyChain m_keyChain;
+
+};
+
+BOOST_FIXTURE_TEST_SUITE(MgmtManagerBase, ManagerBaseTest)
+
+BOOST_AUTO_TEST_CASE(SetResponse)
+{
+  ControlResponse response(200, "OK");
+
+  BOOST_CHECK_EQUAL(response.getCode(), 200);
+  BOOST_CHECK_EQUAL(response.getText(), "OK");
+
+  testSetResponse(response, 100, "test");
+
+  BOOST_CHECK_EQUAL(response.getCode(), 100);
+  BOOST_CHECK_EQUAL(response.getText(), "test");
+}
+
+BOOST_AUTO_TEST_CASE(SendResponse4Arg)
+{
+  ndn::nfd::ControlParameters parameters;
+  parameters.setName("/test/body");
+
+  getInternalFace()->onReceiveData += [this, parameters] (const Data& response) {
+    this->validateControlResponse(response, "/response", 100, "test", parameters.wireEncode());
+  };
+
+  testSendResponse("/response", 100, "test", parameters.wireEncode());
+  BOOST_REQUIRE(didCallbackFire());
+}
+
+
+BOOST_AUTO_TEST_CASE(SendResponse3Arg)
+{
+  getInternalFace()->onReceiveData += [this] (const Data& response) {
+    this->validateControlResponse(response, "/response", 100, "test");
+  };
+
+  testSendResponse("/response", 100, "test");
+  BOOST_REQUIRE(didCallbackFire());
+}
+
+BOOST_AUTO_TEST_CASE(SendResponse2Arg)
+{
+  getInternalFace()->onReceiveData += [this] (const Data& response) {
+    this->validateControlResponse(response, "/response", 100, "test");
+  };
+
+  ControlResponse response(100, "test");
+
+  testSendResponse("/response", 100, "test");
+  BOOST_REQUIRE(didCallbackFire());
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/NFD/tests/daemon/mgmt/status-server.cpp b/NFD/tests/daemon/mgmt/status-server.cpp
new file mode 100644
index 0000000..bda69a4
--- /dev/null
+++ b/NFD/tests/daemon/mgmt/status-server.cpp
@@ -0,0 +1,105 @@
+/* -*- 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/>.
+ */
+
+#include "mgmt/status-server.hpp"
+#include "fw/forwarder.hpp"
+#include "version.hpp"
+#include "mgmt/internal-face.hpp"
+
+#include "tests/test-common.hpp"
+#include "tests/daemon/face/dummy-face.hpp"
+
+namespace nfd {
+namespace tests {
+
+BOOST_FIXTURE_TEST_SUITE(MgmtStatusServer, BaseFixture)
+
+shared_ptr<const Data> g_response;
+
+void
+interceptResponse(const Data& data)
+{
+  g_response = data.shared_from_this();
+}
+
+BOOST_AUTO_TEST_CASE(Status)
+{
+  // initialize
+  time::system_clock::TimePoint t1 = time::system_clock::now();
+  Forwarder forwarder;
+  shared_ptr<InternalFace> internalFace = make_shared<InternalFace>();
+  internalFace->onReceiveData += &interceptResponse;
+  ndn::KeyChain keyChain;
+  StatusServer statusServer(internalFace, ref(forwarder), keyChain);
+  time::system_clock::TimePoint t2 = time::system_clock::now();
+
+  // populate tables
+  forwarder.getFib().insert("ndn:/fib1");
+  forwarder.getPit().insert(*makeInterest("ndn:/pit1"));
+  forwarder.getPit().insert(*makeInterest("ndn:/pit2"));
+  forwarder.getPit().insert(*makeInterest("ndn:/pit3"));
+  forwarder.getPit().insert(*makeInterest("ndn:/pit4"));
+  forwarder.getMeasurements().get("ndn:/measurements1");
+  forwarder.getMeasurements().get("ndn:/measurements2");
+  forwarder.getMeasurements().get("ndn:/measurements3");
+  BOOST_CHECK_GE(forwarder.getFib().size(), 1);
+  BOOST_CHECK_GE(forwarder.getPit().size(), 4);
+  BOOST_CHECK_GE(forwarder.getMeasurements().size(), 3);
+
+  // request
+  shared_ptr<Interest> request = makeInterest("ndn:/localhost/nfd/status");
+  request->setMustBeFresh(true);
+  request->setChildSelector(1);
+
+  g_response.reset();
+  time::system_clock::TimePoint t3 = time::system_clock::now();
+  internalFace->sendInterest(*request);
+  g_io.run_one();
+  time::system_clock::TimePoint t4 = time::system_clock::now();
+  BOOST_REQUIRE(static_cast<bool>(g_response));
+
+  // verify
+  ndn::nfd::ForwarderStatus status;
+  BOOST_REQUIRE_NO_THROW(status.wireDecode(g_response->getContent()));
+
+  BOOST_CHECK_EQUAL(status.getNfdVersion(), NFD_VERSION_BUILD_STRING);
+  BOOST_CHECK_GE(time::toUnixTimestamp(status.getStartTimestamp()), time::toUnixTimestamp(t1));
+  BOOST_CHECK_LE(time::toUnixTimestamp(status.getStartTimestamp()), time::toUnixTimestamp(t2));
+  BOOST_CHECK_GE(time::toUnixTimestamp(status.getCurrentTimestamp()), time::toUnixTimestamp(t3));
+  BOOST_CHECK_LE(time::toUnixTimestamp(status.getCurrentTimestamp()), time::toUnixTimestamp(t4));
+
+  // StatusServer under test isn't added to Forwarder,
+  // so request and response won't affect table size
+  BOOST_CHECK_EQUAL(status.getNNameTreeEntries(), forwarder.getNameTree().size());
+  BOOST_CHECK_EQUAL(status.getNFibEntries(), forwarder.getFib().size());
+  BOOST_CHECK_EQUAL(status.getNPitEntries(), forwarder.getPit().size());
+  BOOST_CHECK_EQUAL(status.getNMeasurementsEntries(), forwarder.getMeasurements().size());
+  BOOST_CHECK_EQUAL(status.getNCsEntries(), forwarder.getCs().size());
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/NFD/tests/daemon/mgmt/strategy-choice-manager.cpp b/NFD/tests/daemon/mgmt/strategy-choice-manager.cpp
new file mode 100644
index 0000000..570c1d8
--- /dev/null
+++ b/NFD/tests/daemon/mgmt/strategy-choice-manager.cpp
@@ -0,0 +1,646 @@
+/* -*- 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/>.
+ */
+
+#include "mgmt/strategy-choice-manager.hpp"
+#include "face/face.hpp"
+#include "mgmt/internal-face.hpp"
+#include "table/name-tree.hpp"
+#include "table/strategy-choice.hpp"
+#include "fw/forwarder.hpp"
+#include "fw/strategy.hpp"
+#include "tests/daemon/face/dummy-face.hpp"
+#include "tests/daemon/fw/dummy-strategy.hpp"
+
+#include <ndn-cxx/management/nfd-strategy-choice.hpp>
+
+#include "tests/test-common.hpp"
+#include "validation-common.hpp"
+
+namespace nfd {
+namespace tests {
+
+NFD_LOG_INIT("MgmtStrategyChoiceManager");
+
+class StrategyChoiceManagerFixture : protected BaseFixture
+{
+public:
+
+  StrategyChoiceManagerFixture()
+    : m_strategyChoice(m_forwarder.getStrategyChoice())
+    , m_face(make_shared<InternalFace>())
+    , m_manager(m_strategyChoice, m_face, m_keyChain)
+    , m_callbackFired(false)
+  {
+    m_strategyChoice.install(make_shared<DummyStrategy>(ref(m_forwarder),
+                                                        "/localhost/nfd/strategy/test-strategy-a"));
+    m_strategyChoice.insert("ndn:/", "/localhost/nfd/strategy/test-strategy-a");
+  }
+
+  virtual
+  ~StrategyChoiceManagerFixture()
+  {
+
+  }
+
+  void
+  validateControlResponseCommon(const Data& response,
+                                const Name& expectedName,
+                                uint32_t expectedCode,
+                                const std::string& expectedText,
+                                ControlResponse& control)
+  {
+    m_callbackFired = true;
+    Block controlRaw = response.getContent().blockFromValue();
+
+    control.wireDecode(controlRaw);
+
+    NFD_LOG_DEBUG("received control response"
+                  << " Name: " << response.getName()
+                  << " code: " << control.getCode()
+                  << " text: " << control.getText());
+
+    BOOST_CHECK_EQUAL(response.getName(), expectedName);
+    BOOST_CHECK_EQUAL(control.getCode(), expectedCode);
+    BOOST_CHECK_EQUAL(control.getText(), expectedText);
+  }
+
+  void
+  validateControlResponse(const Data& response,
+                          const Name& expectedName,
+                          uint32_t expectedCode,
+                          const std::string& expectedText)
+  {
+    ControlResponse control;
+    validateControlResponseCommon(response, expectedName,
+                                  expectedCode, expectedText, control);
+
+    if (!control.getBody().empty())
+      {
+        BOOST_FAIL("found unexpected control response body");
+      }
+  }
+
+  void
+  validateControlResponse(const Data& response,
+                          const Name& expectedName,
+                          uint32_t expectedCode,
+                          const std::string& expectedText,
+                          const Block& expectedBody)
+  {
+    ControlResponse control;
+    validateControlResponseCommon(response, expectedName,
+                                  expectedCode, expectedText, control);
+
+    BOOST_REQUIRE(!control.getBody().empty());
+    BOOST_REQUIRE(control.getBody().value_size() == expectedBody.value_size());
+
+    BOOST_CHECK(memcmp(control.getBody().value(), expectedBody.value(),
+                       expectedBody.value_size()) == 0);
+
+  }
+
+  void
+  validateList(const Data& data, const ndn::nfd::StrategyChoice& expectedChoice)
+  {
+    m_callbackFired = true;
+    ndn::nfd::StrategyChoice choice(data.getContent().blockFromValue());
+    BOOST_CHECK_EQUAL(choice.getStrategy(), expectedChoice.getStrategy());
+    BOOST_CHECK_EQUAL(choice.getName(), expectedChoice.getName());
+  }
+
+  bool
+  didCallbackFire()
+  {
+    return m_callbackFired;
+  }
+
+  void
+  resetCallbackFired()
+  {
+    m_callbackFired = false;
+  }
+
+  shared_ptr<InternalFace>&
+  getFace()
+  {
+    return m_face;
+  }
+
+  StrategyChoiceManager&
+  getManager()
+  {
+    return m_manager;
+  }
+
+  StrategyChoice&
+  getStrategyChoice()
+  {
+    return m_strategyChoice;
+  }
+
+  void
+  addInterestRule(const std::string& regex,
+                  ndn::IdentityCertificate& certificate)
+  {
+    m_manager.addInterestRule(regex, certificate);
+  }
+
+protected:
+  Forwarder m_forwarder;
+  StrategyChoice& m_strategyChoice;
+  shared_ptr<InternalFace> m_face;
+  StrategyChoiceManager m_manager;
+  ndn::KeyChain m_keyChain;
+
+private:
+  bool m_callbackFired;
+};
+
+class AllStrategiesFixture : public StrategyChoiceManagerFixture
+{
+public:
+  AllStrategiesFixture()
+  {
+    m_strategyChoice.install(make_shared<DummyStrategy>(ref(m_forwarder),
+                                                        "/localhost/nfd/strategy/test-strategy-b"));
+
+    const Name strategyCVersion1("/localhost/nfd/strategy/test-strategy-c/%FD%01");
+    m_strategyChoice.install(make_shared<DummyStrategy>(ref(m_forwarder),
+                                                        strategyCVersion1));
+
+    const Name strategyCVersion2("/localhost/nfd/strategy/test-strategy-c/%FD%02");
+    m_strategyChoice.install(make_shared<DummyStrategy>(ref(m_forwarder),
+                                                        strategyCVersion2));
+  }
+
+  virtual
+  ~AllStrategiesFixture()
+  {
+
+  }
+};
+
+template <typename T> class AuthorizedCommandFixture : public CommandFixture<T>
+{
+public:
+  AuthorizedCommandFixture()
+  {
+    const std::string regex = "^<localhost><nfd><strategy-choice>";
+    T::addInterestRule(regex, *CommandFixture<T>::m_certificate);
+  }
+
+  virtual
+  ~AuthorizedCommandFixture()
+  {
+
+  }
+};
+
+BOOST_FIXTURE_TEST_SUITE(MgmtStrategyChoiceManager,
+                         AuthorizedCommandFixture<AllStrategiesFixture>)
+
+BOOST_FIXTURE_TEST_CASE(ShortName, AllStrategiesFixture)
+{
+  shared_ptr<Interest> command(make_shared<Interest>("/localhost/nfd/strategy-choice"));
+
+  getFace()->onReceiveData += [this, command] (const Data& response) {
+    this->validateControlResponse(response, command->getName(), 400, "Malformed command");
+  };
+
+  getFace()->sendInterest(*command);
+  g_io.run_one();
+
+  BOOST_REQUIRE(didCallbackFire());
+}
+
+BOOST_FIXTURE_TEST_CASE(MalformedCommmand, AllStrategiesFixture)
+{
+  shared_ptr<Interest> command(make_shared<Interest>("/localhost/nfd/strategy-choice"));
+
+  getFace()->onReceiveData += [this, command] (const Data& response) {
+    this->validateControlResponse(response, command->getName(), 400, "Malformed command");
+  };
+
+  getManager().onStrategyChoiceRequest(*command);
+
+  BOOST_REQUIRE(didCallbackFire());
+}
+
+BOOST_FIXTURE_TEST_CASE(UnsignedCommand, AllStrategiesFixture)
+{
+  ControlParameters parameters;
+  parameters.setName("/test");
+  parameters.setStrategy("/localhost/nfd/strategy/best-route");
+
+  Block encodedParameters(parameters.wireEncode());
+
+  Name commandName("/localhost/nfd/strategy-choice");
+  commandName.append("set");
+  commandName.append(encodedParameters);
+
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+
+  getFace()->onReceiveData += [this, command] (const Data& response) {
+    this->validateControlResponse(response, command->getName(), 401, "Signature required");
+  };
+
+  getManager().onStrategyChoiceRequest(*command);
+
+  BOOST_REQUIRE(didCallbackFire());
+}
+
+BOOST_FIXTURE_TEST_CASE(UnauthorizedCommand,
+                        UnauthorizedCommandFixture<StrategyChoiceManagerFixture>)
+{
+  ControlParameters parameters;
+  parameters.setName("/test");
+  parameters.setStrategy("/localhost/nfd/strategy/best-route");
+
+  Block encodedParameters(parameters.wireEncode());
+
+  Name commandName("/localhost/nfd/strategy-choice");
+  commandName.append("set");
+  commandName.append(encodedParameters);
+
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+  generateCommand(*command);
+
+  getFace()->onReceiveData += [this, command] (const Data& response) {
+    this->validateControlResponse(response, command->getName(), 403, "Unauthorized command");
+  };
+
+  getManager().onStrategyChoiceRequest(*command);
+
+  BOOST_REQUIRE(didCallbackFire());
+}
+
+BOOST_AUTO_TEST_CASE(UnsupportedVerb)
+{
+  ControlParameters parameters;
+  parameters.setName("/test");
+  parameters.setStrategy("/localhost/nfd/strategy/test-strategy-b");
+
+  Block encodedParameters(parameters.wireEncode());
+
+  Name commandName("/localhost/nfd/strategy-choice");
+  commandName.append("unsupported");
+  commandName.append(encodedParameters);
+
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+  generateCommand(*command);
+
+  getFace()->onReceiveData += [this, command] (const Data& response) {
+    this->validateControlResponse(response, command->getName(), 501, "Unsupported command");
+  };
+
+  getManager().onValidatedStrategyChoiceRequest(command);
+
+  BOOST_REQUIRE(didCallbackFire());
+}
+
+BOOST_AUTO_TEST_CASE(BadOptionParse)
+{
+  Name commandName("/localhost/nfd/strategy-choice");
+  commandName.append("set");
+  commandName.append("NotReallyParameters");
+
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+  generateCommand(*command);
+
+  getFace()->onReceiveData += [this, command] (const Data& response) {
+    this->validateControlResponse(response, command->getName(), 400, "Malformed command");
+  };
+
+  getManager().onValidatedStrategyChoiceRequest(command);
+
+  BOOST_REQUIRE(didCallbackFire());
+}
+
+BOOST_AUTO_TEST_CASE(SetStrategies)
+{
+  ControlParameters parameters;
+  parameters.setName("/test");
+  parameters.setStrategy("/localhost/nfd/strategy/test-strategy-b");
+
+  Block encodedParameters(parameters.wireEncode());
+
+  Name commandName("/localhost/nfd/strategy-choice");
+  commandName.append("set");
+  commandName.append(encodedParameters);
+
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+
+  getFace()->onReceiveData += [this, command, encodedParameters] (const Data& response) {
+    this->validateControlResponse(response, command->getName(),
+                                  200, "Success", encodedParameters);
+  };
+
+  getManager().onValidatedStrategyChoiceRequest(command);
+
+  BOOST_REQUIRE(didCallbackFire());
+  fw::Strategy& strategy = getStrategyChoice().findEffectiveStrategy("/test");
+  BOOST_REQUIRE_EQUAL(strategy.getName(), "/localhost/nfd/strategy/test-strategy-b");
+}
+
+BOOST_AUTO_TEST_CASE(SetStrategySpecifiedVersion)
+{
+  ControlParameters parameters;
+  parameters.setName("/test");
+  parameters.setStrategy("/localhost/nfd/strategy/test-strategy-c/%FD%01");
+
+  Block encodedParameters(parameters.wireEncode());
+
+  Name commandName("/localhost/nfd/strategy-choice");
+  commandName.append("set");
+  commandName.append(encodedParameters);
+
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+
+  getFace()->onReceiveData += [this, command, encodedParameters] (const Data& response) {
+    this->validateControlResponse(response, command->getName(),
+                                  200, "Success", encodedParameters);
+  };
+
+  getManager().onValidatedStrategyChoiceRequest(command);
+
+  BOOST_REQUIRE(didCallbackFire());
+  fw::Strategy& strategy = getStrategyChoice().findEffectiveStrategy("/test");
+  BOOST_REQUIRE_EQUAL(strategy.getName(), "/localhost/nfd/strategy/test-strategy-c/%FD%01");
+}
+
+BOOST_AUTO_TEST_CASE(SetStrategyLatestVersion)
+{
+  ControlParameters parameters;
+  parameters.setName("/test");
+  parameters.setStrategy("/localhost/nfd/strategy/test-strategy-c");
+
+  Block encodedParameters(parameters.wireEncode());
+
+  Name commandName("/localhost/nfd/strategy-choice");
+  commandName.append("set");
+  commandName.append(encodedParameters);
+
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+
+  ControlParameters responseParameters;
+  responseParameters.setName("/test");
+  responseParameters.setStrategy("/localhost/nfd/strategy/test-strategy-c/%FD%02");
+
+  getFace()->onReceiveData += [this, command, responseParameters] (const Data& response) {
+    this->validateControlResponse(response, command->getName(),
+                                  200, "Success", responseParameters.wireEncode());
+  };
+
+  getManager().onValidatedStrategyChoiceRequest(command);
+
+  BOOST_REQUIRE(didCallbackFire());
+  fw::Strategy& strategy = getStrategyChoice().findEffectiveStrategy("/test");
+  BOOST_REQUIRE_EQUAL(strategy.getName(), "/localhost/nfd/strategy/test-strategy-c/%FD%02");
+}
+
+BOOST_AUTO_TEST_CASE(SetStrategiesMissingName)
+{
+  ControlParameters parameters;
+  parameters.setStrategy("/localhost/nfd/strategy/test-strategy-b");
+
+  Block encodedParameters(parameters.wireEncode());
+
+  Name commandName("/localhost/nfd/strategy-choice");
+  commandName.append("set");
+  commandName.append(encodedParameters);
+
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+
+  getFace()->onReceiveData += [this, command] (const Data& response) {
+    this->validateControlResponse(response, command->getName(), 400, "Malformed command");
+  };
+
+  getManager().onValidatedStrategyChoiceRequest(command);
+
+  BOOST_REQUIRE(didCallbackFire());
+}
+
+BOOST_AUTO_TEST_CASE(SetStrategiesMissingStrategy)
+{
+  ControlParameters parameters;
+  parameters.setName("/test");
+
+  Block encodedParameters(parameters.wireEncode());
+
+  Name commandName("/localhost/nfd/strategy-choice");
+  commandName.append("set");
+  commandName.append(encodedParameters);
+
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+
+  getFace()->onReceiveData += [this, command] (const Data& response) {
+    this->validateControlResponse(response, command->getName(), 400, "Malformed command");
+  };
+
+  getManager().onValidatedStrategyChoiceRequest(command);
+
+  BOOST_REQUIRE(didCallbackFire());
+  fw::Strategy& strategy = getStrategyChoice().findEffectiveStrategy("/test");
+  BOOST_REQUIRE_EQUAL(strategy.getName(), "/localhost/nfd/strategy/test-strategy-a");
+}
+
+BOOST_AUTO_TEST_CASE(SetUnsupportedStrategy)
+{
+  ControlParameters parameters;
+  parameters.setName("/test");
+  parameters.setStrategy("/localhost/nfd/strategy/unit-test-doesnotexist");
+
+  Block encodedParameters(parameters.wireEncode());
+
+  Name commandName("/localhost/nfd/strategy-choice");
+  commandName.append("set");
+  commandName.append(encodedParameters);
+
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+  generateCommand(*command);
+
+  getFace()->onReceiveData += [this, command] (const Data& response) {
+    this->validateControlResponse(response, command->getName(), 504, "Unsupported strategy");
+  };
+
+  getManager().onValidatedStrategyChoiceRequest(command);
+
+  BOOST_REQUIRE(didCallbackFire());
+  fw::Strategy& strategy = getStrategyChoice().findEffectiveStrategy("/test");
+  BOOST_CHECK_EQUAL(strategy.getName(), "/localhost/nfd/strategy/test-strategy-a");
+}
+
+class DefaultStrategyOnlyFixture : public StrategyChoiceManagerFixture
+{
+public:
+  DefaultStrategyOnlyFixture()
+    : StrategyChoiceManagerFixture()
+  {
+
+  }
+
+  virtual
+  ~DefaultStrategyOnlyFixture()
+  {
+
+  }
+};
+
+
+/// \todo I'm not sure this code branch (code 405) can happen. The manager tests for the strategy first and will return 504.
+// BOOST_FIXTURE_TEST_CASE(SetNotInstalled, DefaultStrategyOnlyFixture)
+// {
+//   BOOST_REQUIRE(!getStrategyChoice().hasStrategy("/localhost/nfd/strategy/test-strategy-b"));
+//   ControlParameters parameters;
+//   parameters.setName("/test");
+//   parameters.setStrategy("/localhost/nfd/strategy/test-strategy-b");
+
+//   Block encodedParameters(parameters.wireEncode());
+
+//   Name commandName("/localhost/nfd/strategy-choice");
+//   commandName.append("set");
+//   commandName.append(encodedParameters);
+
+//   shared_ptr<Interest> command(make_shared<Interest>(commandName));
+//   generateCommand(*command);
+
+//   getFace()->onReceiveData +=
+//     bind(&StrategyChoiceManagerFixture::validateControlResponse, this, _1,
+//          command->getName(), 405, "Strategy not installed");
+
+//   getManager().onValidatedStrategyChoiceRequest(command);
+
+//   BOOST_REQUIRE(didCallbackFire());
+//   fw::Strategy& strategy = getStrategyChoice().findEffectiveStrategy("/test");
+//   BOOST_CHECK_EQUAL(strategy.getName(), "/localhost/nfd/strategy/test-strategy-a");
+// }
+
+BOOST_AUTO_TEST_CASE(Unset)
+{
+  ControlParameters parameters;
+  parameters.setName("/test");
+
+  BOOST_REQUIRE(m_strategyChoice.insert("/test", "/localhost/nfd/strategy/test-strategy-b"));
+  BOOST_REQUIRE_EQUAL(m_strategyChoice.findEffectiveStrategy("/test").getName(),
+                      "/localhost/nfd/strategy/test-strategy-b");
+
+  Block encodedParameters(parameters.wireEncode());
+
+  Name commandName("/localhost/nfd/strategy-choice");
+  commandName.append("unset");
+  commandName.append(encodedParameters);
+
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+  generateCommand(*command);
+
+  getFace()->onReceiveData += [this, command, encodedParameters] (const Data& response) {
+    this->validateControlResponse(response, command->getName(),
+                                  200, "Success", encodedParameters);
+  };
+
+  getManager().onValidatedStrategyChoiceRequest(command);
+
+  BOOST_REQUIRE(didCallbackFire());
+
+  BOOST_CHECK_EQUAL(m_strategyChoice.findEffectiveStrategy("/test").getName(),
+                    "/localhost/nfd/strategy/test-strategy-a");
+}
+
+BOOST_AUTO_TEST_CASE(UnsetRoot)
+{
+  ControlParameters parameters;
+  parameters.setName("/");
+
+  Block encodedParameters(parameters.wireEncode());
+
+  Name commandName("/localhost/nfd/strategy-choice");
+  commandName.append("unset");
+  commandName.append(encodedParameters);
+
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+  generateCommand(*command);
+
+  getFace()->onReceiveData += [this, command] (const Data& response) {
+    this->validateControlResponse(response, command->getName(),
+                                  403, "Cannot unset root prefix strategy");
+  };
+
+  getManager().onValidatedStrategyChoiceRequest(command);
+
+  BOOST_REQUIRE(didCallbackFire());
+
+  BOOST_CHECK_EQUAL(m_strategyChoice.findEffectiveStrategy("/test").getName(),
+                    "/localhost/nfd/strategy/test-strategy-a");
+}
+
+BOOST_AUTO_TEST_CASE(UnsetMissingName)
+{
+  ControlParameters parameters;
+
+  BOOST_REQUIRE(m_strategyChoice.insert("/test", "/localhost/nfd/strategy/test-strategy-b"));
+  BOOST_REQUIRE_EQUAL(m_strategyChoice.findEffectiveStrategy("/test").getName(),
+                      "/localhost/nfd/strategy/test-strategy-b");
+
+  Block encodedParameters(parameters.wireEncode());
+
+  Name commandName("/localhost/nfd/strategy-choice");
+  commandName.append("unset");
+  commandName.append(encodedParameters);
+
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+  generateCommand(*command);
+
+  getFace()->onReceiveData += [this, command] (const Data& response) {
+    this->validateControlResponse(response, command->getName(), 400, "Malformed command");
+  };
+
+  getManager().onValidatedStrategyChoiceRequest(command);
+
+  BOOST_REQUIRE(didCallbackFire());
+
+  BOOST_CHECK_EQUAL(m_strategyChoice.findEffectiveStrategy("/test").getName(),
+                    "/localhost/nfd/strategy/test-strategy-b");
+}
+
+BOOST_AUTO_TEST_CASE(Publish)
+{
+  Name commandName("/localhost/nfd/strategy-choice/list");
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+
+  ndn::nfd::StrategyChoice expectedChoice;
+  expectedChoice.setStrategy("/localhost/nfd/strategy/test-strategy-a");
+  expectedChoice.setName("/");
+
+  getFace()->onReceiveData +=
+    bind(&StrategyChoiceManagerFixture::validateList, this, _1, expectedChoice);
+
+  m_manager.onStrategyChoiceRequest(*command);
+  BOOST_REQUIRE(didCallbackFire());
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/NFD/tests/daemon/mgmt/strategy-choice-publisher.cpp b/NFD/tests/daemon/mgmt/strategy-choice-publisher.cpp
new file mode 100644
index 0000000..e85bc4a
--- /dev/null
+++ b/NFD/tests/daemon/mgmt/strategy-choice-publisher.cpp
@@ -0,0 +1,169 @@
+/* -*- 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_TESTS_NFD_MGMT_STRATEGY_CHOICE_PUBLISHER_HPP
+#define NFD_TESTS_NFD_MGMT_STRATEGY_CHOICE_PUBLISHER_HPP
+
+#include "mgmt/strategy-choice-publisher.hpp"
+#include "mgmt/app-face.hpp"
+#include "mgmt/internal-face.hpp"
+#include "fw/forwarder.hpp"
+#include "../fw/dummy-strategy.hpp"
+
+#include "tests/test-common.hpp"
+
+#include <ndn-cxx/management/nfd-strategy-choice.hpp>
+
+namespace nfd {
+namespace tests {
+
+class StrategyChoicePublisherFixture : BaseFixture
+{
+public:
+
+  StrategyChoicePublisherFixture()
+    : m_strategyChoice(m_forwarder.getStrategyChoice())
+    , m_face(make_shared<InternalFace>())
+    , m_publisher(m_strategyChoice, *m_face, "/localhost/nfd/strategy-choice/list", m_keyChain)
+    , STRATEGY_A(make_shared<DummyStrategy>(boost::ref(m_forwarder),
+                                            "/localhost/nfd/strategy/dummy-strategy-a"))
+    , STRATEGY_B(make_shared<DummyStrategy>(boost::ref(m_forwarder),
+                                            "/localhost/nfd/strategy/dummy-strategy-b"))
+    , m_finished(false)
+  {
+    m_strategyChoice.install(STRATEGY_A);
+    m_strategyChoice.install(STRATEGY_B);
+
+    ndn::nfd::StrategyChoice expectedRootEntry;
+    expectedRootEntry.setStrategy(STRATEGY_A->getName());
+    expectedRootEntry.setName("/");
+
+    m_strategyChoice.insert("/", STRATEGY_A->getName());
+    m_expectedEntries["/"] = expectedRootEntry;
+  }
+
+  void
+  validatePublish(const Data& data)
+  {
+    Block payload = data.getContent();
+
+    m_buffer.appendByteArray(payload.value(), payload.value_size());
+
+    BOOST_CHECK_NO_THROW(data.getName()[-1].toSegment());
+    if (data.getFinalBlockId() != data.getName()[-1])
+      {
+        return;
+      }
+
+    // wrap the Strategy Choice entries in a single Content TLV for easy parsing
+    m_buffer.prependVarNumber(m_buffer.size());
+    m_buffer.prependVarNumber(tlv::Content);
+
+    ndn::Block parser(m_buffer.buf(), m_buffer.size());
+    parser.parse();
+
+    BOOST_REQUIRE_EQUAL(parser.elements_size(), m_expectedEntries.size());
+
+    for (Block::element_const_iterator i = parser.elements_begin();
+         i != parser.elements_end();
+         ++i)
+      {
+        if (i->type() != ndn::tlv::nfd::StrategyChoice)
+          {
+            BOOST_FAIL("expected StrategyChoice, got type #" << i->type());
+          }
+
+        ndn::nfd::StrategyChoice entry(*i);
+
+        std::map<std::string, ndn::nfd::StrategyChoice>::const_iterator expectedEntryPos =
+          m_expectedEntries.find(entry.getName().toUri());
+
+        BOOST_REQUIRE(expectedEntryPos != m_expectedEntries.end());
+        const ndn::nfd::StrategyChoice& expectedEntry = expectedEntryPos->second;
+
+        BOOST_CHECK_EQUAL(entry.getStrategy(), expectedEntry.getStrategy());
+        BOOST_CHECK_EQUAL(entry.getName(), expectedEntry.getName());
+
+        m_matchedEntries.insert(entry.getName().toUri());
+      }
+
+    BOOST_CHECK_EQUAL(m_matchedEntries.size(), m_expectedEntries.size());
+
+    m_finished = true;
+  }
+
+protected:
+  Forwarder m_forwarder;
+  StrategyChoice& m_strategyChoice;
+  shared_ptr<InternalFace> m_face;
+  StrategyChoicePublisher m_publisher;
+
+  shared_ptr<DummyStrategy> STRATEGY_A;
+  shared_ptr<DummyStrategy> STRATEGY_B;
+
+  ndn::EncodingBuffer m_buffer;
+
+  std::map<std::string, ndn::nfd::StrategyChoice> m_expectedEntries;
+  std::set<std::string> m_matchedEntries;
+
+  bool m_finished;
+
+  ndn::KeyChain m_keyChain;
+};
+
+
+
+BOOST_FIXTURE_TEST_SUITE(MgmtStrategyChoicePublisher, StrategyChoicePublisherFixture)
+
+BOOST_AUTO_TEST_CASE(Publish)
+{
+  m_strategyChoice.insert("/test/a", STRATEGY_A->getName());
+  m_strategyChoice.insert("/test/b", STRATEGY_B->getName());
+
+  ndn::nfd::StrategyChoice expectedEntryA;
+  expectedEntryA.setStrategy(STRATEGY_A->getName());
+  expectedEntryA.setName("/test/a");
+
+  ndn::nfd::StrategyChoice expectedEntryB;
+  expectedEntryB.setStrategy(STRATEGY_B->getName());
+  expectedEntryB.setName("/test/b");
+
+  m_expectedEntries["/test/a"] = expectedEntryA;
+  m_expectedEntries["/test/b"] = expectedEntryB;
+
+  m_face->onReceiveData +=
+    bind(&StrategyChoicePublisherFixture::validatePublish, this, _1);
+
+  m_publisher.publish();
+  BOOST_REQUIRE(m_finished);
+}
+
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
+
+#endif // NFD_TESTS_NFD_MGMT_STRATEGY_CHOICE_PUBLISHER_HPP
diff --git a/NFD/tests/daemon/mgmt/tables-config-section.cpp b/NFD/tests/daemon/mgmt/tables-config-section.cpp
new file mode 100644
index 0000000..5129287
--- /dev/null
+++ b/NFD/tests/daemon/mgmt/tables-config-section.cpp
@@ -0,0 +1,399 @@
+/* -*- 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/>.
+ */
+
+#include "mgmt/tables-config-section.hpp"
+#include "fw/forwarder.hpp"
+
+
+#include "tests/test-common.hpp"
+#include "tests/daemon/fw/dummy-strategy.hpp"
+
+namespace nfd {
+namespace tests {
+
+NFD_LOG_INIT("MgmtTablesConfigSection");
+
+class TablesConfigSectionFixture : protected BaseFixture
+{
+public:
+
+  TablesConfigSectionFixture()
+    : m_cs(m_forwarder.getCs())
+    , m_pit(m_forwarder.getPit())
+    , m_fib(m_forwarder.getFib())
+    , m_strategyChoice(m_forwarder.getStrategyChoice())
+    , m_measurements(m_forwarder.getMeasurements())
+    , m_tablesConfig(m_cs, m_pit, m_fib, m_strategyChoice, m_measurements)
+  {
+    m_tablesConfig.setConfigFile(m_config);
+  }
+
+  void
+  runConfig(const std::string& CONFIG, bool isDryRun)
+  {
+    m_config.parse(CONFIG, isDryRun, "dummy-config");
+  }
+
+  bool
+  validateException(const std::runtime_error& exception, const std::string& expectedMsg)
+  {
+    if (exception.what() != expectedMsg)
+      {
+        NFD_LOG_DEBUG("exception.what(): " << exception.what());
+        NFD_LOG_DEBUG("msg:            : " << expectedMsg);
+
+        return false;
+      }
+    return true;
+  }
+
+protected:
+  Forwarder m_forwarder;
+
+  Cs& m_cs;
+  Pit& m_pit;
+  Fib& m_fib;
+  StrategyChoice& m_strategyChoice;
+  Measurements& m_measurements;
+
+  TablesConfigSection m_tablesConfig;
+  ConfigFile m_config;
+};
+
+BOOST_FIXTURE_TEST_SUITE(TestTableConfigSection, TablesConfigSectionFixture)
+
+BOOST_AUTO_TEST_CASE(ConfigureTablesWithDefaults)
+{
+  const size_t initialLimit = m_cs.getLimit();
+
+  m_tablesConfig.ensureTablesAreConfigured();
+  BOOST_CHECK_NE(initialLimit, m_cs.getLimit());
+}
+
+BOOST_AUTO_TEST_CASE(EmptyTablesSection)
+{
+  const std::string CONFIG =
+    "tables\n"
+    "{\n"
+    "}\n";
+
+  const size_t nCsMaxPackets = m_cs.getLimit();
+
+  BOOST_REQUIRE_NO_THROW(runConfig(CONFIG, true));
+  BOOST_CHECK_EQUAL(m_cs.getLimit(), nCsMaxPackets);
+
+  BOOST_REQUIRE_NO_THROW(runConfig(CONFIG, false));
+  BOOST_CHECK_NE(m_cs.getLimit(), nCsMaxPackets);
+
+  const size_t defaultLimit = m_cs.getLimit();
+
+  m_tablesConfig.ensureTablesAreConfigured();
+  BOOST_CHECK_EQUAL(defaultLimit, m_cs.getLimit());
+}
+
+BOOST_AUTO_TEST_CASE(ValidCsMaxPackets)
+{
+  const std::string CONFIG =
+    "tables\n"
+    "{\n"
+    "  cs_max_packets 101\n"
+    "}\n";
+
+  BOOST_REQUIRE_NE(m_cs.getLimit(), 101);
+
+  BOOST_REQUIRE_NO_THROW(runConfig(CONFIG, true));
+  BOOST_CHECK_NE(m_cs.getLimit(), 101);
+
+  BOOST_REQUIRE_NO_THROW(runConfig(CONFIG, false));
+  BOOST_CHECK_EQUAL(m_cs.getLimit(), 101);
+
+  m_tablesConfig.ensureTablesAreConfigured();
+  BOOST_CHECK_EQUAL(m_cs.getLimit(), 101);
+}
+
+BOOST_AUTO_TEST_CASE(MissingValueCsMaxPackets)
+{
+  const std::string CONFIG =
+    "tables\n"
+    "{\n"
+    "  cs_max_packets\n"
+    "}\n";
+
+  const std::string expectedMsg =
+    "Invalid value for option \"cs_max_packets\" in \"tables\" section";
+
+  BOOST_CHECK_EXCEPTION(runConfig(CONFIG, true),
+                        ConfigFile::Error,
+                        bind(&TablesConfigSectionFixture::validateException,
+                             this, _1, expectedMsg));
+
+  BOOST_CHECK_EXCEPTION(runConfig(CONFIG, false),
+                        ConfigFile::Error,
+                        bind(&TablesConfigSectionFixture::validateException,
+                             this, _1, expectedMsg));
+}
+
+BOOST_AUTO_TEST_CASE(InvalidValueCsMaxPackets)
+{
+  const std::string CONFIG =
+    "tables\n"
+    "{\n"
+    "  cs_max_packets invalid\n"
+    "}\n";
+
+  const std::string expectedMsg =
+    "Invalid value for option \"cs_max_packets\" in \"tables\" section";
+
+  BOOST_CHECK_EXCEPTION(runConfig(CONFIG, true),
+                        ConfigFile::Error,
+                        bind(&TablesConfigSectionFixture::validateException,
+                             this, _1, expectedMsg));
+
+
+  BOOST_CHECK_EXCEPTION(runConfig(CONFIG, false),
+                        ConfigFile::Error,
+                        bind(&TablesConfigSectionFixture::validateException,
+                             this, _1, expectedMsg));
+}
+
+BOOST_AUTO_TEST_CASE(ConfigStrategy)
+{
+  const std::string CONFIG =
+    "tables\n"
+    "{\n"
+    "strategy_choice\n"
+    "{\n"
+    "  / /localhost/nfd/strategy/test-strategy-a\n"
+    "  /a /localhost/nfd/strategy/test-strategy-b\n"
+    "}\n"
+    "}\n";
+
+  m_strategyChoice.install(make_shared<DummyStrategy>(ref(m_forwarder),
+                                                      "/localhost/nfd/strategy/test-strategy-a"));
+  m_strategyChoice.install(make_shared<DummyStrategy>(ref(m_forwarder),
+                                                      "/localhost/nfd/strategy/test-strategy-b"));
+
+  runConfig(CONFIG, true);
+  {
+    fw::Strategy& rootStrategy = m_strategyChoice.findEffectiveStrategy("/");
+    BOOST_REQUIRE_NE(rootStrategy.getName(), "/localhost/nfd/strategy/test-strategy-a");
+    BOOST_REQUIRE_NE(rootStrategy.getName(), "/localhost/nfd/strategy/test-strategy-b");
+
+    fw::Strategy& aStrategy = m_strategyChoice.findEffectiveStrategy("/a");
+    BOOST_REQUIRE_NE(aStrategy.getName(), "/localhost/nfd/strategy/test-strategy-b");
+    BOOST_REQUIRE_NE(aStrategy.getName(), "/localhost/nfd/strategy/test-strategy-a");
+  }
+
+  runConfig(CONFIG, false);
+  {
+    fw::Strategy& rootStrategy = m_strategyChoice.findEffectiveStrategy("/");
+    BOOST_REQUIRE_EQUAL(rootStrategy.getName(), "/localhost/nfd/strategy/test-strategy-a");
+
+    fw::Strategy& aStrategy = m_strategyChoice.findEffectiveStrategy("/a");
+    BOOST_REQUIRE_EQUAL(aStrategy.getName(), "/localhost/nfd/strategy/test-strategy-b");
+  }
+}
+
+BOOST_AUTO_TEST_CASE(ConfigVersionedStrategy)
+{
+  const std::string CONFIG =
+    "tables\n"
+    "{\n"
+    "strategy_choice\n"
+    "{\n"
+    "  /test/latest /localhost/nfd/strategy/test-strategy-a\n"
+    "  /test/old /localhost/nfd/strategy/test-strategy-a/%FD%01\n"
+    "}\n"
+    "}\n";
+
+
+  auto version1 = make_shared<DummyStrategy>(ref(m_forwarder),
+                                             "/localhost/nfd/strategy/test-strategy-a/%FD%01");
+
+  auto version2 = make_shared<DummyStrategy>(ref(m_forwarder),
+                                             "/localhost/nfd/strategy/test-strategy-a/%FD%02");
+  m_strategyChoice.install(version1);
+  m_strategyChoice.install(version2);
+
+  runConfig(CONFIG, true);
+  {
+    fw::Strategy& testLatestStrategy = m_strategyChoice.findEffectiveStrategy("/test/latest");
+    BOOST_REQUIRE_NE(testLatestStrategy.getName(),
+                     "/localhost/nfd/strategy/test-strategy-a/%FD%01");
+    BOOST_REQUIRE_NE(testLatestStrategy.getName(),
+                     "/localhost/nfd/strategy/test-strategy-a/%FD%02");
+
+    fw::Strategy& testOldStrategy = m_strategyChoice.findEffectiveStrategy("/test/old");
+    BOOST_REQUIRE_NE(testOldStrategy.getName(),
+                     "/localhost/nfd/strategy/test-strategy-a/%FD%01");
+    BOOST_REQUIRE_NE(testOldStrategy.getName(),
+                     "/localhost/nfd/strategy/test-strategy-a/%FD%02");
+  }
+
+  runConfig(CONFIG, false);
+  {
+    fw::Strategy& testLatestStrategy = m_strategyChoice.findEffectiveStrategy("/test/latest");
+    BOOST_REQUIRE_EQUAL(testLatestStrategy.getName(),
+                        "/localhost/nfd/strategy/test-strategy-a/%FD%02");
+
+    fw::Strategy& testOldStrategy = m_strategyChoice.findEffectiveStrategy("/test/old");
+    BOOST_REQUIRE_EQUAL(testOldStrategy.getName(),
+                        "/localhost/nfd/strategy/test-strategy-a/%FD%01");
+  }
+}
+
+BOOST_AUTO_TEST_CASE(InvalidStrategy)
+{
+  const std::string CONFIG =
+    "tables\n"
+    "{\n"
+    "strategy_choice\n"
+    "{\n"
+    "  / /localhost/nfd/strategy/test-doesnotexist\n"
+    "}\n"
+    "}\n";
+
+
+  const std::string expectedMsg =
+    "Invalid strategy choice \"/localhost/nfd/strategy/test-doesnotexist\" "
+    "for prefix \"/\" in \"strategy_choice\" section";
+
+  BOOST_CHECK_EXCEPTION(runConfig(CONFIG, true),
+                        ConfigFile::Error,
+                        bind(&TablesConfigSectionFixture::validateException,
+                             this, _1, expectedMsg));
+
+  BOOST_CHECK_EXCEPTION(runConfig(CONFIG, false),
+                        ConfigFile::Error,
+                        bind(&TablesConfigSectionFixture::validateException,
+                             this, _1, expectedMsg));
+}
+
+BOOST_AUTO_TEST_CASE(MissingStrategyPrefix)
+{
+  const std::string CONFIG =
+    "tables\n"
+    "{\n"
+    "strategy_choice\n"
+    "{\n"
+    "  /localhost/nfd/strategy/test-strategy-a\n"
+    "}\n"
+    "}\n";
+
+
+  m_strategyChoice.install(make_shared<DummyStrategy>(ref(m_forwarder),
+                                                      "/localhost/nfd/strategy/test-strategy-a"));
+
+
+  const std::string expectedMsg = "Invalid strategy choice \"\" for prefix "
+    "\"/localhost/nfd/strategy/test-strategy-a\" in \"strategy_choice\" section";
+
+  BOOST_CHECK_EXCEPTION(runConfig(CONFIG, true),
+                        ConfigFile::Error,
+                        bind(&TablesConfigSectionFixture::validateException,
+                             this, _1, expectedMsg));
+
+  BOOST_CHECK_EXCEPTION(runConfig(CONFIG, false),
+                        ConfigFile::Error,
+                        bind(&TablesConfigSectionFixture::validateException,
+                             this, _1, expectedMsg));
+}
+
+BOOST_AUTO_TEST_CASE(DuplicateStrategy)
+{
+  const std::string CONFIG =
+    "tables\n"
+    "{\n"
+    "strategy_choice\n"
+    "{\n"
+    "  / /localhost/nfd/strategy/test-strategy-a\n"
+    "  /a /localhost/nfd/strategy/test-strategy-b\n"
+    "  / /localhost/nfd/strategy/test-strategy-b\n"
+    "}\n"
+    "}\n";
+
+  m_strategyChoice.install(make_shared<DummyStrategy>(ref(m_forwarder),
+                                                      "/localhost/nfd/strategy/test-strategy-a"));
+  m_strategyChoice.install(make_shared<DummyStrategy>(ref(m_forwarder),
+                                                      "/localhost/nfd/strategy/test-strategy-b"));
+
+  const std::string expectedMsg =
+    "Duplicate strategy choice for prefix \"/\" in \"strategy_choice\" section";
+
+  BOOST_CHECK_EXCEPTION(runConfig(CONFIG, true),
+                        ConfigFile::Error,
+                        bind(&TablesConfigSectionFixture::validateException,
+                             this, _1, expectedMsg));
+
+  BOOST_CHECK_EXCEPTION(runConfig(CONFIG, false),
+                        ConfigFile::Error,
+                        bind(&TablesConfigSectionFixture::validateException,
+                             this, _1, expectedMsg));
+}
+
+class IgnoreNotTablesSection
+{
+public:
+  void
+  operator()(const std::string& filename,
+             const std::string& sectionName,
+             const ConfigSection& section,
+             bool isDryRun)
+
+  {
+    // Ignore "not_tables" section
+    if (sectionName == "not_tables")
+      {
+        // do nothing
+      }
+  }
+};
+
+BOOST_AUTO_TEST_CASE(MissingTablesSection)
+{
+  const std::string CONFIG =
+    "not_tables\n"
+    "{\n"
+    "  some_other_field 0\n"
+    "}\n";
+
+  ConfigFile passiveConfig((IgnoreNotTablesSection()));
+
+  const size_t initialLimit = m_cs.getLimit();
+
+  passiveConfig.parse(CONFIG, true, "dummy-config");
+  BOOST_REQUIRE_EQUAL(initialLimit, m_cs.getLimit());
+
+  passiveConfig.parse(CONFIG, false, "dummy-config");
+  BOOST_REQUIRE_EQUAL(initialLimit, m_cs.getLimit());
+
+  m_tablesConfig.ensureTablesAreConfigured();
+  BOOST_CHECK_NE(initialLimit, m_cs.getLimit());
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/NFD/tests/daemon/mgmt/validation-common.cpp b/NFD/tests/daemon/mgmt/validation-common.cpp
new file mode 100644
index 0000000..6065c27
--- /dev/null
+++ b/NFD/tests/daemon/mgmt/validation-common.cpp
@@ -0,0 +1,50 @@
+/* -*- 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
+ *
+ * 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 "validation-common.hpp"
+
+#include <boost/test/unit_test.hpp>
+
+namespace nfd {
+namespace tests {
+
+const Name CommandIdentityGlobalFixture::s_identityName("/unit-test/CommandFixture/id");
+shared_ptr<ndn::IdentityCertificate> CommandIdentityGlobalFixture::s_certificate;
+
+CommandIdentityGlobalFixture::CommandIdentityGlobalFixture()
+{
+  BOOST_ASSERT(!static_cast<bool>(s_certificate));
+  s_certificate = m_keys.getCertificate(m_keys.createIdentity(s_identityName));
+}
+
+CommandIdentityGlobalFixture::~CommandIdentityGlobalFixture()
+{
+  s_certificate.reset();
+  m_keys.deleteIdentity(s_identityName);
+}
+
+BOOST_GLOBAL_FIXTURE(CommandIdentityGlobalFixture)
+
+} // namespace tests
+} // namespace nfd
diff --git a/NFD/tests/daemon/mgmt/validation-common.hpp b/NFD/tests/daemon/mgmt/validation-common.hpp
new file mode 100644
index 0000000..f8b1367
--- /dev/null
+++ b/NFD/tests/daemon/mgmt/validation-common.hpp
@@ -0,0 +1,125 @@
+/* -*- 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
+ *
+ * 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_TESTS_NFD_MGMT_VALIDATION_COMMON_HPP
+#define NFD_TESTS_NFD_MGMT_VALIDATION_COMMON_HPP
+
+#include "common.hpp"
+#include <ndn-cxx/util/command-interest-generator.hpp>
+
+namespace nfd {
+namespace tests {
+
+// class ValidatedManagementFixture
+// {
+// public:
+//   ValidatedManagementFixture()
+//     : m_validator(make_shared<ndn::CommandInterestValidator>())
+//   {
+//   }
+
+//   virtual
+//   ~ValidatedManagementFixture()
+//   {
+//   }
+
+// protected:
+//   shared_ptr<ndn::CommandInterestValidator> m_validator;
+// };
+
+/// a global fixture that holds the identity for CommandFixture
+class CommandIdentityGlobalFixture
+{
+public:
+  CommandIdentityGlobalFixture();
+
+  ~CommandIdentityGlobalFixture();
+
+  static const Name& getIdentityName()
+  {
+    return s_identityName;
+  }
+
+  static shared_ptr<ndn::IdentityCertificate> getCertificate()
+  {
+    BOOST_ASSERT(static_cast<bool>(s_certificate));
+    return s_certificate;
+  }
+
+private:
+  ndn::KeyChain m_keys;
+  static const Name s_identityName;
+  static shared_ptr<ndn::IdentityCertificate> s_certificate;
+};
+
+template<typename T>
+class CommandFixture : public T
+{
+public:
+  virtual
+  ~CommandFixture()
+  {
+  }
+
+  void
+  generateCommand(Interest& interest)
+  {
+    m_generator.generateWithIdentity(interest, getIdentityName());
+  }
+
+  const Name&
+  getIdentityName() const
+  {
+    return CommandIdentityGlobalFixture::getIdentityName();
+  }
+
+protected:
+  CommandFixture()
+    : m_certificate(CommandIdentityGlobalFixture::getCertificate())
+  {
+  }
+
+protected:
+  shared_ptr<ndn::IdentityCertificate> m_certificate;
+  ndn::CommandInterestGenerator m_generator;
+};
+
+template <typename T>
+class UnauthorizedCommandFixture : public CommandFixture<T>
+{
+public:
+  UnauthorizedCommandFixture()
+  {
+  }
+
+  virtual
+  ~UnauthorizedCommandFixture()
+  {
+  }
+};
+
+} // namespace tests
+} // namespace nfd
+
+#endif // NFD_TESTS_NFD_MGMT_VALIDATION_COMMON_HPP
diff --git a/NFD/tests/daemon/table/cs.cpp b/NFD/tests/daemon/table/cs.cpp
new file mode 100644
index 0000000..4032ff4
--- /dev/null
+++ b/NFD/tests/daemon/table/cs.cpp
@@ -0,0 +1,1028 @@
+/* -*- 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
+ *
+ * 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/>.
+ *
+ * \author Ilya Moiseenko <iliamo@ucla.edu>
+ **/
+
+#include "table/cs.hpp"
+#include "table/cs-entry.hpp"
+
+#include "tests/test-common.hpp"
+
+namespace nfd {
+namespace tests {
+
+class CsAccessor : public Cs
+{
+public:
+  bool
+  evictItem_accessor()
+  {
+    return evictItem();
+  }
+};
+
+BOOST_FIXTURE_TEST_SUITE(TableCs, BaseFixture)
+
+BOOST_AUTO_TEST_CASE(SetLimit)
+{
+  Cs cs(1);
+
+  BOOST_CHECK_EQUAL(cs.insert(*makeData("/1")), true);
+  BOOST_CHECK_EQUAL(cs.insert(*makeData("/2")), true);
+  BOOST_CHECK_EQUAL(cs.size(), 1);
+
+  cs.setLimit(3);
+  BOOST_CHECK_EQUAL(cs.insert(*makeData("/3")), true);
+  BOOST_CHECK_EQUAL(cs.insert(*makeData("/4")), true);
+  BOOST_CHECK_EQUAL(cs.insert(*makeData("/5")), true);
+  BOOST_CHECK_EQUAL(cs.size(), 3);
+
+  cs.setLimit(2);
+  BOOST_CHECK_EQUAL(cs.size(), 2);
+}
+
+BOOST_AUTO_TEST_CASE(Insertion)
+{
+  Cs cs;
+
+  BOOST_CHECK_EQUAL(cs.insert(*makeData("/insertion")), true);
+}
+
+BOOST_AUTO_TEST_CASE(Insertion2)
+{
+  Cs cs;
+
+  cs.insert(*makeData("/a"));
+  cs.insert(*makeData("/b"));
+  cs.insert(*makeData("/c"));
+  cs.insert(*makeData("/d"));
+
+  BOOST_CHECK_EQUAL(cs.size(), 4);
+}
+
+BOOST_AUTO_TEST_CASE(DuplicateInsertion)
+{
+  Cs cs;
+
+  shared_ptr<Data> data0 = makeData("/insert/smth");
+  BOOST_CHECK_EQUAL(cs.insert(*data0), true);
+
+  shared_ptr<Data> data = makeData("/insert/duplicate");
+  BOOST_CHECK_EQUAL(cs.insert(*data), true);
+
+  cs.insert(*data);
+  BOOST_CHECK_EQUAL(cs.size(), 2);
+}
+
+
+BOOST_AUTO_TEST_CASE(DuplicateInsertion2)
+{
+  Cs cs;
+
+  shared_ptr<Data> data = makeData("/insert/duplicate");
+  BOOST_CHECK_EQUAL(cs.insert(*data), true);
+
+  cs.insert(*data);
+  BOOST_CHECK_EQUAL(cs.size(), 1);
+
+  shared_ptr<Data> data2 = makeData("/insert/original");
+  BOOST_CHECK_EQUAL(cs.insert(*data2), true);
+  BOOST_CHECK_EQUAL(cs.size(), 2);
+}
+
+BOOST_AUTO_TEST_CASE(InsertAndFind)
+{
+  Cs cs;
+
+  Name name("/insert/and/find");
+
+  shared_ptr<Data> data = makeData(name);
+  BOOST_CHECK_EQUAL(cs.insert(*data), true);
+
+  shared_ptr<Interest> interest = make_shared<Interest>(name);
+
+  const Data* found = cs.find(*interest);
+
+  BOOST_REQUIRE(found != 0);
+  BOOST_CHECK_EQUAL(data->getName(), found->getName());
+}
+
+BOOST_AUTO_TEST_CASE(InsertAndNotFind)
+{
+  Cs cs;
+
+  Name name("/insert/and/find");
+  shared_ptr<Data> data = makeData(name);
+  BOOST_CHECK_EQUAL(cs.insert(*data), true);
+
+  Name name2("/not/find");
+  shared_ptr<Interest> interest = make_shared<Interest>(name2);
+
+  const Data* found = cs.find(*interest);
+
+  BOOST_CHECK_EQUAL(found, static_cast<const Data*>(0));
+}
+
+
+BOOST_AUTO_TEST_CASE(InsertAndErase)
+{
+  CsAccessor cs;
+
+  shared_ptr<Data> data = makeData("/insertandelete");
+  cs.insert(*data);
+  BOOST_CHECK_EQUAL(cs.size(), 1);
+
+  cs.evictItem_accessor();
+  BOOST_CHECK_EQUAL(cs.size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(StalenessQueue)
+{
+  CsAccessor cs;
+
+  Name name2("/insert/fresh");
+  shared_ptr<Data> data2 = makeData(name2);
+  data2->setFreshnessPeriod(time::milliseconds(5000));
+  signData(data2);
+  cs.insert(*data2);
+
+  Name name("/insert/expire");
+  shared_ptr<Data> data = makeData(name);
+  data->setFreshnessPeriod(time::milliseconds(500));
+  signData(data);
+  cs.insert(*data);
+
+  BOOST_CHECK_EQUAL(cs.size(), 2);
+
+  sleep(3);
+
+  cs.evictItem_accessor();
+  BOOST_CHECK_EQUAL(cs.size(), 1);
+
+  shared_ptr<Interest> interest = make_shared<Interest>(name2);
+  const Data* found = cs.find(*interest);
+  BOOST_REQUIRE(found != 0);
+  BOOST_CHECK_EQUAL(found->getName(), name2);
+}
+
+BOOST_AUTO_TEST_CASE(Bug2043) // eviction order of unsolicited packets
+{
+  Cs cs(3);
+
+  cs.insert(*makeData("/a"), true);
+  cs.insert(*makeData("/b"), true);
+  cs.insert(*makeData("/c"), true);
+  BOOST_CHECK_EQUAL(cs.size(), 3);
+
+  cs.insert(*makeData("/d"), true);
+  BOOST_CHECK_EQUAL(cs.size(), 3);
+
+  const Data* oldestData = cs.find(Interest("/a"));
+  const Data* newestData = cs.find(Interest("/c"));
+  BOOST_CHECK(oldestData == 0);
+  BOOST_CHECK(newestData != 0);
+}
+
+BOOST_AUTO_TEST_CASE(UnsolicitedWithSolicitedEvictionOrder)
+{
+  Cs cs(3);
+
+  cs.insert(*makeData("/a"), true);
+  cs.insert(*makeData("/b"), false);
+  cs.insert(*makeData("/c"), true);
+  BOOST_CHECK_EQUAL(cs.size(), 3);
+
+  cs.insert(*makeData("/d"), true);
+  BOOST_CHECK_EQUAL(cs.size(), 3);
+
+  const Data* oldestData = cs.find(Interest("/a"));
+  const Data* newestData = cs.find(Interest("/c"));
+  BOOST_CHECK(oldestData == 0);
+  BOOST_CHECK(newestData != 0);
+}
+
+//even max size
+BOOST_AUTO_TEST_CASE(ReplacementEvenSize)
+{
+  Cs cs(4);
+
+  shared_ptr<Data> data = makeData("/a");
+  cs.insert(*data);
+
+  shared_ptr<Data> data2 = makeData("/b");
+  cs.insert(*data2);
+
+  shared_ptr<Data> data3 = makeData("/c");
+  cs.insert(*data3);
+
+  shared_ptr<Data> data4 = makeData("/d");
+  cs.insert(*data4);
+
+  shared_ptr<Data> data5 = makeData("/e");
+  cs.insert(*data5);
+
+  BOOST_CHECK_EQUAL(cs.size(), 4);
+}
+
+//odd max size
+BOOST_AUTO_TEST_CASE(Replacement2)
+{
+  Cs cs(3);
+
+  shared_ptr<Data> data = makeData("/a");
+  cs.insert(*data);
+
+  shared_ptr<Data> data2 = makeData("/b");
+  cs.insert(*data2);
+
+  shared_ptr<Data> data3 = makeData("/c");
+  cs.insert(*data3);
+
+  shared_ptr<Data> data4 = makeData("/d");
+  cs.insert(*data4);
+
+  BOOST_CHECK_EQUAL(cs.size(), 3);
+}
+
+BOOST_AUTO_TEST_CASE(InsertAndEraseByName)
+{
+  CsAccessor cs;
+
+  Name name("/insertandremovebyname");
+  shared_ptr<Data> data = makeData(name);
+  cs.insert(*data);
+
+  ndn::ConstBufferPtr digest1 = ndn::crypto::sha256(data->wireEncode().wire(),
+                                                    data->wireEncode().size());
+
+  shared_ptr<Data> data2 = makeData("/a");
+  cs.insert(*data2);
+
+  shared_ptr<Data> data3 = makeData("/z");
+  cs.insert(*data3);
+
+  BOOST_CHECK_EQUAL(cs.size(), 3);
+
+  name.appendImplicitSha256Digest(digest1);
+  cs.erase(name);
+  BOOST_CHECK_EQUAL(cs.size(), 2);
+}
+
+BOOST_AUTO_TEST_CASE(DigestCalculation)
+{
+  shared_ptr<Data> data = makeData("/digest/compute");
+
+  ndn::ConstBufferPtr digest1 = ndn::crypto::sha256(data->wireEncode().wire(),
+                                                    data->wireEncode().size());
+  BOOST_CHECK_EQUAL(digest1->size(), 32);
+
+  cs::Entry* entry = new cs::Entry();
+  entry->setData(*data, false);
+
+  BOOST_CHECK_EQUAL_COLLECTIONS(digest1->begin(), digest1->end(),
+                                entry->getFullName()[-1].value_begin(),
+                                entry->getFullName()[-1].value_end());
+}
+
+BOOST_AUTO_TEST_CASE(InsertCanonical)
+{
+  Cs cs;
+
+  shared_ptr<Data> data = makeData("/a");
+  cs.insert(*data);
+
+  shared_ptr<Data> data2 = makeData("/b");
+  cs.insert(*data2);
+
+  shared_ptr<Data> data3 = makeData("/c");
+  cs.insert(*data3);
+
+  shared_ptr<Data> data4 = makeData("/d");
+  cs.insert(*data4);
+
+  shared_ptr<Data> data5 = makeData("/c/c/1/2/3/4/5/6");
+  cs.insert(*data5);
+
+  shared_ptr<Data> data6 = makeData("/c/c/1/2/3");
+  cs.insert(*data6);
+
+  shared_ptr<Data> data7 = makeData("/c/c/1");
+  cs.insert(*data7);
+}
+
+BOOST_AUTO_TEST_CASE(EraseCanonical)
+{
+  Cs cs;
+
+  shared_ptr<Data> data = makeData("/a");
+  cs.insert(*data);
+
+  shared_ptr<Data> data2 = makeData("/b");
+  cs.insert(*data2);
+
+  shared_ptr<Data> data3 = makeData("/c");
+  cs.insert(*data3);
+
+  shared_ptr<Data> data4 = makeData("/d");
+  cs.insert(*data4);
+
+  shared_ptr<Data> data5 = makeData("/c/c/1/2/3/4/5/6");
+  cs.insert(*data5);
+
+  shared_ptr<Data> data6 = makeData("/c/c/1/2/3");
+  cs.insert(*data6);
+
+  shared_ptr<Data> data7 = makeData("/c/c/1");
+  cs.insert(*data7);
+
+  ndn::ConstBufferPtr digest1 = ndn::crypto::sha256(data->wireEncode().wire(),
+                                                    data->wireEncode().size());
+
+  Name name("/a");
+  name.appendImplicitSha256Digest(digest1->buf(), digest1->size());
+  cs.erase(name);
+  BOOST_CHECK_EQUAL(cs.size(), 6);
+}
+
+BOOST_AUTO_TEST_CASE(ImplicitDigestSelector)
+{
+  CsAccessor cs;
+
+  Name name("/digest/works");
+  shared_ptr<Data> data = makeData(name);
+  cs.insert(*data);
+
+  shared_ptr<Data> data2 = makeData("/a");
+  cs.insert(*data2);
+
+  shared_ptr<Data> data3 = makeData("/z/z/z");
+  cs.insert(*data3);
+
+  ndn::ConstBufferPtr digest1 = ndn::crypto::sha256(data->wireEncode().wire(),
+                                                    data->wireEncode().size());
+  uint8_t digest2[32] = {0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1};
+
+  shared_ptr<Interest> interest = make_shared<Interest>();
+  interest->setName(Name(name).append(digest1->buf(), digest1->size()));
+  interest->setMinSuffixComponents(0);
+  interest->setMaxSuffixComponents(0);
+
+  const Data* found = cs.find(*interest);
+  BOOST_CHECK_NE(found, static_cast<const Data*>(0));
+  BOOST_CHECK_EQUAL(found->getName(), name);
+
+  shared_ptr<Interest> interest2 = make_shared<Interest>();
+  interest2->setName(Name(name).append(digest2, 32));
+  interest2->setMinSuffixComponents(0);
+  interest2->setMaxSuffixComponents(0);
+
+  const Data* notfound = cs.find(*interest2);
+  BOOST_CHECK_EQUAL(notfound, static_cast<const Data*>(0));
+}
+
+BOOST_AUTO_TEST_CASE(ChildSelector)
+{
+  Cs cs;
+
+  shared_ptr<Data> data = makeData("/a");
+  cs.insert(*data);
+
+  shared_ptr<Data> data2 = makeData("/b");
+  cs.insert(*data2);
+
+  shared_ptr<Data> data4 = makeData("/d");
+  cs.insert(*data4);
+
+  shared_ptr<Data> data5 = makeData("/c/c");
+  cs.insert(*data5);
+
+  shared_ptr<Data> data6 = makeData("/c/f");
+  cs.insert(*data6);
+
+  shared_ptr<Data> data7 = makeData("/c/n");
+  cs.insert(*data7);
+
+  shared_ptr<Interest> interest = make_shared<Interest>("/c");
+  interest->setChildSelector(1);
+
+  const Data* found = cs.find(*interest);
+  BOOST_CHECK_EQUAL(found->getName(), "/c/n");
+
+  shared_ptr<Interest> interest2 = make_shared<Interest>("/c");
+  interest2->setChildSelector(0);
+
+  const Data* found2 = cs.find(*interest2);
+  BOOST_CHECK_EQUAL(found2->getName(), "/c/c");
+}
+
+BOOST_AUTO_TEST_CASE(ChildSelector2)
+{
+  Cs cs;
+
+  shared_ptr<Data> data = makeData("/a/b/1");
+  cs.insert(*data);
+
+  shared_ptr<Data> data2 = makeData("/a/b/2");
+  cs.insert(*data2);
+
+  shared_ptr<Data> data3 = makeData("/a/z/1");
+  cs.insert(*data3);
+
+  shared_ptr<Data> data4 = makeData("/a/z/2");
+  cs.insert(*data4);
+
+  shared_ptr<Interest> interest = make_shared<Interest>("/a");
+  interest->setChildSelector(1);
+
+  const Data* found = cs.find(*interest);
+  BOOST_CHECK_EQUAL(found->getName(), "/a/z/1");
+}
+
+BOOST_AUTO_TEST_CASE(MustBeFreshSelector)
+{
+  Cs cs;
+
+  Name name("/insert/nonfresh");
+  shared_ptr<Data> data = makeData(name);
+  data->setFreshnessPeriod(time::milliseconds(500));
+  signData(data);
+  cs.insert(*data);
+
+  sleep(1);
+
+  shared_ptr<Interest> interest = make_shared<Interest>(name);
+  interest->setMustBeFresh(true);
+
+  const Data* found = cs.find(*interest);
+  BOOST_CHECK_EQUAL(found, static_cast<const Data*>(0));
+}
+
+BOOST_AUTO_TEST_CASE(PublisherKeySelector)
+{
+  Cs cs;
+
+  Name name("/insert/withkey");
+  shared_ptr<Data> data = makeData(name);
+  cs.insert(*data);
+
+  shared_ptr<Interest> interest = make_shared<Interest>(name);
+  Name keyName("/somewhere/key");
+
+  ndn::KeyLocator locator(keyName);
+  interest->setPublisherPublicKeyLocator(locator);
+
+  const Data* found = cs.find(*interest);
+  BOOST_CHECK_EQUAL(found, static_cast<const Data*>(0));
+}
+
+BOOST_AUTO_TEST_CASE(PublisherKeySelector2)
+{
+  Cs cs;
+  Name name("/insert/withkey");
+  shared_ptr<Data> data = makeData(name);
+  cs.insert(*data);
+
+  Name name2("/insert/withkey2");
+  shared_ptr<Data> data2 = make_shared<Data>(name2);
+
+  Name keyName("/somewhere/key");
+  const ndn::KeyLocator locator(keyName);
+
+  ndn::SignatureSha256WithRsa fakeSignature;
+  fakeSignature.setValue(ndn::dataBlock(tlv::SignatureValue,
+                                        reinterpret_cast<const uint8_t*>(0), 0));
+
+  fakeSignature.setKeyLocator(locator);
+  data2->setSignature(fakeSignature);
+  data2->wireEncode();
+
+  cs.insert(*data2);
+
+  shared_ptr<Interest> interest = make_shared<Interest>(name2);
+  interest->setPublisherPublicKeyLocator(locator);
+
+  const Data* found = cs.find(*interest);
+  BOOST_CHECK_NE(found, static_cast<const Data*>(0));
+  BOOST_CHECK_EQUAL(found->getName(), data2->getName());
+}
+
+
+/// @todo Expected failures, needs to be fixed as part of Issue #2118
+BOOST_AUTO_TEST_CASE_EXPECTED_FAILURES(MinMaxComponentsSelector, 1)
+BOOST_AUTO_TEST_CASE(MinMaxComponentsSelector)
+{
+  Cs cs;
+
+  shared_ptr<Data> data = makeData("/a");
+  cs.insert(*data);
+
+  shared_ptr<Data> data2 = makeData("/b");
+  cs.insert(*data2);
+
+  shared_ptr<Data> data4 = makeData("/d");
+  cs.insert(*data4);
+
+  shared_ptr<Data> data5 = makeData("/c/c/1/2/3/4/5/6");
+  cs.insert(*data5);
+
+  shared_ptr<Data> data6 = makeData("/c/c/6/7/8/9");
+  cs.insert(*data6);
+
+  shared_ptr<Data> data7 = makeData("/c/c/1/2/3");
+  cs.insert(*data7);
+
+  shared_ptr<Data> data8 = makeData("/c/c/1");
+  cs.insert(*data8);
+
+  shared_ptr<Interest> interest = make_shared<Interest>("/c/c");
+  interest->setMinSuffixComponents(3);
+  interest->setChildSelector(0);
+
+  const Data* found = cs.find(*interest);
+  BOOST_CHECK_EQUAL(found->getName(), "/c/c/1/2/3/4/5/6");
+
+  shared_ptr<Interest> interest2 = make_shared<Interest>("/c/c");
+  interest2->setMinSuffixComponents(4);
+  interest2->setChildSelector(1);
+
+  const Data* found2 = cs.find(*interest2);
+  BOOST_CHECK_EQUAL(found2->getName(), "/c/c/6/7/8/9");
+
+  shared_ptr<Interest> interest3 = make_shared<Interest>("/c/c");
+  interest3->setMaxSuffixComponents(2);
+  interest3->setChildSelector(1);
+
+  const Data* found3 = cs.find(*interest3);
+  BOOST_CHECK_EQUAL(found3->getName(), "/c/c/1");
+}
+
+BOOST_AUTO_TEST_CASE(ExcludeSelector)
+{
+  Cs cs;
+
+  shared_ptr<Data> data = makeData("/a");
+  cs.insert(*data);
+
+  shared_ptr<Data> data2 = makeData("/b");
+  cs.insert(*data2);
+
+  shared_ptr<Data> data3 = makeData("/c/a");
+  cs.insert(*data3);
+
+  shared_ptr<Data> data4 = makeData("/d");
+  cs.insert(*data4);
+
+  shared_ptr<Data> data5 = makeData("/c/c");
+  cs.insert(*data5);
+
+  shared_ptr<Data> data6 = makeData("/c/f");
+  cs.insert(*data6);
+
+  shared_ptr<Data> data7 = makeData("/c/n");
+  cs.insert(*data7);
+
+  shared_ptr<Interest> interest = make_shared<Interest>("/c");
+  interest->setChildSelector(1);
+  Exclude e;
+  e.excludeOne (Name::Component("n"));
+  interest->setExclude(e);
+
+  const Data* found = cs.find(*interest);
+  BOOST_CHECK_EQUAL(found->getName(), "/c/f");
+
+  shared_ptr<Interest> interest2 = make_shared<Interest>("/c");
+  interest2->setChildSelector(0);
+
+  Exclude e2;
+  e2.excludeOne (Name::Component("a"));
+  interest2->setExclude(e2);
+
+  const Data* found2 = cs.find(*interest2);
+  BOOST_CHECK_EQUAL(found2->getName(), "/c/c");
+
+  shared_ptr<Interest> interest3 = make_shared<Interest>("/c");
+  interest3->setChildSelector(0);
+
+  Exclude e3;
+  e3.excludeOne (Name::Component("c"));
+  interest3->setExclude(e3);
+
+  const Data* found3 = cs.find(*interest3);
+  BOOST_CHECK_EQUAL(found3->getName(), "/c/a");
+}
+
+//
+
+class FindFixture : protected BaseFixture
+{
+protected:
+  void
+  insert(uint32_t id, const Name& name)
+  {
+    shared_ptr<Data> data = makeData(name);
+    data->setFreshnessPeriod(time::milliseconds(99999));
+    data->setContent(reinterpret_cast<const uint8_t*>(&id), sizeof(id));
+    signData(data);
+
+    m_cs.insert(*data);
+  }
+
+  Interest&
+  startInterest(const Name& name)
+  {
+    m_interest = make_shared<Interest>(name);
+    return *m_interest;
+  }
+
+  uint32_t
+  find()
+  {
+    const Data* found = m_cs.find(*m_interest);
+    if (found == 0) {
+      return 0;
+    }
+    const Block& content = found->getContent();
+    if (content.value_size() != sizeof(uint32_t)) {
+      return 0;
+    }
+    return *reinterpret_cast<const uint32_t*>(content.value());
+  }
+
+protected:
+  Cs m_cs;
+  shared_ptr<Interest> m_interest;
+};
+
+BOOST_FIXTURE_TEST_SUITE(Find, FindFixture)
+
+BOOST_AUTO_TEST_CASE(EmptyDataName)
+{
+  insert(1, "ndn:/");
+
+  startInterest("ndn:/");
+  BOOST_CHECK_EQUAL(find(), 1);
+}
+
+BOOST_AUTO_TEST_CASE(EmptyInterestName)
+{
+  insert(1, "ndn:/A");
+
+  startInterest("ndn:/");
+  BOOST_CHECK_EQUAL(find(), 1);
+}
+
+BOOST_AUTO_TEST_CASE(Leftmost)
+{
+  insert(1, "ndn:/A");
+  insert(2, "ndn:/B/p/1");
+  insert(3, "ndn:/B/p/2");
+  insert(4, "ndn:/B/q/1");
+  insert(5, "ndn:/B/q/2");
+  insert(6, "ndn:/C");
+
+  startInterest("ndn:/B");
+  BOOST_CHECK_EQUAL(find(), 2);
+}
+
+BOOST_AUTO_TEST_CASE(Rightmost)
+{
+  insert(1, "ndn:/A");
+  insert(2, "ndn:/B/p/1");
+  insert(3, "ndn:/B/p/2");
+  insert(4, "ndn:/B/q/1");
+  insert(5, "ndn:/B/q/2");
+  insert(6, "ndn:/C");
+
+  startInterest("ndn:/B")
+    .setChildSelector(1);
+  BOOST_CHECK_EQUAL(find(), 4);
+}
+
+/// @todo Expected failures, needs to be fixed as part of Issue #2118
+BOOST_AUTO_TEST_CASE_EXPECTED_FAILURES(Leftmost_ExactName1, 1)
+BOOST_AUTO_TEST_CASE(Leftmost_ExactName1)
+{
+  insert(1, "ndn:/");
+  insert(2, "ndn:/A/B");
+  insert(3, "ndn:/A/C");
+  insert(4, "ndn:/A");
+  insert(5, "ndn:/D");
+
+  // Intuitively you would think Data 4 should be between Data 1 and 2,
+  // but Data 4 has full Name ndn:/A/<32-octet hash>.
+  startInterest("ndn:/A");
+  BOOST_CHECK_EQUAL(find(), 2);
+}
+
+BOOST_AUTO_TEST_CASE(Leftmost_ExactName33)
+{
+  insert(1, "ndn:/");
+  insert(2, "ndn:/A");
+  insert(3, "ndn:/A/BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"); // 33 'B's
+  insert(4, "ndn:/A/CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC"); // 33 'C's
+  insert(5, "ndn:/D");
+
+  // Data 2 is returned, because <32-octet hash> is less than Data 3.
+  startInterest("ndn:/A");
+  BOOST_CHECK_EQUAL(find(), 2);
+}
+
+/// @todo Expected failures, needs to be fixed as part of Issue #2118
+BOOST_AUTO_TEST_CASE_EXPECTED_FAILURES(MinSuffixComponents, 2)
+BOOST_AUTO_TEST_CASE(MinSuffixComponents)
+{
+  insert(1, "ndn:/A/1/2/3/4");
+  insert(2, "ndn:/B/1/2/3");
+  insert(3, "ndn:/C/1/2");
+  insert(4, "ndn:/D/1");
+  insert(5, "ndn:/E");
+  insert(6, "ndn:/");
+
+  startInterest("ndn:/")
+    .setChildSelector(1)
+    .setMinSuffixComponents(0);
+  BOOST_CHECK_EQUAL(find(), 6);
+
+  startInterest("ndn:/")
+    .setChildSelector(1)
+    .setMinSuffixComponents(1);
+  BOOST_CHECK_EQUAL(find(), 6);
+
+  startInterest("ndn:/")
+    .setChildSelector(1)
+    .setMinSuffixComponents(2);
+  BOOST_CHECK_EQUAL(find(), 5);
+
+  startInterest("ndn:/")
+    .setChildSelector(1)
+    .setMinSuffixComponents(3);
+  BOOST_CHECK_EQUAL(find(), 4);
+
+  startInterest("ndn:/")
+    .setChildSelector(1)
+    .setMinSuffixComponents(4);
+  BOOST_CHECK_EQUAL(find(), 3);
+
+  startInterest("ndn:/")
+    .setChildSelector(1)
+    .setMinSuffixComponents(5);
+  BOOST_CHECK_EQUAL(find(), 2);
+
+  startInterest("ndn:/")
+    .setChildSelector(1)
+    .setMinSuffixComponents(6);
+  BOOST_CHECK_EQUAL(find(), 1);
+
+  startInterest("ndn:/")
+    .setChildSelector(1)
+    .setMinSuffixComponents(7);
+  BOOST_CHECK_EQUAL(find(), 0);
+}
+
+/// @todo Expected failures, needs to be fixed as part of Issue #2118
+BOOST_AUTO_TEST_CASE_EXPECTED_FAILURES(MaxSuffixComponents, 5)
+BOOST_AUTO_TEST_CASE(MaxSuffixComponents)
+{
+  insert(1, "ndn:/");
+  insert(2, "ndn:/A");
+  insert(3, "ndn:/A/B");
+  insert(4, "ndn:/A/B/C");
+  insert(5, "ndn:/A/B/C/D");
+  insert(6, "ndn:/A/B/C/D/E");
+  // Order is 6,5,4,3,2,1, because <32-octet hash> is greater than a 1-octet component.
+
+  startInterest("ndn:/")
+    .setMaxSuffixComponents(0);
+  BOOST_CHECK_EQUAL(find(), 0);
+
+  startInterest("ndn:/")
+    .setMaxSuffixComponents(1);
+  BOOST_CHECK_EQUAL(find(), 1);
+
+  startInterest("ndn:/")
+    .setMaxSuffixComponents(2);
+  BOOST_CHECK_EQUAL(find(), 2);
+
+  startInterest("ndn:/")
+    .setMaxSuffixComponents(3);
+  BOOST_CHECK_EQUAL(find(), 3);
+
+  startInterest("ndn:/")
+    .setMaxSuffixComponents(4);
+  BOOST_CHECK_EQUAL(find(), 4);
+
+  startInterest("ndn:/")
+    .setMaxSuffixComponents(5);
+  BOOST_CHECK_EQUAL(find(), 5);
+
+  startInterest("ndn:/")
+    .setMaxSuffixComponents(6);
+  BOOST_CHECK_EQUAL(find(), 6);
+}
+
+BOOST_AUTO_TEST_CASE(DigestOrder)
+{
+  insert(1, "ndn:/A");
+  insert(2, "ndn:/A");
+  // We don't know which comes first, but there must be some order
+
+  startInterest("ndn:/A")
+    .setChildSelector(0);
+  uint32_t leftmost = find();
+
+  startInterest("ndn:/A")
+    .setChildSelector(1);
+  uint32_t rightmost = find();
+
+  BOOST_CHECK_NE(leftmost, rightmost);
+}
+
+/// @todo Expected failures, needs to be fixed as part of Issue #2118
+BOOST_AUTO_TEST_CASE_EXPECTED_FAILURES(DigestExclude, 1)
+BOOST_AUTO_TEST_CASE(DigestExclude)
+{
+  insert(1, "ndn:/A/B");
+  insert(2, "ndn:/A");
+  insert(3, "ndn:/A/CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC"); // 33 'C's
+
+  startInterest("ndn:/A")
+    .setExclude(Exclude().excludeBefore(name::Component(reinterpret_cast<const uint8_t*>(
+        "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
+        "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"), 31))); // 31 0xFF's
+  BOOST_CHECK_EQUAL(find(), 2);
+
+  startInterest("ndn:/A")
+    .setChildSelector(1)
+    .setExclude(Exclude().excludeAfter(name::Component(reinterpret_cast<const uint8_t*>(
+        "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+        "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+        "\x00"), 33))); // 33 0x00's
+  BOOST_CHECK_EQUAL(find(), 2);
+}
+
+BOOST_AUTO_TEST_CASE(ExactName32)
+{
+  insert(1, "ndn:/A/BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"); // 32 'B's
+  insert(2, "ndn:/A/CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC"); // 32 'C's
+
+  startInterest("ndn:/A/BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB");
+  BOOST_CHECK_EQUAL(find(), 1);
+}
+
+/// @todo Expected failures, needs to be fixed as part of Issue #2118
+BOOST_AUTO_TEST_CASE_EXPECTED_FAILURES(MinSuffixComponents32, 2)
+BOOST_AUTO_TEST_CASE(MinSuffixComponents32)
+{
+  insert(1, "ndn:/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/A/1/2/3/4"); // 32 'x's
+  insert(2, "ndn:/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/B/1/2/3");
+  insert(3, "ndn:/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/C/1/2");
+  insert(4, "ndn:/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/D/1");
+  insert(5, "ndn:/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/E");
+  insert(6, "ndn:/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
+
+  startInterest("ndn:/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")
+    .setChildSelector(1)
+    .setMinSuffixComponents(0);
+  BOOST_CHECK_EQUAL(find(), 6);
+
+  startInterest("ndn:/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")
+    .setChildSelector(1)
+    .setMinSuffixComponents(1);
+  BOOST_CHECK_EQUAL(find(), 6);
+
+  startInterest("ndn:/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")
+    .setChildSelector(1)
+    .setMinSuffixComponents(2);
+  BOOST_CHECK_EQUAL(find(), 5);
+
+  startInterest("ndn:/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")
+    .setChildSelector(1)
+    .setMinSuffixComponents(3);
+  BOOST_CHECK_EQUAL(find(), 4);
+
+  startInterest("ndn:/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")
+    .setChildSelector(1)
+    .setMinSuffixComponents(4);
+  BOOST_CHECK_EQUAL(find(), 3);
+
+  startInterest("ndn:/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")
+    .setChildSelector(1)
+    .setMinSuffixComponents(5);
+  BOOST_CHECK_EQUAL(find(), 2);
+
+  startInterest("ndn:/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")
+    .setChildSelector(1)
+    .setMinSuffixComponents(6);
+  BOOST_CHECK_EQUAL(find(), 1);
+
+  startInterest("ndn:/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")
+    .setChildSelector(1)
+    .setMinSuffixComponents(7);
+  BOOST_CHECK_EQUAL(find(), 0);
+}
+
+/// @todo Expected failures, needs to be fixed as part of Issue #2118
+BOOST_AUTO_TEST_CASE_EXPECTED_FAILURES(MaxSuffixComponents32, 5)
+BOOST_AUTO_TEST_CASE(MaxSuffixComponents32)
+{
+  insert(1, "ndn:/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/"); // 32 'x's
+  insert(2, "ndn:/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/A");
+  insert(3, "ndn:/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/A/B");
+  insert(4, "ndn:/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/A/B/C");
+  insert(5, "ndn:/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/A/B/C/D");
+  insert(6, "ndn:/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/A/B/C/D/E");
+  // Order is 6,5,4,3,2,1, because <32-octet hash> is greater than a 1-octet component.
+
+  startInterest("ndn:/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")
+    .setMaxSuffixComponents(0);
+  BOOST_CHECK_EQUAL(find(), 0);
+
+  startInterest("ndn:/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")
+    .setMaxSuffixComponents(1);
+  BOOST_CHECK_EQUAL(find(), 1);
+
+  startInterest("ndn:/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")
+    .setMaxSuffixComponents(2);
+  BOOST_CHECK_EQUAL(find(), 2);
+
+  startInterest("ndn:/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")
+    .setMaxSuffixComponents(3);
+  BOOST_CHECK_EQUAL(find(), 3);
+
+  startInterest("ndn:/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")
+    .setMaxSuffixComponents(4);
+  BOOST_CHECK_EQUAL(find(), 4);
+
+  startInterest("ndn:/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")
+    .setMaxSuffixComponents(5);
+  BOOST_CHECK_EQUAL(find(), 5);
+
+  startInterest("ndn:/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")
+    .setMaxSuffixComponents(6);
+  BOOST_CHECK_EQUAL(find(), 6);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // Find
+
+BOOST_AUTO_TEST_CASE(Iterator)
+{
+  Cs cs;
+
+  Name nameA("/A");
+  Name nameAB("/A/B");
+  Name nameABC("/A/B/C");
+  Name nameD("/D");
+
+  BOOST_CHECK_EQUAL(cs.size(), 0);
+  BOOST_CHECK(cs.begin() == cs.end());
+
+  cs.insert(*makeData(nameABC));
+  BOOST_CHECK_EQUAL(cs.size(), 1);
+  BOOST_CHECK(cs.begin() != cs.end());
+  BOOST_CHECK(cs.begin()->getName() == nameABC);
+  BOOST_CHECK((*cs.begin()).getName() == nameABC);
+
+  auto i = cs.begin();
+  auto j = cs.begin();
+  BOOST_CHECK(++i == cs.end());
+  BOOST_CHECK(j++ == cs.begin());
+  BOOST_CHECK(j == cs.end());
+
+  cs.insert(*makeData(nameA));
+  cs.insert(*makeData(nameAB));
+  cs.insert(*makeData(nameD));
+
+  std::set<Name> expected = {nameA, nameAB, nameABC, nameD};
+  std::set<Name> actual;
+  for (const auto& csEntry : cs) {
+    actual.insert(csEntry.getName());
+  }
+  BOOST_CHECK_EQUAL_COLLECTIONS(actual.begin(), actual.end(), expected.begin(), expected.end());
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TableCs
+
+} // namespace tests
+} // namespace nfd
diff --git a/NFD/tests/daemon/table/dead-nonce-list.cpp b/NFD/tests/daemon/table/dead-nonce-list.cpp
new file mode 100644
index 0000000..970d537
--- /dev/null
+++ b/NFD/tests/daemon/table/dead-nonce-list.cpp
@@ -0,0 +1,162 @@
+/* -*- 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/>.
+ */
+
+#include "table/dead-nonce-list.hpp"
+
+#include "tests/test-common.hpp"
+#include "tests/limited-io.hpp"
+
+namespace nfd {
+namespace tests {
+
+BOOST_FIXTURE_TEST_SUITE(TableDeadNonceList, BaseFixture)
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+  Name nameA("ndn:/A");
+  Name nameB("ndn:/B");
+  const uint32_t nonce1 = 0x53b4eaa8;
+  const uint32_t nonce2 = 0x1f46372b;
+
+  DeadNonceList dnl;
+  BOOST_CHECK_EQUAL(dnl.size(), 0);
+  BOOST_CHECK_EQUAL(dnl.has(nameA, nonce1), false);
+
+  dnl.add(nameA, nonce1);
+  BOOST_CHECK_EQUAL(dnl.size(), 1);
+  BOOST_CHECK_EQUAL(dnl.has(nameA, nonce1), true);
+  BOOST_CHECK_EQUAL(dnl.has(nameA, nonce2), false);
+  BOOST_CHECK_EQUAL(dnl.has(nameB, nonce1), false);
+}
+
+BOOST_AUTO_TEST_CASE(MinLifetime)
+{
+  BOOST_CHECK_THROW(DeadNonceList dnl(time::milliseconds::zero()), std::invalid_argument);
+}
+
+/// A Fixture that periodically inserts Nonces
+class PeriodicalInsertionFixture : public BaseFixture
+{
+protected:
+  PeriodicalInsertionFixture()
+    : dnl(LIFETIME)
+    , name("ndn:/N")
+    , addNonceBatch(0)
+    , addNonceInterval(LIFETIME / DeadNonceList::EXPECTED_MARK_COUNT)
+  {
+    this->addNonce();
+  }
+
+  ~PeriodicalInsertionFixture()
+  {
+    scheduler::cancel(addNonceEvent);
+  }
+
+  void
+  setRate(size_t nNoncesPerLifetime)
+  {
+    addNonceBatch = nNoncesPerLifetime / DeadNonceList::EXPECTED_MARK_COUNT;
+  }
+
+  void
+  addNonce()
+  {
+    for (size_t i = 0; i < addNonceBatch; ++i) {
+      dnl.add(name, ++lastNonce);
+    }
+
+    scheduler::cancel(addNonceEvent); // avoid double schedules
+    if (addNonceInterval > time::nanoseconds::zero()) {
+      addNonceEvent = scheduler::schedule(addNonceInterval,
+                                          bind(&PeriodicalInsertionFixture::addNonce, this));
+    }
+  }
+
+protected:
+  static const time::nanoseconds LIFETIME;
+  DeadNonceList dnl;
+  Name name;
+  uint32_t lastNonce;
+  size_t addNonceBatch;
+  time::nanoseconds addNonceInterval;
+  scheduler::EventId addNonceEvent;
+};
+const time::nanoseconds PeriodicalInsertionFixture::LIFETIME = time::milliseconds(200);
+
+BOOST_FIXTURE_TEST_CASE(Lifetime, PeriodicalInsertionFixture)
+{
+  BOOST_CHECK_EQUAL(dnl.getLifetime(), LIFETIME);
+
+  LimitedIo limitedIo;
+
+  const int RATE = DeadNonceList::INITIAL_CAPACITY / 2;
+  this->setRate(RATE);
+  limitedIo.defer(LIFETIME * 10);
+
+  Name nameC("ndn:/C");
+  const uint32_t nonceC = 0x25390656;
+  BOOST_CHECK_EQUAL(dnl.has(nameC, nonceC), false);
+  dnl.add(nameC, nonceC);
+  BOOST_CHECK_EQUAL(dnl.has(nameC, nonceC), true);
+
+  limitedIo.defer(LIFETIME / 2); // -50%, entry should exist
+  BOOST_CHECK_EQUAL(dnl.has(nameC, nonceC), true);
+
+  limitedIo.defer(LIFETIME); // +50%, entry should be gone
+  BOOST_CHECK_EQUAL(dnl.has(nameC, nonceC), false);
+}
+
+BOOST_FIXTURE_TEST_CASE(CapacityDown, PeriodicalInsertionFixture)
+{
+  LimitedIo limitedIo;
+
+  ssize_t cap0 = dnl.m_capacity;
+
+  const int RATE = DeadNonceList::INITIAL_CAPACITY / 3;
+  this->setRate(RATE);
+  limitedIo.defer(LIFETIME * 10);
+
+  ssize_t cap1 = dnl.m_capacity;
+  BOOST_CHECK_LT(std::abs(cap1 - RATE), std::abs(cap0 - RATE));
+}
+
+BOOST_FIXTURE_TEST_CASE(CapacityUp, PeriodicalInsertionFixture)
+{
+  LimitedIo limitedIo;
+
+  ssize_t cap0 = dnl.m_capacity;
+
+  const int RATE = DeadNonceList::INITIAL_CAPACITY * 3;
+  this->setRate(RATE);
+  limitedIo.defer(LIFETIME * 10);
+
+  ssize_t cap1 = dnl.m_capacity;
+  BOOST_CHECK_LT(std::abs(cap1 - RATE), std::abs(cap0 - RATE));
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/NFD/tests/daemon/table/fib.cpp b/NFD/tests/daemon/table/fib.cpp
new file mode 100644
index 0000000..d75088b
--- /dev/null
+++ b/NFD/tests/daemon/table/fib.cpp
@@ -0,0 +1,436 @@
+/* -*- 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/>.
+ */
+
+#include "table/fib.hpp"
+#include "tests/daemon/face/dummy-face.hpp"
+
+#include "tests/test-common.hpp"
+
+namespace nfd {
+namespace tests {
+
+BOOST_FIXTURE_TEST_SUITE(TableFib, BaseFixture)
+
+BOOST_AUTO_TEST_CASE(Entry)
+{
+  Name prefix("ndn:/pxWhfFza");
+  shared_ptr<Face> face1 = make_shared<DummyFace>();
+  shared_ptr<Face> face2 = make_shared<DummyFace>();
+
+  fib::Entry entry(prefix);
+  BOOST_CHECK_EQUAL(entry.getPrefix(), prefix);
+
+  const fib::NextHopList& nexthops1 = entry.getNextHops();
+  // []
+  BOOST_CHECK_EQUAL(nexthops1.size(), 0);
+
+  entry.addNextHop(face1, 20);
+  const fib::NextHopList& nexthops2 = entry.getNextHops();
+  // [(face1,20)]
+  BOOST_CHECK_EQUAL(nexthops2.size(), 1);
+  BOOST_CHECK_EQUAL(nexthops2.begin()->getFace(), face1);
+  BOOST_CHECK_EQUAL(nexthops2.begin()->getCost(), 20);
+
+  entry.addNextHop(face1, 30);
+  const fib::NextHopList& nexthops3 = entry.getNextHops();
+  // [(face1,30)]
+  BOOST_CHECK_EQUAL(nexthops3.size(), 1);
+  BOOST_CHECK_EQUAL(nexthops3.begin()->getFace(), face1);
+  BOOST_CHECK_EQUAL(nexthops3.begin()->getCost(), 30);
+
+  entry.addNextHop(face2, 40);
+  const fib::NextHopList& nexthops4 = entry.getNextHops();
+  // [(face1,30), (face2,40)]
+  BOOST_CHECK_EQUAL(nexthops4.size(), 2);
+  int i = -1;
+  for (fib::NextHopList::const_iterator it = nexthops4.begin();
+    it != nexthops4.end(); ++it) {
+    ++i;
+    switch (i) {
+      case 0 :
+        BOOST_CHECK_EQUAL(it->getFace(), face1);
+        BOOST_CHECK_EQUAL(it->getCost(), 30);
+        break;
+      case 1 :
+        BOOST_CHECK_EQUAL(it->getFace(), face2);
+        BOOST_CHECK_EQUAL(it->getCost(), 40);
+        break;
+    }
+  }
+
+  entry.addNextHop(face2, 10);
+  const fib::NextHopList& nexthops5 = entry.getNextHops();
+  // [(face2,10), (face1,30)]
+  BOOST_CHECK_EQUAL(nexthops5.size(), 2);
+  i = -1;
+  for (fib::NextHopList::const_iterator it = nexthops5.begin();
+    it != nexthops5.end(); ++it) {
+    ++i;
+    switch (i) {
+      case 0 :
+        BOOST_CHECK_EQUAL(it->getFace(), face2);
+        BOOST_CHECK_EQUAL(it->getCost(), 10);
+        break;
+      case 1 :
+        BOOST_CHECK_EQUAL(it->getFace(), face1);
+        BOOST_CHECK_EQUAL(it->getCost(), 30);
+        break;
+    }
+  }
+
+  entry.removeNextHop(face1);
+  const fib::NextHopList& nexthops6 = entry.getNextHops();
+  // [(face2,10)]
+  BOOST_CHECK_EQUAL(nexthops6.size(), 1);
+  BOOST_CHECK_EQUAL(nexthops6.begin()->getFace(), face2);
+  BOOST_CHECK_EQUAL(nexthops6.begin()->getCost(), 10);
+
+  entry.removeNextHop(face1);
+  const fib::NextHopList& nexthops7 = entry.getNextHops();
+  // [(face2,10)]
+  BOOST_CHECK_EQUAL(nexthops7.size(), 1);
+  BOOST_CHECK_EQUAL(nexthops7.begin()->getFace(), face2);
+  BOOST_CHECK_EQUAL(nexthops7.begin()->getCost(), 10);
+
+  entry.removeNextHop(face2);
+  const fib::NextHopList& nexthops8 = entry.getNextHops();
+  // []
+  BOOST_CHECK_EQUAL(nexthops8.size(), 0);
+
+  entry.removeNextHop(face2);
+  const fib::NextHopList& nexthops9 = entry.getNextHops();
+  // []
+  BOOST_CHECK_EQUAL(nexthops9.size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(Insert_LongestPrefixMatch)
+{
+  Name nameEmpty;
+  Name nameA   ("ndn:/A");
+  Name nameAB  ("ndn:/A/B");
+  Name nameABC ("ndn:/A/B/C");
+  Name nameABCD("ndn:/A/B/C/D");
+  Name nameE   ("ndn:/E");
+
+  std::pair<shared_ptr<fib::Entry>, bool> insertRes;
+  shared_ptr<fib::Entry> entry;
+
+  NameTree nameTree;
+  Fib fib(nameTree);
+  // []
+  BOOST_CHECK_EQUAL(fib.size(), 0);
+
+  entry = fib.findLongestPrefixMatch(nameA);
+  BOOST_REQUIRE(static_cast<bool>(entry)); // the empty entry
+
+  insertRes = fib.insert(nameEmpty);
+  BOOST_CHECK_EQUAL(insertRes.second, true);
+  BOOST_CHECK_EQUAL(insertRes.first->getPrefix(), nameEmpty);
+  // ['/']
+  BOOST_CHECK_EQUAL(fib.size(), 1);
+
+  entry = fib.findLongestPrefixMatch(nameA);
+  BOOST_REQUIRE(static_cast<bool>(entry));
+  BOOST_CHECK_EQUAL(entry->getPrefix(), nameEmpty);
+
+  insertRes = fib.insert(nameA);
+  BOOST_CHECK_EQUAL(insertRes.second, true);
+  BOOST_CHECK_EQUAL(insertRes.first->getPrefix(), nameA);
+  // ['/', '/A']
+  BOOST_CHECK_EQUAL(fib.size(), 2);
+
+  insertRes = fib.insert(nameA);
+  BOOST_CHECK_EQUAL(insertRes.second, false);
+  BOOST_CHECK_EQUAL(insertRes.first->getPrefix(), nameA);
+  // ['/', '/A']
+  BOOST_CHECK_EQUAL(fib.size(), 2);
+
+  entry = fib.findLongestPrefixMatch(nameA);
+  BOOST_REQUIRE(static_cast<bool>(entry));
+  BOOST_CHECK_EQUAL(entry->getPrefix(), nameA);
+
+  entry = fib.findLongestPrefixMatch(nameABCD);
+  BOOST_REQUIRE(static_cast<bool>(entry));
+  BOOST_CHECK_EQUAL(entry->getPrefix(), nameA);
+
+  insertRes = fib.insert(nameABC);
+  BOOST_CHECK_EQUAL(insertRes.second, true);
+  BOOST_CHECK_EQUAL(insertRes.first->getPrefix(), nameABC);
+  // ['/', '/A', '/A/B/C']
+  BOOST_CHECK_EQUAL(fib.size(), 3);
+
+  entry = fib.findLongestPrefixMatch(nameA);
+  BOOST_REQUIRE(static_cast<bool>(entry));
+  BOOST_CHECK_EQUAL(entry->getPrefix(), nameA);
+
+  entry = fib.findLongestPrefixMatch(nameAB);
+  BOOST_REQUIRE(static_cast<bool>(entry));
+  BOOST_CHECK_EQUAL(entry->getPrefix(), nameA);
+
+  entry = fib.findLongestPrefixMatch(nameABCD);
+  BOOST_REQUIRE(static_cast<bool>(entry));
+  BOOST_CHECK_EQUAL(entry->getPrefix(), nameABC);
+
+  entry = fib.findLongestPrefixMatch(nameE);
+  BOOST_REQUIRE(static_cast<bool>(entry));
+  BOOST_CHECK_EQUAL(entry->getPrefix(), nameEmpty);
+}
+
+BOOST_AUTO_TEST_CASE(RemoveNextHopFromAllEntries)
+{
+  shared_ptr<Face> face1 = make_shared<DummyFace>();
+  shared_ptr<Face> face2 = make_shared<DummyFace>();
+  Name nameEmpty("ndn:/");
+  Name nameA("ndn:/A");
+  Name nameB("ndn:/B");
+
+  std::pair<shared_ptr<fib::Entry>, bool> insertRes;
+  shared_ptr<fib::Entry> entry;
+
+  NameTree nameTree;
+  Fib fib(nameTree);
+  // {}
+
+  insertRes = fib.insert(nameA);
+  insertRes.first->addNextHop(face1, 0);
+  insertRes.first->addNextHop(face2, 0);
+  // {'/A':[1,2]}
+
+  insertRes = fib.insert(nameB);
+  insertRes.first->addNextHop(face1, 0);
+  // {'/A':[1,2], '/B':[1]}
+  BOOST_CHECK_EQUAL(fib.size(), 2);
+
+  insertRes = fib.insert("/C");
+  insertRes.first->addNextHop(face2, 1);
+  // {'/A':[1,2], '/B':[1], '/C':[2]}
+  BOOST_CHECK_EQUAL(fib.size(), 3);
+
+  insertRes = fib.insert("/B/1");
+  insertRes.first->addNextHop(face1, 0);
+  // {'/A':[1,2], '/B':[1], '/B/1':[1], '/C':[2]}
+  BOOST_CHECK_EQUAL(fib.size(), 4);
+
+  insertRes = fib.insert("/B/1/2");
+  insertRes.first->addNextHop(face1, 0);
+  // {'/A':[1,2], '/B':[1], '/B/1':[1], '/B/1/2':[1], '/C':[2]}
+  BOOST_CHECK_EQUAL(fib.size(), 5);
+
+  insertRes = fib.insert("/B/1/2/3");
+  insertRes.first->addNextHop(face1, 0);
+  // {'/A':[1,2], '/B':[1], '/B/1':[1], '/B/1/2':[1], '/B/1/3':[1], '/C':[2]}
+  BOOST_CHECK_EQUAL(fib.size(), 6);
+
+  insertRes = fib.insert("/B/1/2/3/4");
+  insertRes.first->addNextHop(face1, 0);
+  // {'/A':[1,2], '/B':[1], '/B/1':[1], '/B/1/2':[1], '/B/1/2/3':[1], '/B/1/2/3/4':[1], '/C':[2]}
+  BOOST_CHECK_EQUAL(fib.size(), 7);
+
+  /////////////
+
+  fib.removeNextHopFromAllEntries(face1);
+  // {'/A':[2], '/C':[2]}
+  BOOST_CHECK_EQUAL(fib.size(), 2);
+
+  entry = fib.findLongestPrefixMatch(nameA);
+  BOOST_CHECK_EQUAL(entry->getPrefix(), nameA);
+  const fib::NextHopList& nexthopsA = entry->getNextHops();
+  BOOST_CHECK_EQUAL(nexthopsA.size(), 1);
+  BOOST_CHECK_EQUAL(nexthopsA.begin()->getFace(), face2);
+
+  entry = fib.findLongestPrefixMatch(nameB);
+  BOOST_CHECK_EQUAL(entry->getPrefix(), nameEmpty);
+}
+
+BOOST_AUTO_TEST_CASE(RemoveNextHopFromManyEntries)
+{
+  NameTree nameTree(16);
+  Fib fib(nameTree);
+  shared_ptr<Face> face1 = make_shared<DummyFace>();
+
+  for (uint64_t i = 0; i < 300; ++i) {
+    shared_ptr<fib::Entry> entry = fib.insert(Name("/P").appendVersion(i)).first;
+    entry->addNextHop(face1, 0);
+  }
+  BOOST_CHECK_EQUAL(fib.size(), 300);
+
+  fib.removeNextHopFromAllEntries(face1);
+  BOOST_CHECK_EQUAL(fib.size(), 0);
+}
+
+void
+validateFindExactMatch(const Fib& fib, const Name& target)
+{
+  shared_ptr<fib::Entry> entry = fib.findExactMatch(target);
+  if (static_cast<bool>(entry))
+    {
+      BOOST_CHECK_EQUAL(entry->getPrefix(), target);
+    }
+  else
+    {
+      BOOST_FAIL("No entry found for " << target);
+    }
+}
+
+void
+validateNoExactMatch(const Fib& fib, const Name& target)
+{
+  shared_ptr<fib::Entry> entry = fib.findExactMatch(target);
+  if (static_cast<bool>(entry))
+    {
+      BOOST_FAIL("Found unexpected entry for " << target);
+    }
+}
+
+BOOST_AUTO_TEST_CASE(FindExactMatch)
+{
+  NameTree nameTree;
+  Fib fib(nameTree);
+  fib.insert("/A");
+  fib.insert("/A/B");
+  fib.insert("/A/B/C");
+
+  validateFindExactMatch(fib, "/A");
+  validateFindExactMatch(fib, "/A/B");
+  validateFindExactMatch(fib, "/A/B/C");
+  validateNoExactMatch(fib, "/");
+
+  validateNoExactMatch(fib, "/does/not/exist");
+
+  NameTree gapNameTree;
+  Fib gapFib(nameTree);
+  fib.insert("/X");
+  fib.insert("/X/Y/Z");
+
+  validateNoExactMatch(gapFib, "/X/Y");
+
+  NameTree emptyNameTree;
+  Fib emptyFib(emptyNameTree);
+  validateNoExactMatch(emptyFib, "/nothing/here");
+}
+
+void
+validateErase(Fib& fib, const Name& target)
+{
+  fib.erase(target);
+
+  shared_ptr<fib::Entry> entry = fib.findExactMatch(target);
+  if (static_cast<bool>(entry))
+    {
+      BOOST_FAIL("Found \"removed\" entry for " << target);
+    }
+}
+
+BOOST_AUTO_TEST_CASE(Erase)
+{
+  NameTree emptyNameTree;
+  Fib emptyFib(emptyNameTree);
+
+  emptyFib.erase("/does/not/exist"); // crash test
+
+  validateErase(emptyFib, "/");
+
+  emptyFib.erase("/still/does/not/exist"); // crash test
+
+  NameTree nameTree;
+  Fib fib(nameTree);
+  fib.insert("/");
+  fib.insert("/A");
+  fib.insert("/A/B");
+  fib.insert("/A/B/C");
+
+  // check if we remove the right thing and leave
+  // everything else alone
+
+  validateErase(fib, "/A/B");
+  validateFindExactMatch(fib, "/A");
+  validateFindExactMatch(fib, "/A/B/C");
+  validateFindExactMatch(fib, "/");
+
+  validateErase(fib, "/A/B/C");
+  validateFindExactMatch(fib, "/A");
+  validateFindExactMatch(fib, "/");
+
+  validateErase(fib, "/A");
+  validateFindExactMatch(fib, "/");
+
+  validateErase(fib, "/");
+  validateNoExactMatch(fib, "/");
+
+  NameTree gapNameTree;
+  Fib gapFib(gapNameTree);
+  gapFib.insert("/X");
+  gapFib.insert("/X/Y/Z");
+
+  gapFib.erase("/X/Y"); //should do nothing
+  validateFindExactMatch(gapFib, "/X");
+  validateFindExactMatch(gapFib, "/X/Y/Z");
+}
+
+BOOST_AUTO_TEST_CASE(EraseNameTreeEntry)
+{
+  NameTree nameTree;
+  Fib fib(nameTree);
+  size_t nNameTreeEntriesBefore = nameTree.size();
+
+  fib.insert("ndn:/A/B/C");
+  fib.erase("ndn:/A/B/C");
+  BOOST_CHECK_EQUAL(nameTree.size(), nNameTreeEntriesBefore);
+}
+
+BOOST_AUTO_TEST_CASE(Iterator)
+{
+  NameTree nameTree;
+  Fib fib(nameTree);
+  Name nameA("/A");
+  Name nameAB("/A/B");
+  Name nameABC("/A/B/C");
+  Name nameRoot("/");
+
+  fib.insert(nameA);
+  fib.insert(nameAB);
+  fib.insert(nameABC);
+  fib.insert(nameRoot);
+
+  std::set<Name> expected;
+  expected.insert(nameA);
+  expected.insert(nameAB);
+  expected.insert(nameABC);
+  expected.insert(nameRoot);
+
+  for (Fib::const_iterator it = fib.begin(); it != fib.end(); it++)
+  {
+    bool isInSet = expected.find(it->getPrefix()) != expected.end();
+    BOOST_CHECK(isInSet);
+    expected.erase(it->getPrefix());
+  }
+
+  BOOST_CHECK_EQUAL(expected.size(), 0);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/NFD/tests/daemon/table/measurements-accessor.cpp b/NFD/tests/daemon/table/measurements-accessor.cpp
new file mode 100644
index 0000000..8668368
--- /dev/null
+++ b/NFD/tests/daemon/table/measurements-accessor.cpp
@@ -0,0 +1,100 @@
+/* -*- 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/>.
+ */
+
+#include "table/measurements-accessor.hpp"
+#include "fw/strategy.hpp"
+
+#include "tests/test-common.hpp"
+#include "../fw/dummy-strategy.hpp"
+
+namespace nfd {
+namespace tests {
+
+BOOST_FIXTURE_TEST_SUITE(TableMeasurementsAccessor, BaseFixture)
+
+class MeasurementsAccessorTestStrategy : public DummyStrategy
+{
+public:
+  MeasurementsAccessorTestStrategy(Forwarder& forwarder, const Name& name)
+    : DummyStrategy(forwarder, name)
+  {
+  }
+
+  virtual
+  ~MeasurementsAccessorTestStrategy()
+  {
+  }
+
+public: // accessors
+  MeasurementsAccessor&
+  getMeasurements_accessor()
+  {
+    return this->getMeasurements();
+  }
+};
+
+BOOST_AUTO_TEST_CASE(Access)
+{
+  Forwarder forwarder;
+
+  auto strategy1 = make_shared<MeasurementsAccessorTestStrategy>(ref(forwarder), "ndn:/strategy1");
+  auto strategy2 = make_shared<MeasurementsAccessorTestStrategy>(ref(forwarder), "ndn:/strategy2");
+
+  Name nameRoot("ndn:/");
+  Name nameA   ("ndn:/A");
+  Name nameAB  ("ndn:/A/B");
+  Name nameABC ("ndn:/A/B/C");
+  Name nameAD  ("ndn:/A/D");
+
+  StrategyChoice& strategyChoice = forwarder.getStrategyChoice();
+  strategyChoice.install(strategy1);
+  strategyChoice.install(strategy2);
+  strategyChoice.insert(nameRoot, strategy1->getName());
+  strategyChoice.insert(nameA   , strategy2->getName());
+  strategyChoice.insert(nameAB  , strategy1->getName());
+
+  MeasurementsAccessor& accessor1 = strategy1->getMeasurements_accessor();
+  MeasurementsAccessor& accessor2 = strategy2->getMeasurements_accessor();
+
+  BOOST_CHECK_EQUAL(static_cast<bool>(accessor1.get(nameRoot)), true);
+  BOOST_CHECK_EQUAL(static_cast<bool>(accessor1.get(nameA   )), false);
+  BOOST_CHECK_EQUAL(static_cast<bool>(accessor1.get(nameAB  )), true);
+  BOOST_CHECK_EQUAL(static_cast<bool>(accessor1.get(nameABC )), true);
+  BOOST_CHECK_EQUAL(static_cast<bool>(accessor1.get(nameAD  )), false);
+
+  shared_ptr<measurements::Entry> entryRoot = forwarder.getMeasurements().get(nameRoot);
+  BOOST_CHECK_NO_THROW(accessor1.getParent(*entryRoot));
+
+  BOOST_CHECK_EQUAL(static_cast<bool>(accessor2.get(nameRoot)), false);
+  BOOST_CHECK_EQUAL(static_cast<bool>(accessor2.get(nameA   )), true);
+  BOOST_CHECK_EQUAL(static_cast<bool>(accessor2.get(nameAB  )), false);
+  BOOST_CHECK_EQUAL(static_cast<bool>(accessor2.get(nameABC )), false);
+  BOOST_CHECK_EQUAL(static_cast<bool>(accessor2.get(nameAD  )), true);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/NFD/tests/daemon/table/measurements.cpp b/NFD/tests/daemon/table/measurements.cpp
new file mode 100644
index 0000000..63254bd
--- /dev/null
+++ b/NFD/tests/daemon/table/measurements.cpp
@@ -0,0 +1,130 @@
+/* -*- 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/>.
+ */
+
+#include "table/measurements.hpp"
+
+#include "tests/test-common.hpp"
+#include "tests/limited-io.hpp"
+
+namespace nfd {
+namespace tests {
+
+BOOST_FIXTURE_TEST_SUITE(TableMeasurements, BaseFixture)
+
+BOOST_AUTO_TEST_CASE(Get_Parent)
+{
+  NameTree nameTree;
+  Measurements measurements(nameTree);
+
+  Name name0;
+  Name nameA ("ndn:/A");
+  Name nameAB("ndn:/A/B");
+
+  shared_ptr<measurements::Entry> entryAB = measurements.get(nameAB);
+  BOOST_REQUIRE(entryAB != nullptr);
+  BOOST_CHECK_EQUAL(entryAB->getName(), nameAB);
+
+  shared_ptr<measurements::Entry> entry0 = measurements.get(name0);
+  BOOST_REQUIRE(entry0 != nullptr);
+
+  shared_ptr<measurements::Entry> entryA = measurements.getParent(*entryAB);
+  BOOST_REQUIRE(entryA != nullptr);
+  BOOST_CHECK_EQUAL(entryA->getName(), nameA);
+
+  shared_ptr<measurements::Entry> entry0c = measurements.getParent(*entryA);
+  BOOST_CHECK_EQUAL(entry0, entry0c);
+}
+
+BOOST_FIXTURE_TEST_CASE(Lifetime, UnitTestTimeFixture)
+{
+  NameTree nameTree;
+  Measurements measurements(nameTree);
+  Name nameA("ndn:/A");
+  Name nameB("ndn:/B");
+  Name nameC("ndn:/C");
+
+  BOOST_CHECK_EQUAL(measurements.size(), 0);
+
+  shared_ptr<measurements::Entry> entryA = measurements.get(nameA);
+  shared_ptr<measurements::Entry> entryB = measurements.get(nameB);
+  shared_ptr<measurements::Entry> entryC = measurements.get(nameC);
+  BOOST_CHECK_EQUAL(measurements.size(), 3);
+
+  const time::nanoseconds EXTEND_A = time::seconds(2);
+  const time::nanoseconds CHECK1 = time::seconds(3);
+  const time::nanoseconds CHECK2 = time::seconds(5);
+  const time::nanoseconds EXTEND_C = time::seconds(6);
+  const time::nanoseconds CHECK3 = time::seconds(7);
+  BOOST_ASSERT(EXTEND_A < CHECK1);
+  BOOST_ASSERT(CHECK1 < Measurements::getInitialLifetime());
+  BOOST_ASSERT(Measurements::getInitialLifetime() < CHECK2);
+  BOOST_ASSERT(CHECK2 < EXTEND_C);
+  BOOST_ASSERT(EXTEND_C < CHECK3);
+
+  measurements.extendLifetime(*entryA, EXTEND_A);
+  measurements.extendLifetime(*entryC, EXTEND_C);
+  // remaining lifetime:
+  //   A = initial lifetime, because it's extended by less duration
+  //   B = initial lifetime
+  //   C = EXTEND_C
+  entryA.reset();
+  entryB.reset();
+  entryC.reset();
+
+  this->advanceClocks(time::milliseconds(100), CHECK1);
+  BOOST_CHECK(measurements.findExactMatch(nameA) != nullptr);
+  BOOST_CHECK(measurements.findExactMatch(nameB) != nullptr);
+  BOOST_CHECK(measurements.findExactMatch(nameC) != nullptr);
+  BOOST_CHECK_EQUAL(measurements.size(), 3);
+
+  this->advanceClocks(time::milliseconds(100), CHECK2 - CHECK1);
+  BOOST_CHECK(measurements.findExactMatch(nameA) == nullptr);
+  BOOST_CHECK(measurements.findExactMatch(nameB) == nullptr);
+  BOOST_CHECK(measurements.findExactMatch(nameC) != nullptr);
+  BOOST_CHECK_EQUAL(measurements.size(), 1);
+
+  this->advanceClocks(time::milliseconds(100), CHECK3 - CHECK2);
+  BOOST_CHECK(measurements.findExactMatch(nameA) == nullptr);
+  BOOST_CHECK(measurements.findExactMatch(nameB) == nullptr);
+  BOOST_CHECK(measurements.findExactMatch(nameC) == nullptr);
+  BOOST_CHECK_EQUAL(measurements.size(), 0);
+}
+
+BOOST_FIXTURE_TEST_CASE(EraseNameTreeEntry, UnitTestTimeFixture)
+{
+  NameTree nameTree;
+  Measurements measurements(nameTree);
+  size_t nNameTreeEntriesBefore = nameTree.size();
+
+  shared_ptr<measurements::Entry> entry = measurements.get("/A");
+  this->advanceClocks(Measurements::getInitialLifetime() + time::milliseconds(10));
+  BOOST_CHECK_EQUAL(measurements.size(), 0);
+  BOOST_CHECK_EQUAL(nameTree.size(), nNameTreeEntriesBefore);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/NFD/tests/daemon/table/name-tree.cpp b/NFD/tests/daemon/table/name-tree.cpp
new file mode 100644
index 0000000..9790596
--- /dev/null
+++ b/NFD/tests/daemon/table/name-tree.cpp
@@ -0,0 +1,591 @@
+/* -*- 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/>.
+ */
+
+#include "table/name-tree.hpp"
+#include <unordered_set>
+
+#include "tests/test-common.hpp"
+
+namespace nfd {
+namespace tests {
+
+using name_tree::Entry;
+
+BOOST_FIXTURE_TEST_SUITE(TableNameTree, BaseFixture)
+
+BOOST_AUTO_TEST_CASE(Hash)
+{
+  Name root("/");
+  root.wireEncode();
+  size_t hashValue = name_tree::computeHash(root);
+  BOOST_CHECK_EQUAL(hashValue, static_cast<size_t>(0));
+
+  Name prefix("/nohello/world/ndn/research");
+  prefix.wireEncode();
+  std::vector<size_t> hashSet = name_tree::computeHashSet(prefix);
+  BOOST_CHECK_EQUAL(hashSet.size(), prefix.size() + 1);
+}
+
+BOOST_AUTO_TEST_CASE(Entry)
+{
+  Name prefix("ndn:/named-data/research/abc/def/ghi");
+
+  shared_ptr<name_tree::Entry> npe = make_shared<name_tree::Entry>(prefix);
+  BOOST_CHECK_EQUAL(npe->getPrefix(), prefix);
+
+  // examine all the get methods
+
+  size_t hash = npe->getHash();
+  BOOST_CHECK_EQUAL(hash, static_cast<size_t>(0));
+
+  shared_ptr<name_tree::Entry> parent = npe->getParent();
+  BOOST_CHECK(!static_cast<bool>(parent));
+
+  std::vector<shared_ptr<name_tree::Entry> >& childList = npe->getChildren();
+  BOOST_CHECK_EQUAL(childList.size(), static_cast<size_t>(0));
+
+  shared_ptr<fib::Entry> fib = npe->getFibEntry();
+  BOOST_CHECK(!static_cast<bool>(fib));
+
+  const std::vector< shared_ptr<pit::Entry> >& pitList = npe->getPitEntries();
+  BOOST_CHECK_EQUAL(pitList.size(), static_cast<size_t>(0));
+
+  // examine all the set method
+
+  npe->setHash(static_cast<size_t>(12345));
+  BOOST_CHECK_EQUAL(npe->getHash(), static_cast<size_t>(12345));
+
+  Name parentName("ndn:/named-data/research/abc/def");
+  parent = make_shared<name_tree::Entry>(parentName);
+  npe->setParent(parent);
+  BOOST_CHECK_EQUAL(npe->getParent(), parent);
+
+  // Insert FIB
+
+  shared_ptr<fib::Entry> fibEntry(new fib::Entry(prefix));
+  shared_ptr<fib::Entry> fibEntryParent(new fib::Entry(parentName));
+
+  npe->setFibEntry(fibEntry);
+  BOOST_CHECK_EQUAL(npe->getFibEntry(), fibEntry);
+
+  npe->setFibEntry(shared_ptr<fib::Entry>());
+  BOOST_CHECK(!static_cast<bool>(npe->getFibEntry()));
+
+  // Insert a PIT
+
+  shared_ptr<pit::Entry> pitEntry(make_shared<pit::Entry>(*makeInterest(prefix)));
+  shared_ptr<pit::Entry> pitEntry2(make_shared<pit::Entry>(*makeInterest(parentName)));
+
+  npe->insertPitEntry(pitEntry);
+  BOOST_CHECK_EQUAL(npe->getPitEntries().size(), 1);
+
+  npe->insertPitEntry(pitEntry2);
+  BOOST_CHECK_EQUAL(npe->getPitEntries().size(), 2);
+
+  npe->erasePitEntry(pitEntry);
+  BOOST_CHECK_EQUAL(npe->getPitEntries().size(), 1);
+
+  npe->erasePitEntry(pitEntry2);
+  BOOST_CHECK_EQUAL(npe->getPitEntries().size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+  size_t nBuckets = 16;
+  NameTree nt(nBuckets);
+
+  BOOST_CHECK_EQUAL(nt.size(), 0);
+  BOOST_CHECK_EQUAL(nt.getNBuckets(), nBuckets);
+
+  Name nameABC("ndn:/a/b/c");
+  shared_ptr<name_tree::Entry> npeABC = nt.lookup(nameABC);
+  BOOST_CHECK_EQUAL(nt.size(), 4);
+
+  Name nameABD("/a/b/d");
+  shared_ptr<name_tree::Entry> npeABD = nt.lookup(nameABD);
+  BOOST_CHECK_EQUAL(nt.size(), 5);
+
+  Name nameAE("/a/e/");
+  shared_ptr<name_tree::Entry> npeAE = nt.lookup(nameAE);
+  BOOST_CHECK_EQUAL(nt.size(), 6);
+
+  Name nameF("/f");
+  shared_ptr<name_tree::Entry> npeF = nt.lookup(nameF);
+  BOOST_CHECK_EQUAL(nt.size(), 7);
+
+  // validate lookup() and findExactMatch()
+
+  Name nameAB ("/a/b");
+  BOOST_CHECK_EQUAL(npeABC->getParent(), nt.findExactMatch(nameAB));
+  BOOST_CHECK_EQUAL(npeABD->getParent(), nt.findExactMatch(nameAB));
+
+  Name nameA ("/a");
+  BOOST_CHECK_EQUAL(npeAE->getParent(), nt.findExactMatch(nameA));
+
+  Name nameRoot ("/");
+  BOOST_CHECK_EQUAL(npeF->getParent(), nt.findExactMatch(nameRoot));
+  BOOST_CHECK_EQUAL(nt.size(), 7);
+
+  Name name0("/does/not/exist");
+  shared_ptr<name_tree::Entry> npe0 = nt.findExactMatch(name0);
+  BOOST_CHECK(!static_cast<bool>(npe0));
+
+
+  // Longest Prefix Matching
+  shared_ptr<name_tree::Entry> temp;
+  Name nameABCLPM("/a/b/c/def/asdf/nlf");
+  temp = nt.findLongestPrefixMatch(nameABCLPM);
+  BOOST_CHECK_EQUAL(temp, nt.findExactMatch(nameABC));
+
+  Name nameABDLPM("/a/b/d/def/asdf/nlf");
+  temp = nt.findLongestPrefixMatch(nameABDLPM);
+  BOOST_CHECK_EQUAL(temp, nt.findExactMatch(nameABD));
+
+  Name nameABLPM("/a/b/hello/world");
+  temp = nt.findLongestPrefixMatch(nameABLPM);
+  BOOST_CHECK_EQUAL(temp, nt.findExactMatch(nameAB));
+
+  Name nameAELPM("/a/e/hello/world");
+  temp = nt.findLongestPrefixMatch(nameAELPM);
+  BOOST_CHECK_EQUAL(temp, nt.findExactMatch(nameAE));
+
+  Name nameALPM("/a/hello/world");
+  temp = nt.findLongestPrefixMatch(nameALPM);
+  BOOST_CHECK_EQUAL(temp, nt.findExactMatch(nameA));
+
+  Name nameFLPM("/f/hello/world");
+  temp = nt.findLongestPrefixMatch(nameFLPM);
+  BOOST_CHECK_EQUAL(temp, nt.findExactMatch(nameF));
+
+  Name nameRootLPM("/does_not_exist");
+  temp = nt.findLongestPrefixMatch(nameRootLPM);
+  BOOST_CHECK_EQUAL(temp, nt.findExactMatch(nameRoot));
+
+  bool eraseResult = false;
+  temp = nt.findExactMatch(nameABC);
+  if (static_cast<bool>(temp))
+    eraseResult = nt.
+  eraseEntryIfEmpty(temp);
+  BOOST_CHECK_EQUAL(nt.size(), 6);
+  BOOST_CHECK(!static_cast<bool>(nt.findExactMatch(nameABC)));
+  BOOST_CHECK_EQUAL(eraseResult, true);
+
+  eraseResult = false;
+  temp = nt.findExactMatch(nameABCLPM);
+  if (static_cast<bool>(temp))
+    eraseResult = nt.
+  eraseEntryIfEmpty(temp);
+  BOOST_CHECK(!static_cast<bool>(temp));
+  BOOST_CHECK_EQUAL(nt.size(), 6);
+  BOOST_CHECK_EQUAL(eraseResult, false);
+
+  // nt.dump(std::cout);
+
+  nt.lookup(nameABC);
+  BOOST_CHECK_EQUAL(nt.size(), 7);
+
+  eraseResult = false;
+  temp = nt.findExactMatch(nameABC);
+  if (static_cast<bool>(temp))
+    eraseResult = nt.
+  eraseEntryIfEmpty(temp);
+  BOOST_CHECK_EQUAL(nt.size(), 6);
+  BOOST_CHECK_EQUAL(eraseResult, true);
+  BOOST_CHECK(!static_cast<bool>(nt.findExactMatch(nameABC)));
+
+  BOOST_CHECK_EQUAL(nt.getNBuckets(), 16);
+
+  // should resize now
+  Name nameABCD("a/b/c/d");
+  nt.lookup(nameABCD);
+  Name nameABCDE("a/b/c/d/e");
+  nt.lookup(nameABCDE);
+  BOOST_CHECK_EQUAL(nt.size(), 9);
+  BOOST_CHECK_EQUAL(nt.getNBuckets(), 32);
+
+  // try to erase /a/b/c, should return false
+  temp = nt.findExactMatch(nameABC);
+  BOOST_CHECK_EQUAL(temp->getPrefix(), nameABC);
+  eraseResult = nt.
+  eraseEntryIfEmpty(temp);
+  BOOST_CHECK_EQUAL(eraseResult, false);
+  temp = nt.findExactMatch(nameABC);
+  BOOST_CHECK_EQUAL(temp->getPrefix(), nameABC);
+
+  temp = nt.findExactMatch(nameABD);
+  if (static_cast<bool>(temp))
+    nt.
+  eraseEntryIfEmpty(temp);
+  BOOST_CHECK_EQUAL(nt.size(), 8);
+}
+
+/** \brief verify a NameTree enumeration contains expected entries
+ *
+ *  Example:
+ *  \code{.cpp}
+ *  auto& enumerable = ...;
+ *  EnumerationVerifier(enumerable)
+ *    .expect("/A")
+ *    .expect("/B")
+ *    .end();
+ *  // enumerable must have /A /B and nothing else to pass the test.
+ *  \endcode
+ */
+class EnumerationVerifier : noncopyable
+{
+public:
+  template<typename Enumerable>
+  EnumerationVerifier(Enumerable&& enumerable)
+  {
+    for (const name_tree::Entry& entry : enumerable) {
+      const Name& name = entry.getPrefix();
+      BOOST_CHECK_MESSAGE(m_names.insert(name).second, "duplicate Name " << name);
+    }
+  }
+
+  EnumerationVerifier&
+  expect(const Name& name)
+  {
+    BOOST_CHECK_MESSAGE(m_names.erase(name) == 1, "missing Name " << name);
+    return *this;
+  }
+
+  void
+  end()
+  {
+    BOOST_CHECK_MESSAGE(m_names.empty(), "excess Names including " << *m_names.begin());
+  }
+
+private:
+  std::unordered_set<Name> m_names;
+};
+
+class EnumerationFixture : public BaseFixture
+{
+protected:
+  EnumerationFixture()
+    : nt(N_BUCKETS)
+  {
+    BOOST_CHECK_EQUAL(nt.size(), 0);
+    BOOST_CHECK_EQUAL(nt.getNBuckets(), N_BUCKETS);
+  }
+
+  void
+  insertAbAc()
+  {
+    nt.lookup("/a/b");
+    nt.lookup("/a/c");
+    BOOST_CHECK_EQUAL(nt.size(), 4);
+    // /, /a, /a/b, /a/c
+  }
+
+  void
+  insertAb1Ab2Ac1Ac2()
+  {
+    nt.lookup("/a/b/1");
+    nt.lookup("/a/b/2");
+    nt.lookup("/a/c/1");
+    nt.lookup("/a/c/2");
+    BOOST_CHECK_EQUAL(nt.size(), 8);
+    // /, /a, /a/b, /a/b/1, /a/b/2, /a/c, /a/c/1, /a/c/2
+  }
+
+protected:
+  static const size_t N_BUCKETS = 16;
+  NameTree nt;
+};
+const size_t EnumerationFixture::N_BUCKETS;
+
+BOOST_FIXTURE_TEST_CASE(IteratorFullEnumerate, EnumerationFixture)
+{
+  nt.lookup("/a/b/c");
+  BOOST_CHECK_EQUAL(nt.size(), 4);
+
+  nt.lookup("/a/b/d");
+  BOOST_CHECK_EQUAL(nt.size(), 5);
+
+  nt.lookup("/a/e");
+  BOOST_CHECK_EQUAL(nt.size(), 6);
+
+  nt.lookup("/f");
+  BOOST_CHECK_EQUAL(nt.size(), 7);
+
+  nt.lookup("/");
+  BOOST_CHECK_EQUAL(nt.size(), 7);
+
+  auto&& enumerable = nt.fullEnumerate();
+  EnumerationVerifier(enumerable)
+    .expect("/")
+    .expect("/a")
+    .expect("/a/b")
+    .expect("/a/b/c")
+    .expect("/a/b/d")
+    .expect("/a/e")
+    .expect("/f")
+    .end();
+}
+
+BOOST_FIXTURE_TEST_SUITE(IteratorPartialEnumerate, EnumerationFixture)
+
+BOOST_AUTO_TEST_CASE(Empty)
+{
+  auto&& enumerable = nt.partialEnumerate("/a");
+
+  EnumerationVerifier(enumerable)
+    .end();
+}
+
+BOOST_AUTO_TEST_CASE(NotIn)
+{
+  this->insertAbAc();
+
+  // Enumerate on some name that is not in nameTree
+  Name name0("/0");
+  auto&& enumerable = nt.partialEnumerate("/0");
+
+  EnumerationVerifier(enumerable)
+    .end();
+}
+
+BOOST_AUTO_TEST_CASE(OnlyA)
+{
+  this->insertAbAc();
+
+  // Accept "root" nameA only
+  auto&& enumerable = nt.partialEnumerate("/a", [] (const name_tree::Entry& entry) {
+    return std::make_pair(entry.getPrefix() == "/a", true);
+  });
+
+  EnumerationVerifier(enumerable)
+    .expect("/a")
+    .end();
+}
+
+BOOST_AUTO_TEST_CASE(ExceptA)
+{
+  this->insertAbAc();
+
+  // Accept anything except "root" nameA
+  auto&& enumerable = nt.partialEnumerate("/a", [] (const name_tree::Entry& entry) {
+    return std::make_pair(entry.getPrefix() != "/a", true);
+  });
+
+  EnumerationVerifier(enumerable)
+    .expect("/a/b")
+    .expect("/a/c")
+    .end();
+}
+
+BOOST_AUTO_TEST_CASE(NoNameANoSubTreeAB)
+{
+  this->insertAb1Ab2Ac1Ac2();
+
+  // No NameA
+  // No SubTree from NameAB
+  auto&& enumerable = nt.partialEnumerate("/a", [] (const name_tree::Entry& entry) {
+      return std::make_pair(entry.getPrefix() != "/a", entry.getPrefix() != "/a/b");
+    });
+
+  EnumerationVerifier(enumerable)
+    .expect("/a/b")
+    .expect("/a/c")
+    .expect("/a/c/1")
+    .expect("/a/c/2")
+    .end();
+}
+
+BOOST_AUTO_TEST_CASE(NoNameANoSubTreeAC)
+{
+  this->insertAb1Ab2Ac1Ac2();
+
+  // No NameA
+  // No SubTree from NameAC
+  auto&& enumerable = nt.partialEnumerate("/a", [] (const name_tree::Entry& entry) {
+      return std::make_pair(entry.getPrefix() != "/a", entry.getPrefix() != "/a/c");
+    });
+
+  EnumerationVerifier(enumerable)
+    .expect("/a/b")
+    .expect("/a/b/1")
+    .expect("/a/b/2")
+    .expect("/a/c")
+    .end();
+}
+
+BOOST_AUTO_TEST_CASE(NoSubTreeA)
+{
+  this->insertAb1Ab2Ac1Ac2();
+
+  // No Subtree from NameA
+  auto&& enumerable = nt.partialEnumerate("/a", [] (const name_tree::Entry& entry) {
+      return std::make_pair(true, entry.getPrefix() != "/a");
+    });
+
+  EnumerationVerifier(enumerable)
+    .expect("/a")
+    .end();
+}
+
+BOOST_AUTO_TEST_CASE(Example)
+{
+  // Example
+  // /
+  // /A
+  // /A/B x
+  // /A/B/C
+  // /A/D x
+  // /E
+  // /F
+
+  nt.lookup("/A");
+  nt.lookup("/A/B");
+  nt.lookup("/A/B/C");
+  nt.lookup("/A/D");
+  nt.lookup("/E");
+  nt.lookup("/F");
+
+  auto&& enumerable = nt.partialEnumerate("/A",
+    [] (const name_tree::Entry& entry) -> std::pair<bool, bool> {
+      bool visitEntry = false;
+      bool visitChildren = false;
+
+      Name name = entry.getPrefix();
+
+      if (name == "/" || name == "/A/B" || name == "/A/B/C" || name == "/A/D") {
+        visitEntry = true;
+      }
+
+      if (name == "/" || name == "/A" || name == "/F") {
+        visitChildren = true;
+      }
+
+      return {visitEntry, visitChildren};
+    });
+
+  EnumerationVerifier(enumerable)
+    .expect("/A/B")
+    .expect("/A/D")
+    .end();
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+BOOST_FIXTURE_TEST_CASE(IteratorFindAllMatches, EnumerationFixture)
+{
+  nt.lookup("/a/b/c/d/e/f");
+  nt.lookup("/a/a/c");
+  nt.lookup("/a/a/d/1");
+  nt.lookup("/a/a/d/2");
+  BOOST_CHECK_EQUAL(nt.size(), 12);
+
+  auto&& allMatches = nt.findAllMatches("/a/b/c/d/e");
+
+  EnumerationVerifier(allMatches)
+    .expect("/")
+    .expect("/a")
+    .expect("/a/b")
+    .expect("/a/b/c")
+    .expect("/a/b/c/d")
+    .expect("/a/b/c/d/e")
+    .end();
+}
+
+BOOST_AUTO_TEST_CASE(HashTableResizeShrink)
+{
+  size_t nBuckets = 16;
+  NameTree nameTree(nBuckets);
+
+  Name prefix("/a/b/c/d/e/f/g/h"); // requires 9 buckets
+
+  shared_ptr<name_tree::Entry> entry = nameTree.lookup(prefix);
+  BOOST_CHECK_EQUAL(nameTree.size(), 9);
+  BOOST_CHECK_EQUAL(nameTree.getNBuckets(), 32);
+
+  nameTree.eraseEntryIfEmpty(entry);
+  BOOST_CHECK_EQUAL(nameTree.size(), 0);
+  BOOST_CHECK_EQUAL(nameTree.getNBuckets(), 16);
+}
+
+// .lookup should not invalidate iterator
+BOOST_AUTO_TEST_CASE(SurvivedIteratorAfterLookup)
+{
+  NameTree nt;
+  nt.lookup("/A/B/C");
+  nt.lookup("/E");
+
+  Name nameB("/A/B");
+  std::set<Name> seenNames;
+  for (NameTree::const_iterator it = nt.begin(); it != nt.end(); ++it) {
+    BOOST_CHECK(seenNames.insert(it->getPrefix()).second);
+    if (it->getPrefix() == nameB) {
+      nt.lookup("/D");
+    }
+  }
+
+  BOOST_CHECK_EQUAL(seenNames.count("/"), 1);
+  BOOST_CHECK_EQUAL(seenNames.count("/A"), 1);
+  BOOST_CHECK_EQUAL(seenNames.count("/A/B"), 1);
+  BOOST_CHECK_EQUAL(seenNames.count("/A/B/C"), 1);
+  BOOST_CHECK_EQUAL(seenNames.count("/E"), 1);
+
+  seenNames.erase("/D"); // /D may or may not appear
+  BOOST_CHECK(seenNames.size() == 5);
+}
+
+// .eraseEntryIfEmpty should not invalidate iterator
+BOOST_AUTO_TEST_CASE(SurvivedIteratorAfterErase)
+{
+  NameTree nt;
+  nt.lookup("/A/B/C");
+  nt.lookup("/A/D/E");
+  nt.lookup("/A/F/G");
+  nt.lookup("/H");
+
+  Name nameD("/A/D");
+  std::set<Name> seenNames;
+  for (NameTree::const_iterator it = nt.begin(); it != nt.end(); ++it) {
+    BOOST_CHECK(seenNames.insert(it->getPrefix()).second);
+    if (it->getPrefix() == nameD) {
+      nt.eraseEntryIfEmpty(nt.findExactMatch("/A/F/G")); // /A/F/G and /A/F are erased
+    }
+  }
+
+  BOOST_CHECK_EQUAL(seenNames.count("/"), 1);
+  BOOST_CHECK_EQUAL(seenNames.count("/A"), 1);
+  BOOST_CHECK_EQUAL(seenNames.count("/A/B"), 1);
+  BOOST_CHECK_EQUAL(seenNames.count("/A/B/C"), 1);
+  BOOST_CHECK_EQUAL(seenNames.count("/A/D"), 1);
+  BOOST_CHECK_EQUAL(seenNames.count("/A/D/E"), 1);
+  BOOST_CHECK_EQUAL(seenNames.count("/H"), 1);
+
+  seenNames.erase("/A/F"); // /A/F may or may not appear
+  seenNames.erase("/A/F/G"); // /A/F/G may or may not appear
+  BOOST_CHECK(seenNames.size() == 7);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/NFD/tests/daemon/table/pit.cpp b/NFD/tests/daemon/table/pit.cpp
new file mode 100644
index 0000000..cabbab9
--- /dev/null
+++ b/NFD/tests/daemon/table/pit.cpp
@@ -0,0 +1,498 @@
+/* -*- 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/>.
+ */
+
+#include "table/pit.hpp"
+#include "tests/daemon/face/dummy-face.hpp"
+
+#include "tests/test-common.hpp"
+
+namespace nfd {
+namespace tests {
+
+BOOST_FIXTURE_TEST_SUITE(TablePit, BaseFixture)
+
+BOOST_AUTO_TEST_CASE(EntryInOutRecords)
+{
+  shared_ptr<Face> face1 = make_shared<DummyFace>();
+  shared_ptr<Face> face2 = make_shared<DummyFace>();
+  Name name("ndn:/KuYfjtRq");
+  shared_ptr<Interest> interest  = makeInterest(name);
+  shared_ptr<Interest> interest1 = makeInterest(name);
+  interest1->setInterestLifetime(time::milliseconds(2528));
+  interest1->setNonce(25559);
+  shared_ptr<Interest> interest2 = makeInterest(name);
+  interest2->setInterestLifetime(time::milliseconds(6464));
+  interest2->setNonce(19004);
+  shared_ptr<Interest> interest3 = makeInterest(name);
+  interest3->setInterestLifetime(time::milliseconds(3585));
+  interest3->setNonce(24216);
+  shared_ptr<Interest> interest4 = makeInterest(name);
+  interest4->setInterestLifetime(time::milliseconds(8795));
+  interest4->setNonce(17365);
+
+  pit::Entry entry(*interest);
+
+  BOOST_CHECK_EQUAL(entry.getInterest().getName(), name);
+  BOOST_CHECK_EQUAL(entry.getName(), name);
+
+  const pit::InRecordCollection& inRecords1 = entry.getInRecords();
+  BOOST_CHECK_EQUAL(inRecords1.size(), 0);
+  const pit::OutRecordCollection& outRecords1 = entry.getOutRecords();
+  BOOST_CHECK_EQUAL(outRecords1.size(), 0);
+
+  // insert InRecord
+  time::steady_clock::TimePoint before1 = time::steady_clock::now();
+  pit::InRecordCollection::iterator in1 =
+    entry.insertOrUpdateInRecord(face1, *interest1);
+  time::steady_clock::TimePoint after1 = time::steady_clock::now();
+  const pit::InRecordCollection& inRecords2 = entry.getInRecords();
+  BOOST_CHECK_EQUAL(inRecords2.size(), 1);
+  BOOST_CHECK(in1 == inRecords2.begin());
+  BOOST_CHECK_EQUAL(in1->getFace(), face1);
+  BOOST_CHECK_EQUAL(in1->getLastNonce(), interest1->getNonce());
+  BOOST_CHECK_GE(in1->getLastRenewed(), before1);
+  BOOST_CHECK_LE(in1->getLastRenewed(), after1);
+  BOOST_CHECK_LE(in1->getExpiry() - in1->getLastRenewed()
+                 - interest1->getInterestLifetime(),
+                 (after1 - before1));
+  BOOST_CHECK(in1 == entry.getInRecord(*face1));
+
+  // insert OutRecord
+  time::steady_clock::TimePoint before2 = time::steady_clock::now();
+  pit::OutRecordCollection::iterator out1 =
+    entry.insertOrUpdateOutRecord(face1, *interest1);
+  time::steady_clock::TimePoint after2 = time::steady_clock::now();
+  const pit::OutRecordCollection& outRecords2 = entry.getOutRecords();
+  BOOST_CHECK_EQUAL(outRecords2.size(), 1);
+  BOOST_CHECK(out1 == outRecords2.begin());
+  BOOST_CHECK_EQUAL(out1->getFace(), face1);
+  BOOST_CHECK_EQUAL(out1->getLastNonce(), interest1->getNonce());
+  BOOST_CHECK_GE(out1->getLastRenewed(), before2);
+  BOOST_CHECK_LE(out1->getLastRenewed(), after2);
+  BOOST_CHECK_LE(out1->getExpiry() - out1->getLastRenewed()
+                 - interest1->getInterestLifetime(),
+                 (after2 - before2));
+  BOOST_CHECK(out1 == entry.getOutRecord(*face1));
+
+  // update InRecord
+  time::steady_clock::TimePoint before3 = time::steady_clock::now();
+  pit::InRecordCollection::iterator in2 =
+    entry.insertOrUpdateInRecord(face1, *interest2);
+  time::steady_clock::TimePoint after3 = time::steady_clock::now();
+  const pit::InRecordCollection& inRecords3 = entry.getInRecords();
+  BOOST_CHECK_EQUAL(inRecords3.size(), 1);
+  BOOST_CHECK(in2 == inRecords3.begin());
+  BOOST_CHECK_EQUAL(in2->getFace(), face1);
+  BOOST_CHECK_EQUAL(in2->getLastNonce(), interest2->getNonce());
+  BOOST_CHECK_LE(in2->getExpiry() - in2->getLastRenewed()
+                 - interest2->getInterestLifetime(),
+                 (after3 - before3));
+
+  // insert another InRecord
+  pit::InRecordCollection::iterator in3 =
+    entry.insertOrUpdateInRecord(face2, *interest3);
+  const pit::InRecordCollection& inRecords4 = entry.getInRecords();
+  BOOST_CHECK_EQUAL(inRecords4.size(), 2);
+  BOOST_CHECK_EQUAL(in3->getFace(), face2);
+
+  // get InRecord
+  pit::InRecordCollection::const_iterator in4 = entry.getInRecord(*face1);
+  BOOST_REQUIRE(in4 != entry.getInRecords().end());
+  BOOST_CHECK_EQUAL(in4->getFace(), face1);
+
+  // delete all InRecords
+  entry.deleteInRecords();
+  const pit::InRecordCollection& inRecords5 = entry.getInRecords();
+  BOOST_CHECK_EQUAL(inRecords5.size(), 0);
+  BOOST_CHECK(entry.getInRecord(*face1) == entry.getInRecords().end());
+
+  // insert another OutRecord
+  pit::OutRecordCollection::iterator out2 =
+    entry.insertOrUpdateOutRecord(face2, *interest4);
+  const pit::OutRecordCollection& outRecords3 = entry.getOutRecords();
+  BOOST_CHECK_EQUAL(outRecords3.size(), 2);
+  BOOST_CHECK_EQUAL(out2->getFace(), face2);
+
+  // get OutRecord
+  pit::OutRecordCollection::const_iterator out3 = entry.getOutRecord(*face1);
+  BOOST_REQUIRE(out3 != entry.getOutRecords().end());
+  BOOST_CHECK_EQUAL(out3->getFace(), face1);
+
+  // delete OutRecord
+  entry.deleteOutRecord(*face2);
+  const pit::OutRecordCollection& outRecords4 = entry.getOutRecords();
+  BOOST_REQUIRE_EQUAL(outRecords4.size(), 1);
+  BOOST_CHECK_EQUAL(outRecords4.begin()->getFace(), face1);
+  BOOST_CHECK(entry.getOutRecord(*face2) == entry.getOutRecords().end());
+}
+
+BOOST_AUTO_TEST_CASE(EntryNonce)
+{
+  shared_ptr<Face> face1 = make_shared<DummyFace>();
+  shared_ptr<Face> face2 = make_shared<DummyFace>();
+
+  shared_ptr<Interest> interest = makeInterest("ndn:/qtCQ7I1c");
+  interest->setNonce(25559);
+
+  pit::Entry entry0(*interest);
+  BOOST_CHECK_EQUAL(entry0.findNonce(25559, *face1), pit::DUPLICATE_NONCE_NONE);
+  BOOST_CHECK_EQUAL(entry0.findNonce(25559, *face2), pit::DUPLICATE_NONCE_NONE);
+  BOOST_CHECK_EQUAL(entry0.findNonce(19004, *face1), pit::DUPLICATE_NONCE_NONE);
+  BOOST_CHECK_EQUAL(entry0.findNonce(19004, *face2), pit::DUPLICATE_NONCE_NONE);
+
+  pit::Entry entry1(*interest);
+  entry1.insertOrUpdateInRecord(face1, *interest);
+  BOOST_CHECK_EQUAL(entry1.findNonce(25559, *face1), pit::DUPLICATE_NONCE_IN_SAME);
+  BOOST_CHECK_EQUAL(entry1.findNonce(25559, *face2), pit::DUPLICATE_NONCE_IN_OTHER);
+  BOOST_CHECK_EQUAL(entry1.findNonce(19004, *face1), pit::DUPLICATE_NONCE_NONE);
+  BOOST_CHECK_EQUAL(entry1.findNonce(19004, *face2), pit::DUPLICATE_NONCE_NONE);
+
+  pit::Entry entry2(*interest);
+  entry2.insertOrUpdateOutRecord(face1, *interest);
+  BOOST_CHECK_EQUAL(entry2.findNonce(25559, *face1), pit::DUPLICATE_NONCE_OUT_SAME);
+  BOOST_CHECK_EQUAL(entry2.findNonce(25559, *face2), pit::DUPLICATE_NONCE_OUT_OTHER);
+  BOOST_CHECK_EQUAL(entry2.findNonce(19004, *face1), pit::DUPLICATE_NONCE_NONE);
+  BOOST_CHECK_EQUAL(entry2.findNonce(19004, *face2), pit::DUPLICATE_NONCE_NONE);
+
+  pit::Entry entry3(*interest);
+  entry3.insertOrUpdateInRecord(face1, *interest);
+  entry3.insertOrUpdateOutRecord(face1, *interest);
+  BOOST_CHECK_EQUAL(entry3.findNonce(25559, *face1),
+                    pit::DUPLICATE_NONCE_IN_SAME | pit::DUPLICATE_NONCE_OUT_SAME);
+  BOOST_CHECK_EQUAL(entry3.findNonce(25559, *face2),
+                    pit::DUPLICATE_NONCE_IN_OTHER | pit::DUPLICATE_NONCE_OUT_OTHER);
+  BOOST_CHECK_EQUAL(entry3.findNonce(19004, *face1), pit::DUPLICATE_NONCE_NONE);
+  BOOST_CHECK_EQUAL(entry3.findNonce(19004, *face2), pit::DUPLICATE_NONCE_NONE);
+
+  pit::Entry entry4(*interest);
+  entry4.insertOrUpdateInRecord(face1, *interest);
+  entry4.insertOrUpdateInRecord(face2, *interest);
+  BOOST_CHECK_EQUAL(entry4.findNonce(25559, *face1),
+                    pit::DUPLICATE_NONCE_IN_SAME | pit::DUPLICATE_NONCE_IN_OTHER);
+  BOOST_CHECK_EQUAL(entry4.findNonce(25559, *face2),
+                    pit::DUPLICATE_NONCE_IN_SAME | pit::DUPLICATE_NONCE_IN_OTHER);
+  BOOST_CHECK_EQUAL(entry4.findNonce(19004, *face1), pit::DUPLICATE_NONCE_NONE);
+  BOOST_CHECK_EQUAL(entry4.findNonce(19004, *face2), pit::DUPLICATE_NONCE_NONE);
+
+  pit::Entry entry5(*interest);
+  entry5.insertOrUpdateOutRecord(face1, *interest);
+  entry5.insertOrUpdateOutRecord(face2, *interest);
+  BOOST_CHECK_EQUAL(entry5.findNonce(25559, *face1),
+                    pit::DUPLICATE_NONCE_OUT_SAME | pit::DUPLICATE_NONCE_OUT_OTHER);
+  BOOST_CHECK_EQUAL(entry5.findNonce(25559, *face2),
+                    pit::DUPLICATE_NONCE_OUT_SAME | pit::DUPLICATE_NONCE_OUT_OTHER);
+  BOOST_CHECK_EQUAL(entry5.findNonce(19004, *face1), pit::DUPLICATE_NONCE_NONE);
+  BOOST_CHECK_EQUAL(entry5.findNonce(19004, *face2), pit::DUPLICATE_NONCE_NONE);
+}
+
+BOOST_AUTO_TEST_CASE(EntryLifetime)
+{
+  shared_ptr<Interest> interest = makeInterest("ndn:/7oIEurbgy6");
+  // library uses -1 to indicate unset lifetime
+  BOOST_ASSERT(interest->getInterestLifetime() < time::milliseconds::zero());
+
+  shared_ptr<Face> face = make_shared<DummyFace>();
+  pit::Entry entry(*interest);
+
+  pit::InRecordCollection::iterator inIt = entry.insertOrUpdateInRecord(face, *interest);
+  BOOST_CHECK_GT(inIt->getExpiry(), time::steady_clock::now());
+
+  pit::OutRecordCollection::iterator outIt = entry.insertOrUpdateOutRecord(face, *interest);
+  BOOST_CHECK_GT(outIt->getExpiry(), time::steady_clock::now());
+}
+
+BOOST_AUTO_TEST_CASE(EntryCanForwardTo)
+{
+  shared_ptr<Interest> interest = makeInterest("ndn:/WDsuBLIMG");
+  pit::Entry entry(*interest);
+
+  shared_ptr<Face> face1 = make_shared<DummyFace>();
+  shared_ptr<Face> face2 = make_shared<DummyFace>();
+
+  entry.insertOrUpdateInRecord(face1, *interest);
+  BOOST_CHECK_EQUAL(entry.canForwardTo(*face1), false);
+  BOOST_CHECK_EQUAL(entry.canForwardTo(*face2), true);
+
+  entry.insertOrUpdateInRecord(face2, *interest);
+  BOOST_CHECK_EQUAL(entry.canForwardTo(*face1), true);
+  BOOST_CHECK_EQUAL(entry.canForwardTo(*face2), true);
+
+  entry.insertOrUpdateOutRecord(face1, *interest);
+  BOOST_CHECK_EQUAL(entry.canForwardTo(*face1), false);
+  BOOST_CHECK_EQUAL(entry.canForwardTo(*face2), true);
+}
+
+BOOST_AUTO_TEST_CASE(Insert)
+{
+  Name name1("ndn:/5vzBNnMst");
+  Name name2("ndn:/igSGfEIM62");
+  Exclude exclude1;
+  exclude1.excludeOne(Name::Component("u26p47oep"));
+  Exclude exclude2;
+  exclude2.excludeBefore(Name::Component("u26p47oep"));
+  ndn::KeyLocator keyLocator1("ndn:/sGAE3peMHA");
+  ndn::KeyLocator keyLocator2("ndn:/nIJH6pr4");
+
+  NameTree nameTree(16);
+  Pit pit(nameTree);
+  BOOST_CHECK_EQUAL(pit.size(), 0);
+  std::pair<shared_ptr<pit::Entry>, bool> insertResult;
+
+  // base
+  shared_ptr<Interest> interestA = make_shared<Interest>(name1);
+  insertResult = pit.insert(*interestA);
+  BOOST_CHECK_EQUAL(insertResult.second, true);
+  BOOST_CHECK_EQUAL(pit.size(), 1);
+
+  // A+MinSuffixComponents
+  shared_ptr<Interest> interestB = make_shared<Interest>(*interestA);
+  interestB->setMinSuffixComponents(2);
+  insertResult = pit.insert(*interestB);
+  BOOST_CHECK_EQUAL(insertResult.second, true);
+  BOOST_CHECK_EQUAL(pit.size(), 2);
+
+  // A+MaxSuffixComponents
+  shared_ptr<Interest> interestC = make_shared<Interest>(*interestA);
+  interestC->setMaxSuffixComponents(4);
+  insertResult = pit.insert(*interestC);
+  BOOST_CHECK_EQUAL(insertResult.second, true);
+  BOOST_CHECK_EQUAL(pit.size(), 3);
+
+  // A+KeyLocator1
+  shared_ptr<Interest> interestD = make_shared<Interest>(*interestA);
+  interestD->setPublisherPublicKeyLocator(keyLocator1);
+  insertResult = pit.insert(*interestD);
+  BOOST_CHECK_EQUAL(insertResult.second, true);
+  BOOST_CHECK_EQUAL(pit.size(), 4);
+
+  // A+KeyLocator2
+  shared_ptr<Interest> interestE = make_shared<Interest>(*interestA);
+  interestE->setPublisherPublicKeyLocator(keyLocator2);
+  insertResult = pit.insert(*interestE);
+  BOOST_CHECK_EQUAL(insertResult.second, true);
+  BOOST_CHECK_EQUAL(pit.size(), 5);
+
+  // A+Exclude1
+  shared_ptr<Interest> interestF = make_shared<Interest>(*interestA);
+  interestF->setExclude(exclude1);
+  insertResult = pit.insert(*interestF);
+  BOOST_CHECK_EQUAL(insertResult.second, true);
+  BOOST_CHECK_EQUAL(pit.size(), 6);
+
+  // A+Exclude2
+  shared_ptr<Interest> interestG = make_shared<Interest>(*interestA);
+  interestG->setExclude(exclude2);
+  insertResult = pit.insert(*interestG);
+  BOOST_CHECK_EQUAL(insertResult.second, true);
+  BOOST_CHECK_EQUAL(pit.size(), 7);
+
+  // A+ChildSelector0
+  shared_ptr<Interest> interestH = make_shared<Interest>(*interestA);
+  interestH->setChildSelector(0);
+  insertResult = pit.insert(*interestH);
+  BOOST_CHECK_EQUAL(insertResult.second, true);
+  BOOST_CHECK_EQUAL(pit.size(), 8);
+
+  // A+ChildSelector1
+  shared_ptr<Interest> interestI = make_shared<Interest>(*interestA);
+  interestI->setChildSelector(1);
+  insertResult = pit.insert(*interestI);
+  BOOST_CHECK_EQUAL(insertResult.second, true);
+  BOOST_CHECK_EQUAL(pit.size(), 9);
+
+  // A+MustBeFresh
+  shared_ptr<Interest> interestJ = make_shared<Interest>(*interestA);
+  interestJ->setMustBeFresh(true);
+  insertResult = pit.insert(*interestJ);
+  BOOST_CHECK_EQUAL(insertResult.second, true);
+  BOOST_CHECK_EQUAL(pit.size(), 10);
+
+  // A+InterestLifetime
+  shared_ptr<Interest> interestK = make_shared<Interest>(*interestA);
+  interestK->setInterestLifetime(time::milliseconds(1000));
+  insertResult = pit.insert(*interestK);
+  BOOST_CHECK_EQUAL(insertResult.second, false);// only guiders differ
+  BOOST_CHECK_EQUAL(pit.size(), 10);
+
+  // A+Nonce
+  shared_ptr<Interest> interestL = make_shared<Interest>(*interestA);
+  interestL->setNonce(2192);
+  insertResult = pit.insert(*interestL);
+  BOOST_CHECK_EQUAL(insertResult.second, false);// only guiders differ
+  BOOST_CHECK_EQUAL(pit.size(), 10);
+
+  // different Name+Exclude1
+  shared_ptr<Interest> interestM = make_shared<Interest>(name2);
+  interestM->setExclude(exclude1);
+  insertResult = pit.insert(*interestM);
+  BOOST_CHECK_EQUAL(insertResult.second, true);
+  BOOST_CHECK_EQUAL(pit.size(), 11);
+}
+
+BOOST_AUTO_TEST_CASE(Erase)
+{
+  shared_ptr<Interest> interest = makeInterest("/z88Admz6A2");
+
+  NameTree nameTree(16);
+  Pit pit(nameTree);
+
+  std::pair<shared_ptr<pit::Entry>, bool> insertResult;
+
+  BOOST_CHECK_EQUAL(pit.size(), 0);
+
+  insertResult = pit.insert(*interest);
+  BOOST_CHECK_EQUAL(insertResult.second, true);
+  BOOST_CHECK_EQUAL(pit.size(), 1);
+
+  insertResult = pit.insert(*interest);
+  BOOST_CHECK_EQUAL(insertResult.second, false);
+  BOOST_CHECK_EQUAL(pit.size(), 1);
+
+  pit.erase(insertResult.first);
+  BOOST_CHECK_EQUAL(pit.size(), 0);
+
+  insertResult = pit.insert(*interest);
+  BOOST_CHECK_EQUAL(insertResult.second, true);
+  BOOST_CHECK_EQUAL(pit.size(), 1);
+
+}
+
+BOOST_AUTO_TEST_CASE(EraseNameTreeEntry)
+{
+  NameTree nameTree;
+  Pit pit(nameTree);
+  size_t nNameTreeEntriesBefore = nameTree.size();
+
+  shared_ptr<Interest> interest = makeInterest("/37xWVvQ2K");
+  shared_ptr<pit::Entry> entry = pit.insert(*interest).first;
+  pit.erase(entry);
+  BOOST_CHECK_EQUAL(nameTree.size(), nNameTreeEntriesBefore);
+}
+
+BOOST_AUTO_TEST_CASE(FindAllDataMatches)
+{
+  Name nameA   ("ndn:/A");
+  Name nameAB  ("ndn:/A/B");
+  Name nameABC ("ndn:/A/B/C");
+  Name nameABCD("ndn:/A/B/C/D");
+  Name nameD   ("ndn:/D");
+
+  shared_ptr<Interest> interestA   = makeInterest(nameA  );
+  shared_ptr<Interest> interestABC = makeInterest(nameABC);
+  shared_ptr<Interest> interestD   = makeInterest(nameD  );
+
+  NameTree nameTree(16);
+  Pit pit(nameTree);
+  int count = 0;
+
+  BOOST_CHECK_EQUAL(pit.size(), 0);
+
+  pit.insert(*interestA  );
+  pit.insert(*interestABC);
+  pit.insert(*interestD  );
+
+  nameTree.lookup(nameABCD); // make sure /A/B/C/D is in nameTree
+
+  BOOST_CHECK_EQUAL(pit.size(), 3);
+
+  shared_ptr<Data> data = makeData(nameABCD);
+
+  pit::DataMatchResult matches = pit.findAllDataMatches(*data);
+
+  bool hasA   = false;
+  bool hasAB  = false;
+  bool hasABC = false;
+  bool hasD   = false;
+
+  for (const shared_ptr<pit::Entry>& entry : matches) {
+    ++count;
+
+    if (entry->getName().equals(nameA ))
+      hasA   = true;
+
+    if (entry->getName().equals(nameAB))
+      hasAB  = true;
+
+    if (entry->getName().equals(nameABC))
+      hasABC = true;
+
+    if (entry->getName().equals(nameD))
+      hasD   = true;
+  }
+  BOOST_CHECK_EQUAL(hasA  , true);
+  BOOST_CHECK_EQUAL(hasAB , false);
+  BOOST_CHECK_EQUAL(hasABC, true);
+  BOOST_CHECK_EQUAL(hasD  , false);
+
+  BOOST_CHECK_EQUAL(count, 2);
+
+}
+
+BOOST_AUTO_TEST_CASE(Iterator)
+{
+  NameTree nameTree(16);
+  Pit pit(nameTree);
+
+  shared_ptr<Interest> interestA    = makeInterest("/A");
+  shared_ptr<Interest> interestABC1 = makeInterest("/A/B/C");
+  shared_ptr<Interest> interestABC2 = makeInterest("/A/B/C");
+  interestABC2->setSelectors(ndn::Selectors().setMinSuffixComponents(10));
+  shared_ptr<Interest> interestD    = makeInterest("/D");
+
+  BOOST_CHECK_EQUAL(pit.size(), 0);
+  BOOST_CHECK(pit.begin() == pit.end());
+
+  pit.insert(*interestABC1);
+  BOOST_CHECK_EQUAL(pit.size(), 1);
+  BOOST_CHECK(pit.begin() != pit.end());
+  BOOST_CHECK(pit.begin()->getInterest() == *interestABC1);
+  BOOST_CHECK((*pit.begin()).getInterest() == *interestABC1);
+
+  auto i = pit.begin();
+  auto j = pit.begin();
+  BOOST_CHECK(++i == pit.end());
+  BOOST_CHECK(j++ == pit.begin());
+  BOOST_CHECK(j == pit.end());
+
+  pit.insert(*interestA);
+  pit.insert(*interestABC2);
+  pit.insert(*interestD);
+
+  std::set<const Interest*> expected = {&*interestA, &*interestABC1, &*interestABC2, &*interestD};
+  std::set<const Interest*> actual;
+  for (const auto& pitEntry : pit) {
+    actual.insert(&pitEntry.getInterest());
+  }
+  BOOST_CHECK(actual == expected);
+  for (auto actualIt = actual.begin(), expectedIt = expected.begin();
+       actualIt != actual.end() && expectedIt != expected.end(); ++actualIt, ++expectedIt) {
+    BOOST_CHECK_EQUAL(**actualIt, **expectedIt);
+  }
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/NFD/tests/daemon/table/strategy-choice.cpp b/NFD/tests/daemon/table/strategy-choice.cpp
new file mode 100644
index 0000000..494c76b
--- /dev/null
+++ b/NFD/tests/daemon/table/strategy-choice.cpp
@@ -0,0 +1,311 @@
+/* -*- 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/>.
+ */
+
+#include "table/strategy-choice.hpp"
+#include "tests/daemon/fw/dummy-strategy.hpp"
+
+#include "tests/test-common.hpp"
+
+namespace nfd {
+namespace tests {
+
+BOOST_FIXTURE_TEST_SUITE(TableStrategyChoice, BaseFixture)
+
+using fw::Strategy;
+
+BOOST_AUTO_TEST_CASE(Get)
+{
+  Forwarder forwarder;
+  Name nameP("ndn:/strategy/P");
+  shared_ptr<Strategy> strategyP = make_shared<DummyStrategy>(ref(forwarder), nameP);
+
+  StrategyChoice& table = forwarder.getStrategyChoice();
+
+  // install
+  BOOST_CHECK_EQUAL(table.install(strategyP), true);
+
+  BOOST_CHECK(table.insert("ndn:/", nameP));
+  // { '/'=>P }
+
+  auto getRoot = table.get("ndn:/");
+  BOOST_CHECK_EQUAL(getRoot.first, true);
+  BOOST_CHECK_EQUAL(getRoot.second, nameP);
+
+  auto getA = table.get("ndn:/A");
+  BOOST_CHECK_EQUAL(getA.first, false);
+}
+
+BOOST_AUTO_TEST_CASE(Effective)
+{
+  Forwarder forwarder;
+  Name nameP("ndn:/strategy/P");
+  Name nameQ("ndn:/strategy/Q");
+  Name nameZ("ndn:/strategy/Z");
+  shared_ptr<Strategy> strategyP = make_shared<DummyStrategy>(ref(forwarder), nameP);
+  shared_ptr<Strategy> strategyQ = make_shared<DummyStrategy>(ref(forwarder), nameQ);
+
+  StrategyChoice& table = forwarder.getStrategyChoice();
+
+  // install
+  BOOST_CHECK_EQUAL(table.install(strategyP), true);
+  BOOST_CHECK_EQUAL(table.install(strategyQ), true);
+  BOOST_CHECK_EQUAL(table.install(strategyQ), false);
+
+  BOOST_CHECK(table.insert("ndn:/", nameP));
+  // { '/'=>P }
+
+  BOOST_CHECK_EQUAL(table.findEffectiveStrategy("ndn:/")   .getName(), nameP);
+  BOOST_CHECK_EQUAL(table.findEffectiveStrategy("ndn:/A")  .getName(), nameP);
+  BOOST_CHECK_EQUAL(table.findEffectiveStrategy("ndn:/A/B").getName(), nameP);
+
+  BOOST_CHECK(table.insert("ndn:/A/B", nameP));
+  // { '/'=>P, '/A/B'=>P }
+
+  BOOST_CHECK_EQUAL(table.findEffectiveStrategy("ndn:/")   .getName(), nameP);
+  BOOST_CHECK_EQUAL(table.findEffectiveStrategy("ndn:/A")  .getName(), nameP);
+  BOOST_CHECK_EQUAL(table.findEffectiveStrategy("ndn:/A/B").getName(), nameP);
+  // same instance
+  BOOST_CHECK_EQUAL(&table.findEffectiveStrategy("ndn:/"),    strategyP.get());
+  BOOST_CHECK_EQUAL(&table.findEffectiveStrategy("ndn:/A"),   strategyP.get());
+  BOOST_CHECK_EQUAL(&table.findEffectiveStrategy("ndn:/A/B"), strategyP.get());
+
+  table.erase("ndn:/A"); // no effect
+  // { '/'=>P, '/A/B'=>P }
+
+  BOOST_CHECK_EQUAL(table.findEffectiveStrategy("ndn:/")   .getName(), nameP);
+  BOOST_CHECK_EQUAL(table.findEffectiveStrategy("ndn:/A")  .getName(), nameP);
+  BOOST_CHECK_EQUAL(table.findEffectiveStrategy("ndn:/A/B").getName(), nameP);
+
+  BOOST_CHECK(table.insert("ndn:/A", nameQ));
+  // { '/'=>P, '/A/B'=>P, '/A'=>Q }
+
+  BOOST_CHECK_EQUAL(table.findEffectiveStrategy("ndn:/")   .getName(), nameP);
+  BOOST_CHECK_EQUAL(table.findEffectiveStrategy("ndn:/A")  .getName(), nameQ);
+  BOOST_CHECK_EQUAL(table.findEffectiveStrategy("ndn:/A/B").getName(), nameP);
+
+  table.erase("ndn:/A/B");
+  // { '/'=>P, '/A'=>Q }
+
+  BOOST_CHECK_EQUAL(table.findEffectiveStrategy("ndn:/")   .getName(), nameP);
+  BOOST_CHECK_EQUAL(table.findEffectiveStrategy("ndn:/A")  .getName(), nameQ);
+  BOOST_CHECK_EQUAL(table.findEffectiveStrategy("ndn:/A/B").getName(), nameQ);
+
+  BOOST_CHECK(!table.insert("ndn:/", nameZ)); // non existent strategy
+
+  BOOST_CHECK(table.insert("ndn:/", nameQ));
+  BOOST_CHECK(table.insert("ndn:/A", nameP));
+  // { '/'=>Q, '/A'=>P }
+
+  BOOST_CHECK_EQUAL(table.findEffectiveStrategy("ndn:/")   .getName(), nameQ);
+  BOOST_CHECK_EQUAL(table.findEffectiveStrategy("ndn:/A")  .getName(), nameP);
+  BOOST_CHECK_EQUAL(table.findEffectiveStrategy("ndn:/A/B").getName(), nameP);
+  BOOST_CHECK_EQUAL(table.findEffectiveStrategy("ndn:/D")  .getName(), nameQ);
+}
+
+//XXX BOOST_CONCEPT_ASSERT((ForwardIterator<std::vector<int>::iterator>))
+//    is also failing. There might be a problem with ForwardIterator concept checking.
+//BOOST_CONCEPT_ASSERT((ForwardIterator<StrategyChoice::const_iterator>));
+
+BOOST_AUTO_TEST_CASE(Enumerate)
+{
+
+  Forwarder forwarder;
+  Name nameP("ndn:/strategy/P");
+  Name nameQ("ndn:/strategy/Q");
+  shared_ptr<Strategy> strategyP = make_shared<DummyStrategy>(ref(forwarder), nameP);
+  shared_ptr<Strategy> strategyQ = make_shared<DummyStrategy>(ref(forwarder), nameQ);
+
+  StrategyChoice& table = forwarder.getStrategyChoice();
+  table.install(strategyP);
+  table.install(strategyQ);
+
+  table.insert("ndn:/",      nameP);
+  table.insert("ndn:/A/B",   nameQ);
+  table.insert("ndn:/A/B/C", nameP);
+  table.insert("ndn:/D",     nameP);
+  table.insert("ndn:/E",     nameQ);
+
+  BOOST_CHECK_EQUAL(table.size(), 5);
+
+  std::map<Name, Name> map; // namespace=>strategyName
+  for (StrategyChoice::const_iterator it = table.begin(); it != table.end(); ++it) {
+    map[it->getPrefix()] = it->getStrategyName();
+  }
+  BOOST_CHECK_EQUAL(map.size(), 5);
+  BOOST_CHECK_EQUAL(map["ndn:/"],      nameP);
+  BOOST_CHECK_EQUAL(map["ndn:/A/B"],   nameQ);
+  BOOST_CHECK_EQUAL(map["ndn:/A/B/C"], nameP);
+  BOOST_CHECK_EQUAL(map["ndn:/D"],     nameP);
+  BOOST_CHECK_EQUAL(map["ndn:/E"],     nameQ);
+  BOOST_CHECK_EQUAL(map.size(), 5);
+}
+
+class PStrategyInfo : public fw::StrategyInfo
+{
+public:
+  static constexpr int
+  getTypeId()
+  {
+    return 10;
+  }
+};
+
+BOOST_AUTO_TEST_CASE(ClearStrategyInfo)
+{
+  Forwarder forwarder;
+  Name nameP("ndn:/strategy/P");
+  Name nameQ("ndn:/strategy/Q");
+  shared_ptr<Strategy> strategyP = make_shared<DummyStrategy>(ref(forwarder), nameP);
+  shared_ptr<Strategy> strategyQ = make_shared<DummyStrategy>(ref(forwarder), nameQ);
+
+  StrategyChoice& table = forwarder.getStrategyChoice();
+  Measurements& measurements = forwarder.getMeasurements();
+
+  // install
+  table.install(strategyP);
+  table.install(strategyQ);
+
+  BOOST_CHECK(table.insert("ndn:/", nameP));
+  // { '/'=>P }
+  measurements.get("ndn:/")     ->getOrCreateStrategyInfo<PStrategyInfo>();
+  measurements.get("ndn:/A")    ->getOrCreateStrategyInfo<PStrategyInfo>();
+  measurements.get("ndn:/A/B")  ->getOrCreateStrategyInfo<PStrategyInfo>();
+  measurements.get("ndn:/A/C")  ->getOrCreateStrategyInfo<PStrategyInfo>();
+
+  BOOST_CHECK(table.insert("ndn:/A/B", nameP));
+  // { '/'=>P, '/A/B'=>P }
+  BOOST_CHECK( static_cast<bool>(measurements.get("ndn:/")   ->getStrategyInfo<PStrategyInfo>()));
+  BOOST_CHECK( static_cast<bool>(measurements.get("ndn:/A")  ->getStrategyInfo<PStrategyInfo>()));
+  BOOST_CHECK( static_cast<bool>(measurements.get("ndn:/A/B")->getStrategyInfo<PStrategyInfo>()));
+  BOOST_CHECK( static_cast<bool>(measurements.get("ndn:/A/C")->getStrategyInfo<PStrategyInfo>()));
+
+  BOOST_CHECK(table.insert("ndn:/A", nameQ));
+  // { '/'=>P, '/A/B'=>P, '/A'=>Q }
+  BOOST_CHECK( static_cast<bool>(measurements.get("ndn:/")   ->getStrategyInfo<PStrategyInfo>()));
+  BOOST_CHECK(!static_cast<bool>(measurements.get("ndn:/A")  ->getStrategyInfo<PStrategyInfo>()));
+  BOOST_CHECK( static_cast<bool>(measurements.get("ndn:/A/B")->getStrategyInfo<PStrategyInfo>()));
+  BOOST_CHECK(!static_cast<bool>(measurements.get("ndn:/A/C")->getStrategyInfo<PStrategyInfo>()));
+
+  table.erase("ndn:/A/B");
+  // { '/'=>P, '/A'=>Q }
+  BOOST_CHECK( static_cast<bool>(measurements.get("ndn:/")   ->getStrategyInfo<PStrategyInfo>()));
+  BOOST_CHECK(!static_cast<bool>(measurements.get("ndn:/A")  ->getStrategyInfo<PStrategyInfo>()));
+  BOOST_CHECK(!static_cast<bool>(measurements.get("ndn:/A/B")->getStrategyInfo<PStrategyInfo>()));
+  BOOST_CHECK(!static_cast<bool>(measurements.get("ndn:/A/C")->getStrategyInfo<PStrategyInfo>()));
+}
+
+BOOST_AUTO_TEST_CASE(EraseNameTreeEntry)
+{
+  Forwarder forwarder;
+  NameTree& nameTree = forwarder.getNameTree();
+  StrategyChoice& table = forwarder.getStrategyChoice();
+
+  Name nameP("ndn:/strategy/P");
+  Name nameQ("ndn:/strategy/Q");
+  shared_ptr<Strategy> strategyP = make_shared<DummyStrategy>(ref(forwarder), nameP);
+  shared_ptr<Strategy> strategyQ = make_shared<DummyStrategy>(ref(forwarder), nameQ);
+  table.install(strategyP);
+  table.install(strategyQ);
+
+  table.insert("ndn:/", nameP);
+
+  size_t nNameTreeEntriesBefore = nameTree.size();
+
+  table.insert("ndn:/A/B", nameQ);
+  table.erase("ndn:/A/B");
+  BOOST_CHECK_EQUAL(nameTree.size(), nNameTreeEntriesBefore);
+}
+
+BOOST_AUTO_TEST_CASE(Versioning)
+{
+  Forwarder forwarder;
+  Name nameP("ndn:/strategy/P");
+  Name nameP1("ndn:/strategy/P/%FD%01");
+  Name nameP2("ndn:/strategy/P/%FD%02");
+  Name name3("ndn:/%FD%03");
+  Name name4("ndn:/%FD%04");
+  Name nameQ("ndn:/strategy/Q");
+  Name nameQ5("ndn:/strategy/Q/%FD%05");
+  shared_ptr<Strategy> strategyP1 = make_shared<DummyStrategy>(ref(forwarder), nameP1);
+  shared_ptr<Strategy> strategyP2 = make_shared<DummyStrategy>(ref(forwarder), nameP2);
+  shared_ptr<Strategy> strategy3  = make_shared<DummyStrategy>(ref(forwarder), name3);
+  shared_ptr<Strategy> strategy4  = make_shared<DummyStrategy>(ref(forwarder), name4);
+  shared_ptr<Strategy> strategyQ  = make_shared<DummyStrategy>(ref(forwarder), nameQ);
+  shared_ptr<Strategy> strategyQ5 = make_shared<DummyStrategy>(ref(forwarder), nameQ5);
+
+  StrategyChoice& table = forwarder.getStrategyChoice();
+
+  // install
+  BOOST_CHECK_EQUAL(table.install(strategyP1), true);
+  BOOST_CHECK_EQUAL(table.install(strategyP1), false);
+  BOOST_CHECK_EQUAL(table.hasStrategy(nameP,  false), true);
+  BOOST_CHECK_EQUAL(table.hasStrategy(nameP,  true),  false);
+  BOOST_CHECK_EQUAL(table.hasStrategy(nameP1, true),  true);
+
+  BOOST_CHECK_EQUAL(table.install(strategyP2), true);
+  BOOST_CHECK_EQUAL(table.install(strategy3),  true);
+  BOOST_CHECK_EQUAL(table.install(strategy4),  true);
+  BOOST_CHECK_EQUAL(table.install(strategyQ),  true);
+  BOOST_CHECK_EQUAL(table.install(strategyQ5), true);
+
+  BOOST_CHECK(table.insert("ndn:/", nameQ));
+  // exact match, { '/'=>Q }
+  BOOST_CHECK_EQUAL(table.findEffectiveStrategy("ndn:/").getName(), nameQ);
+
+  BOOST_CHECK(table.insert("ndn:/", nameQ));
+  BOOST_CHECK(table.insert("ndn:/", nameP));
+  // { '/'=>P2 }
+  BOOST_CHECK_EQUAL(table.findEffectiveStrategy("ndn:/").getName(), nameP2);
+
+  BOOST_CHECK(table.insert("ndn:/", nameQ));
+  BOOST_CHECK(table.insert("ndn:/", nameP1));
+  // { '/'=>P1 }
+  BOOST_CHECK_EQUAL(table.findEffectiveStrategy("ndn:/").getName(), nameP1);
+
+  BOOST_CHECK(table.insert("ndn:/", nameQ));
+  BOOST_CHECK(table.insert("ndn:/", nameP2));
+  // { '/'=>P2 }
+  BOOST_CHECK_EQUAL(table.findEffectiveStrategy("ndn:/").getName(), nameP2);
+
+  BOOST_CHECK(table.insert("ndn:/", nameQ));
+  BOOST_CHECK(! table.insert("ndn:/", "ndn:/strategy/A"));
+  // not installed
+  BOOST_CHECK_EQUAL(table.findEffectiveStrategy("ndn:/").getName(), nameQ);
+
+  BOOST_CHECK(table.insert("ndn:/", nameQ));
+  BOOST_CHECK(! table.insert("ndn:/", "ndn:/strategy/Z"));
+  // not installed
+  BOOST_CHECK_EQUAL(table.findEffectiveStrategy("ndn:/").getName(), nameQ);
+
+  BOOST_CHECK(table.insert("ndn:/", nameP1));
+  BOOST_CHECK(table.insert("ndn:/", "ndn:/"));
+  // match one component longer only, { '/'=>4 }
+  BOOST_CHECK_EQUAL(table.findEffectiveStrategy("ndn:/").getName(), name4);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/NFD/tests/daemon/table/strategy-info-host.cpp b/NFD/tests/daemon/table/strategy-info-host.cpp
new file mode 100644
index 0000000..5c0c14f
--- /dev/null
+++ b/NFD/tests/daemon/table/strategy-info-host.cpp
@@ -0,0 +1,140 @@
+/* -*- 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/>.
+ */
+
+#include "table/strategy-info-host.hpp"
+
+#include "tests/test-common.hpp"
+
+namespace nfd {
+namespace tests {
+
+using fw::StrategyInfo;
+
+static int g_DummyStrategyInfo_count = 0;
+
+class DummyStrategyInfo : public StrategyInfo
+{
+public:
+  static constexpr int
+  getTypeId()
+  {
+    return 1;
+  }
+
+  DummyStrategyInfo(int id)
+    : m_id(id)
+  {
+    ++g_DummyStrategyInfo_count;
+  }
+
+  virtual
+  ~DummyStrategyInfo()
+  {
+    --g_DummyStrategyInfo_count;
+  }
+
+  int m_id;
+};
+
+class DummyStrategyInfo2 : public StrategyInfo
+{
+public:
+  static constexpr int
+  getTypeId()
+  {
+    return 2;
+  }
+
+  DummyStrategyInfo2(int id)
+    : m_id(id)
+  {
+  }
+
+  int m_id;
+};
+
+BOOST_FIXTURE_TEST_SUITE(TableStrategyInfoHost, BaseFixture)
+
+BOOST_AUTO_TEST_CASE(SetGetClear)
+{
+  StrategyInfoHost host;
+
+  BOOST_CHECK(host.getStrategyInfo<DummyStrategyInfo>() == nullptr);
+
+  g_DummyStrategyInfo_count = 0;
+
+  shared_ptr<DummyStrategyInfo> info = make_shared<DummyStrategyInfo>(7591);
+  host.setStrategyInfo(info);
+  BOOST_REQUIRE(host.getStrategyInfo<DummyStrategyInfo>() != nullptr);
+  BOOST_CHECK_EQUAL(host.getStrategyInfo<DummyStrategyInfo>()->m_id, 7591);
+
+  info.reset(); // unlink local reference
+  // host should still have a reference to info
+  BOOST_REQUIRE(host.getStrategyInfo<DummyStrategyInfo>() != nullptr);
+  BOOST_CHECK_EQUAL(host.getStrategyInfo<DummyStrategyInfo>()->m_id, 7591);
+
+  host.clearStrategyInfo();
+  BOOST_CHECK(host.getStrategyInfo<DummyStrategyInfo>() == nullptr);
+  BOOST_CHECK_EQUAL(g_DummyStrategyInfo_count, 0);
+}
+
+BOOST_AUTO_TEST_CASE(Create)
+{
+  StrategyInfoHost host;
+
+  host.getOrCreateStrategyInfo<DummyStrategyInfo>(3503);
+  BOOST_REQUIRE(host.getStrategyInfo<DummyStrategyInfo>() != nullptr);
+  BOOST_CHECK_EQUAL(host.getStrategyInfo<DummyStrategyInfo>()->m_id, 3503);
+
+  host.getOrCreateStrategyInfo<DummyStrategyInfo>(1032);
+  BOOST_REQUIRE(host.getStrategyInfo<DummyStrategyInfo>() != nullptr);
+  BOOST_CHECK_EQUAL(host.getStrategyInfo<DummyStrategyInfo>()->m_id, 3503);
+
+  host.setStrategyInfo<DummyStrategyInfo>(nullptr);
+  host.getOrCreateStrategyInfo<DummyStrategyInfo>(9956);
+  BOOST_REQUIRE(host.getStrategyInfo<DummyStrategyInfo>() != nullptr);
+  BOOST_CHECK_EQUAL(host.getStrategyInfo<DummyStrategyInfo>()->m_id, 9956);
+}
+
+BOOST_AUTO_TEST_CASE(Types)
+{
+  StrategyInfoHost host;
+
+  host.getOrCreateStrategyInfo<DummyStrategyInfo>(8063);
+  BOOST_REQUIRE(host.getStrategyInfo<DummyStrategyInfo>() != nullptr);
+  BOOST_CHECK_EQUAL(host.getStrategyInfo<DummyStrategyInfo>()->m_id, 8063);
+
+  host.getOrCreateStrategyInfo<DummyStrategyInfo2>(2871);
+  BOOST_REQUIRE(host.getStrategyInfo<DummyStrategyInfo2>() != nullptr);
+  BOOST_CHECK_EQUAL(host.getStrategyInfo<DummyStrategyInfo2>()->m_id, 2871);
+
+  BOOST_REQUIRE(host.getStrategyInfo<DummyStrategyInfo>() != nullptr);
+  BOOST_CHECK_EQUAL(host.getStrategyInfo<DummyStrategyInfo>()->m_id, 8063);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/NFD/tests/global-configuration.cpp b/NFD/tests/global-configuration.cpp
new file mode 100644
index 0000000..2b4ce9d
--- /dev/null
+++ b/NFD/tests/global-configuration.cpp
@@ -0,0 +1,53 @@
+/* -*- 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
+ *
+ * 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 "test-common.hpp"
+#include "core/logger.hpp"
+#include "core/config-file.hpp"
+
+#include <boost/filesystem.hpp>
+
+namespace nfd {
+namespace tests {
+
+class GlobalConfigurationFixture
+{
+public:
+  GlobalConfigurationFixture()
+  {
+    const std::string filename = "unit-tests.conf";
+    if (boost::filesystem::exists(filename))
+      {
+        ConfigFile config;
+        LoggerFactory::getInstance().setConfigFile(config);
+
+        config.parse(filename, false);
+      }
+  }
+};
+
+BOOST_GLOBAL_FIXTURE(GlobalConfigurationFixture)
+
+} // namespace tests
+} // namespace nfd
diff --git a/NFD/tests/identity-management-fixture.cpp b/NFD/tests/identity-management-fixture.cpp
new file mode 100644
index 0000000..c668f54
--- /dev/null
+++ b/NFD/tests/identity-management-fixture.cpp
@@ -0,0 +1,53 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2014 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "identity-management-fixture.hpp"
+
+namespace nfd {
+namespace tests {
+
+IdentityManagementFixture::IdentityManagementFixture()
+  : m_keyChain("sqlite3", "file")
+{
+}
+
+IdentityManagementFixture::~IdentityManagementFixture()
+{
+  for (auto&& id : m_identities) {
+    m_keyChain.deleteIdentity(id);
+  }
+}
+
+bool
+IdentityManagementFixture::addIdentity(const ndn::Name& identity, const ndn::KeyParams& params)
+{
+  try {
+    m_keyChain.createIdentity(identity, params);
+    m_identities.push_back(identity);
+    return true;
+  }
+  catch (std::runtime_error&) {
+    return false;
+  }
+}
+
+} // namespace tests
+} // namespace nfd
diff --git a/NFD/tests/identity-management-fixture.hpp b/NFD/tests/identity-management-fixture.hpp
new file mode 100644
index 0000000..265c3bc
--- /dev/null
+++ b/NFD/tests/identity-management-fixture.hpp
@@ -0,0 +1,56 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2014 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "tests/test-common.hpp"
+#include <ndn-cxx/security/key-chain.hpp>
+#include <vector>
+
+#include "boost-test.hpp"
+
+namespace nfd {
+namespace tests {
+
+/**
+ * @brief IdentityManagementFixture is a test suite level fixture.
+ *
+ * Test cases in the suite can use this fixture to create identities.
+ * Identities added via addIdentity method are automatically deleted
+ * during test teardown.
+ */
+class IdentityManagementFixture : public nfd::tests::BaseFixture
+{
+public:
+  IdentityManagementFixture();
+
+  ~IdentityManagementFixture();
+
+  // @brief add identity, return true if succeed.
+  bool
+  addIdentity(const ndn::Name& identity,
+              const ndn::KeyParams& params = ndn::KeyChain::DEFAULT_KEY_PARAMS);
+
+protected:
+  ndn::KeyChain m_keyChain;
+  std::vector<ndn::Name> m_identities;
+};
+
+} // namespace tests
+} // namespace nfd
diff --git a/NFD/tests/limited-io.cpp b/NFD/tests/limited-io.cpp
new file mode 100644
index 0000000..4bfd802
--- /dev/null
+++ b/NFD/tests/limited-io.cpp
@@ -0,0 +1,126 @@
+/* -*- 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/>.
+ */
+
+#include "limited-io.hpp"
+#include "core/logger.hpp"
+
+namespace nfd {
+namespace tests {
+
+NFD_LOG_INIT("LimitedIo");
+
+const int LimitedIo::UNLIMITED_OPS = std::numeric_limits<int>::max();
+const time::nanoseconds LimitedIo::UNLIMITED_TIME = time::nanoseconds::min();
+
+LimitedIo::LimitedIo()
+  : m_uttf(nullptr)
+  , m_isRunning(false)
+  , m_nOpsRemaining(0)
+{
+}
+
+LimitedIo::LimitedIo(UnitTestTimeFixture* uttf)
+  : m_uttf(uttf)
+  , m_isRunning(false)
+  , m_nOpsRemaining(0)
+{
+}
+
+LimitedIo::StopReason
+LimitedIo::run(int nOpsLimit, const time::nanoseconds& timeLimit, const time::nanoseconds& tick)
+{
+  BOOST_ASSERT(!m_isRunning);
+
+  if (nOpsLimit <= 0) {
+    return EXCEED_OPS;
+  }
+
+  m_isRunning = true;
+
+  m_reason = NO_WORK;
+  m_nOpsRemaining = nOpsLimit;
+  if (timeLimit >= time::nanoseconds::zero()) {
+    m_timeout = scheduler::schedule(timeLimit, bind(&LimitedIo::afterTimeout, this));
+  }
+
+  try {
+    if (m_uttf == nullptr) {
+      getGlobalIoService().run();
+    }
+    else {
+      // timeLimit is enforced by afterTimeout
+      m_uttf->advanceClocks(tick, time::nanoseconds::max());
+    }
+  }
+  catch (StopException&) {
+  }
+  catch (std::exception& ex) {
+    m_reason = EXCEPTION;
+    NFD_LOG_ERROR("g_io.run() exception: " << ex.what());
+    m_lastException = ex;
+  }
+
+  getGlobalIoService().reset();
+  scheduler::cancel(m_timeout);
+  m_isRunning = false;
+  return m_reason;
+}
+
+void
+LimitedIo::afterOp()
+{
+  if (!m_isRunning) {
+    // Do not proceed further if .afterOp() is invoked out of .run(),
+    return;
+  }
+
+  --m_nOpsRemaining;
+  if (m_nOpsRemaining <= 0) {
+    m_reason = EXCEED_OPS;
+    getGlobalIoService().stop();
+    if (m_uttf != nullptr) {
+      throw StopException();
+    }
+  }
+}
+
+void
+LimitedIo::afterTimeout()
+{
+  m_reason = EXCEED_TIME;
+  getGlobalIoService().stop();
+  if (m_uttf != nullptr) {
+    throw StopException();
+  }
+}
+
+const std::exception&
+LimitedIo::getLastException() const
+{
+  return m_lastException;
+}
+
+} // namespace tests
+} // namespace nfd
diff --git a/NFD/tests/limited-io.hpp b/NFD/tests/limited-io.hpp
new file mode 100644
index 0000000..ee358b0
--- /dev/null
+++ b/NFD/tests/limited-io.hpp
@@ -0,0 +1,113 @@
+/* -*- 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_TESTS_LIMITED_IO_HPP
+#define NFD_TESTS_LIMITED_IO_HPP
+
+#include "test-common.hpp"
+#include "core/global-io.hpp"
+#include "core/scheduler.hpp"
+
+namespace nfd {
+namespace tests {
+
+/** \brief provides IO operations limit and/or time limit for unit testing
+ */
+class LimitedIo : noncopyable
+{
+public:
+  LimitedIo();
+
+  /** \brief construct with UnitTestTimeFixture
+   */
+  LimitedIo(UnitTestTimeFixture* uttf);
+
+  /// indicates why .run returns
+  enum StopReason
+  {
+    /// g_io.run() returns normally because there's no work to do
+    NO_WORK,
+    /// .afterOp() has been invoked nOpsLimit times
+    EXCEED_OPS,
+    /// nTimeLimit has elapsed
+    EXCEED_TIME,
+    /// an exception is thrown
+    EXCEPTION
+  };
+
+  /** \brief g_io.run() with operation count and/or time limit
+   *  \param nOpsLimit operation count limit, pass UNLIMITED_OPS for no limit
+   *  \param timeLimit time limit, pass UNLIMITED_TIME for no limit
+   *  \param tick if this LimitedIo is constructed with UnitTestTimeFixture,
+   *              this is passed to .advanceClocks(), otherwise ignored
+   */
+  StopReason
+  run(int nOpsLimit, const time::nanoseconds& timeLimit,
+      const time::nanoseconds& tick = time::milliseconds(1));
+
+  /// count an operation
+  void
+  afterOp();
+
+  const std::exception&
+  getLastException() const;
+
+  /** \brief defer for specified duration
+   *
+   *  equivalent to .run(UNLIMITED_OPS, d)
+   */
+  void
+  defer(const time::nanoseconds& d)
+  {
+    this->run(UNLIMITED_OPS, d);
+  }
+
+private:
+  /** \brief an exception to stop IO operation
+   */
+  class StopException
+  {
+  };
+
+  void
+  afterTimeout();
+
+public:
+  static const int UNLIMITED_OPS;
+  static const time::nanoseconds UNLIMITED_TIME;
+
+private:
+  UnitTestTimeFixture* m_uttf;
+  bool m_isRunning;
+  int m_nOpsRemaining;
+  EventId m_timeout;
+  StopReason m_reason;
+  std::exception m_lastException;
+};
+
+} // namespace tests
+} // namespace nfd
+
+#endif // NFD_TESTS_LIMITED_IO_HPP
diff --git a/NFD/tests/main.cpp b/NFD/tests/main.cpp
new file mode 100644
index 0000000..2733634
--- /dev/null
+++ b/NFD/tests/main.cpp
@@ -0,0 +1,28 @@
+/* -*- 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
+ *
+ * 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/>.
+ **/
+
+#define BOOST_TEST_MAIN 1
+#define BOOST_TEST_DYN_LINK 1
+
+#include "boost-test.hpp"
diff --git a/NFD/tests/other/cs-smoketest.cpp b/NFD/tests/other/cs-smoketest.cpp
new file mode 100644
index 0000000..1fc97c9
--- /dev/null
+++ b/NFD/tests/other/cs-smoketest.cpp
@@ -0,0 +1,111 @@
+/* -*- 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/>.
+ */
+
+#include "table/cs.hpp"
+#include "table/cs-entry.hpp"
+#include <ndn-cxx/security/key-chain.hpp>
+
+namespace nfd {
+namespace cs_smoketest {
+
+static void
+runStressTest()
+{
+  shared_ptr<Data> dataWorkload[70000];
+  shared_ptr<Interest> interestWorkload[70000];
+
+  ndn::SignatureSha256WithRsa fakeSignature;
+  fakeSignature.setValue(ndn::dataBlock(tlv::SignatureValue,
+                                        reinterpret_cast<const uint8_t*>(0), 0));
+
+  // 182 MB in memory
+  for (int i = 0; i < 70000; i++) {
+    Name name("/stress/test");
+    name.appendNumber(i % 4);
+    name.appendNumber(i);
+
+    shared_ptr<Interest> interest = make_shared<Interest>(name);
+    interestWorkload[i] = interest;
+
+    shared_ptr<Data> data = make_shared<Data>(name);
+    data->setSignature(fakeSignature);
+    dataWorkload[i] = data;
+  }
+
+  time::duration<double, boost::nano> previousResult(0);
+
+  for (size_t nInsertions = 1000; nInsertions < 10000000; nInsertions *= 2) {
+    Cs cs;
+    srand(time::toUnixTimestamp(time::system_clock::now()).count());
+
+    time::steady_clock::TimePoint startTime = time::steady_clock::now();
+
+    size_t workloadCounter = 0;
+    for (size_t i = 0; i < nInsertions; i++) {
+      if (workloadCounter > 69999)
+        workloadCounter = 0;
+
+      cs.find(*interestWorkload[workloadCounter]);
+      Data& data = *dataWorkload[workloadCounter];
+      data.setName(data.getName()); // reset data.m_fullName
+      data.wireEncode();
+      cs.insert(data);
+
+      workloadCounter++;
+    }
+
+    time::steady_clock::TimePoint endTime = time::steady_clock::now();
+
+    time::duration<double, boost::nano> runDuration = endTime - startTime;
+    time::duration<double, boost::nano> perOperationTime = runDuration / nInsertions;
+
+    std::cout << "nItem = " << nInsertions << std::endl;
+    std::cout << "Total running time = "
+              << time::duration_cast<time::duration<double> >(runDuration)
+              << std::endl;
+    std::cout << "Average per-operation time = "
+              << time::duration_cast<time::duration<double, boost::micro> >(perOperationTime)
+              << std::endl;
+
+    if (previousResult > time::nanoseconds(1))
+      std::cout << "Change compared to the previous: "
+                << (100.0 * perOperationTime / previousResult) << "%" << std::endl;
+
+    std::cout << "\n=================================\n" << std::endl;
+
+    previousResult = perOperationTime;
+  }
+}
+
+} // namespace cs_smoketest
+} // namespace nfd
+
+int
+main(int argc, char** argv)
+{
+  nfd::cs_smoketest::runStressTest();
+
+  return 0;
+}
diff --git a/NFD/tests/other/wscript b/NFD/tests/other/wscript
new file mode 100644
index 0000000..69aa2bb
--- /dev/null
+++ b/NFD/tests/other/wscript
@@ -0,0 +1,33 @@
+# -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
+
+"""
+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
+
+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/>.
+"""
+
+top = '../..'
+
+def build(bld):
+    bld.program(target="../../cs-smoketest",
+                source="cs-smoketest.cpp",
+                use='daemon-objects',
+                install_path=None,
+                )
diff --git a/NFD/tests/rib/fib-updates-common.hpp b/NFD/tests/rib/fib-updates-common.hpp
new file mode 100644
index 0000000..c8c0af4
--- /dev/null
+++ b/NFD/tests/rib/fib-updates-common.hpp
@@ -0,0 +1,112 @@
+/* -*- 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/>.
+ */
+
+namespace nfd {
+namespace rib {
+namespace tests {
+
+inline FaceEntry
+createFaceEntry(uint64_t faceId, uint64_t origin, uint64_t cost, uint64_t flags)
+{
+  FaceEntry temp;
+  temp.faceId = faceId;
+  temp.origin = origin;
+  temp.cost = cost;
+  temp.flags = flags;
+
+  return temp;
+}
+
+inline bool
+compareNameFaceIdCostAction(const shared_ptr<const FibUpdate>& lhs,
+                            const shared_ptr<const FibUpdate>& rhs)
+{
+  if (lhs->name < rhs->name)
+    {
+      return true;
+    }
+  else if (lhs->name == rhs->name)
+    {
+      if (lhs->faceId < rhs->faceId)
+        {
+          return true;
+        }
+      else if (lhs->faceId == rhs->faceId)
+        {
+          if (lhs->cost < rhs->cost)
+            {
+              return true;
+            }
+          else if (lhs->cost == rhs->cost)
+            {
+              return lhs->action < rhs->action;
+            }
+        }
+    }
+
+  return false;
+}
+
+class FibUpdatesFixture : public nfd::tests::BaseFixture
+{
+public:
+  void
+  insertFaceEntry(const Name& name, uint64_t faceId, uint64_t origin, uint64_t cost, uint64_t flags)
+  {
+    rib::FaceEntry faceEntry;
+    faceEntry.faceId = faceId;
+    faceEntry.origin = origin;
+    faceEntry.cost = cost;
+    faceEntry.flags = flags;
+
+    rib.insert(name, faceEntry);
+  }
+
+  void
+  eraseFaceEntry(const Name& name, uint64_t faceId, uint64_t origin)
+  {
+    rib::FaceEntry faceEntry;
+    faceEntry.faceId = faceId;
+    faceEntry.origin = origin;
+
+    rib.erase(name, faceEntry);
+  }
+
+
+  Rib::FibUpdateList
+  getSortedFibUpdates()
+  {
+    Rib::FibUpdateList updates = rib.getFibUpdates();
+    updates.sort(&compareNameFaceIdCostAction);
+    return updates;
+  }
+
+public:
+  rib::Rib rib;
+};
+
+} // namespace tests
+} // namespace rib
+} // namespace nfd
diff --git a/NFD/tests/rib/fib-updates-erase-face.cpp b/NFD/tests/rib/fib-updates-erase-face.cpp
new file mode 100644
index 0000000..e56c0d3
--- /dev/null
+++ b/NFD/tests/rib/fib-updates-erase-face.cpp
@@ -0,0 +1,396 @@
+/* -*- 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/>.
+ */
+
+#include "rib/rib.hpp"
+
+#include "tests/test-common.hpp"
+#include "fib-updates-common.hpp"
+
+namespace nfd {
+namespace rib {
+namespace tests {
+
+BOOST_FIXTURE_TEST_SUITE(FibUpdates, FibUpdatesFixture)
+
+BOOST_AUTO_TEST_SUITE(EraseFace)
+
+BOOST_AUTO_TEST_CASE(WithInheritedFace_Root)
+{
+  insertFaceEntry("/", 1, 0, 10, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertFaceEntry("/a", 1, 0, 50, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertFaceEntry("/a/b", 2, 0, 75, 0);
+
+  // Clear updates generated from previous insertions
+  rib.clearFibUpdates();
+
+  // Should generate 1 updates: 1 to remove face 1 from /
+  eraseFaceEntry("/", 1, 0);
+
+  Rib::FibUpdateList updates = getSortedFibUpdates();
+  BOOST_REQUIRE_EQUAL(updates.size(), 1);
+
+  Rib::FibUpdateList::const_iterator update = updates.begin();
+  BOOST_CHECK_EQUAL((*update)->name,  "/");
+  BOOST_CHECK_EQUAL((*update)->faceId, 1);
+  BOOST_CHECK_EQUAL((*update)->action, FibUpdate::REMOVE_NEXTHOP);
+}
+
+BOOST_AUTO_TEST_CASE(WithInheritedFace)
+{
+  insertFaceEntry("/a", 5, 0, 10, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertFaceEntry("/a", 5, 255, 5, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertFaceEntry("/a", 2, 0, 20, 0);
+  insertFaceEntry("/a/b", 3, 0, 5, 0);
+
+  // /a should have face 5 with cost 10; /a/b should have face 3 with cost 5 and
+  // face 5 with cost 10
+  eraseFaceEntry("/a", 5, 255);
+
+  // Clear updates generated from previous insertions
+  rib.clearFibUpdates();
+
+  // Should generate 2 updates: 1 to remove face 3 from /a/b and one to remove inherited face.
+  eraseFaceEntry("/a/b", 3, 0);
+
+  Rib::FibUpdateList updates = getSortedFibUpdates();
+  BOOST_REQUIRE_EQUAL(updates.size(), 2);
+
+  Rib::FibUpdateList::const_iterator update = updates.begin();
+  BOOST_CHECK_EQUAL((*update)->name,  "/a/b");
+  BOOST_CHECK_EQUAL((*update)->faceId, 3);
+  BOOST_CHECK_EQUAL((*update)->action, FibUpdate::REMOVE_NEXTHOP);
+
+  ++update;
+  BOOST_CHECK_EQUAL((*update)->name,  "/a/b");
+  BOOST_CHECK_EQUAL((*update)->faceId, 5);
+  BOOST_CHECK_EQUAL((*update)->action, FibUpdate::REMOVE_NEXTHOP);
+}
+
+BOOST_AUTO_TEST_CASE(MultipleFaces)
+{
+  insertFaceEntry("/a", 5, 0, 10, 0);
+  insertFaceEntry("/a", 5, 255, 5, 0);
+
+  // Clear updates generated from previous insertions
+  rib.clearFibUpdates();
+
+  // Should generate 1 updates: 1 to update cost to 10 for /a
+  eraseFaceEntry("/a", 5, 255);
+
+  Rib::FibUpdateList updates = getSortedFibUpdates();
+  BOOST_REQUIRE_EQUAL(updates.size(), 1);
+
+  Rib::FibUpdateList::const_iterator update = updates.begin();
+  BOOST_CHECK_EQUAL((*update)->name,  "/a");
+  BOOST_CHECK_EQUAL((*update)->faceId, 5);
+  BOOST_CHECK_EQUAL((*update)->cost, 10);
+  BOOST_CHECK_EQUAL((*update)->action, FibUpdate::ADD_NEXTHOP);
+}
+
+BOOST_AUTO_TEST_CASE(NoFlags_NoCaptureChange_NoCaptureOnRoute)
+{
+  insertFaceEntry("/", 1, 0, 5, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertFaceEntry("/a", 2, 0, 10, 0);
+  insertFaceEntry("/a/b", 3, 0, 10, 0);
+  insertFaceEntry("/a/c", 1, 0, 100, 0);
+  insertFaceEntry("/a", 1, 128, 50, 0);
+
+  // Clear updates generated from previous insertions
+  rib.clearFibUpdates();
+
+  // Should generate 1 updates: 1 to update cost for /a
+  eraseFaceEntry("/a", 1, 128);
+
+  Rib::FibUpdateList updates = getSortedFibUpdates();
+  BOOST_REQUIRE_EQUAL(updates.size(), 1);
+
+  Rib::FibUpdateList::const_iterator update = updates.begin();
+  BOOST_CHECK_EQUAL((*update)->name,  "/a");
+  BOOST_CHECK_EQUAL((*update)->faceId, 1);
+  BOOST_CHECK_EQUAL((*update)->cost, 5);
+  BOOST_CHECK_EQUAL((*update)->action, FibUpdate::ADD_NEXTHOP);
+}
+
+BOOST_AUTO_TEST_CASE(MakeRibEmpty)
+{
+  insertFaceEntry("/", 1, 0, 5, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+
+  // Clear updates generated from previous insertions
+  rib.clearFibUpdates();
+
+  // Should generate 1 updates: 1 to remove face from /
+  eraseFaceEntry("/", 1, 0);
+
+  Rib::FibUpdateList updates = getSortedFibUpdates();
+  BOOST_REQUIRE_EQUAL(updates.size(), 1);
+
+  Rib::FibUpdateList::const_iterator update = updates.begin();
+  BOOST_CHECK_EQUAL((*update)->name,  "/");
+  BOOST_CHECK_EQUAL((*update)->faceId, 1);
+  BOOST_CHECK_EQUAL((*update)->action, FibUpdate::REMOVE_NEXTHOP);
+}
+
+BOOST_AUTO_TEST_CASE(NoFlags_NoCaptureChange_CaptureOnRoute)
+{
+  insertFaceEntry("/", 1, 0, 5, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertFaceEntry("/a", 2, 0, 10, ndn::nfd::ROUTE_FLAG_CAPTURE);
+  insertFaceEntry("/a/b", 3, 0, 10, 0);
+  insertFaceEntry("/a/c", 1, 0, 100, 0);
+  insertFaceEntry("/a", 1, 128, 50, 0);
+
+  // Clear updates generated from previous insertions
+  rib.clearFibUpdates();
+
+  // Should generate 1 updates: 1 to remove face from /a
+  eraseFaceEntry("/a", 1, 128);
+
+  Rib::FibUpdateList updates = getSortedFibUpdates();
+  BOOST_REQUIRE_EQUAL(updates.size(), 1);
+
+  Rib::FibUpdateList::const_iterator update = updates.begin();
+  BOOST_CHECK_EQUAL((*update)->name,  "/a");
+  BOOST_CHECK_EQUAL((*update)->faceId, 1);
+  BOOST_CHECK_EQUAL((*update)->action, FibUpdate::REMOVE_NEXTHOP);
+}
+
+BOOST_AUTO_TEST_CASE(BothFlags_NoCaptureChange_CaptureOnRoute)
+{
+  insertFaceEntry("/", 1, 0, 5, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertFaceEntry("/a", 2, 0, 10, ndn::nfd::ROUTE_FLAG_CAPTURE);
+  insertFaceEntry("/a/b", 3, 0, 10, 0);
+  insertFaceEntry("/a/c", 1, 0, 100, 0);
+  insertFaceEntry("/a", 1, 128, 50, (ndn::nfd::ROUTE_FLAG_CHILD_INHERIT |
+                                     ndn::nfd::ROUTE_FLAG_CAPTURE));
+
+  // Clear updates generated from previous insertions
+  rib.clearFibUpdates();
+
+  // Should generate 2 updates: 1 to remove face1 from /a and
+  // 1 to remove face1 to /a/b
+  eraseFaceEntry("/a", 1, 128);
+
+  Rib::FibUpdateList updates = getSortedFibUpdates();
+  BOOST_REQUIRE_EQUAL(updates.size(), 2);
+
+  Rib::FibUpdateList::const_iterator update = updates.begin();
+  BOOST_CHECK_EQUAL((*update)->name,  "/a");
+  BOOST_CHECK_EQUAL((*update)->faceId, 1);
+  BOOST_CHECK_EQUAL((*update)->action, FibUpdate::REMOVE_NEXTHOP);
+
+  ++update;
+  BOOST_CHECK_EQUAL((*update)->name,  "/a/b");
+  BOOST_CHECK_EQUAL((*update)->faceId, 1);
+  BOOST_CHECK_EQUAL((*update)->action, FibUpdate::REMOVE_NEXTHOP);
+}
+
+BOOST_AUTO_TEST_CASE(BothFlags_CaptureChange_NoCaptureOnRoute)
+{
+  insertFaceEntry("/", 1, 0, 5, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertFaceEntry("/a", 2, 0, 10, 0);
+  insertFaceEntry("/a/b", 3, 0, 10, 0);
+  insertFaceEntry("/a/c", 1, 0, 100, 0);
+  insertFaceEntry("/a", 1, 128, 50, (ndn::nfd::ROUTE_FLAG_CHILD_INHERIT |
+                                     ndn::nfd::ROUTE_FLAG_CAPTURE));
+
+  // Clear updates generated from previous insertions
+  rib.clearFibUpdates();
+
+  // Should generate 2 updates: 1 to add face1 to /a and
+  // 1 to add face1 to /a/b
+  eraseFaceEntry("/a", 1, 128);
+
+  Rib::FibUpdateList updates = getSortedFibUpdates();
+  BOOST_REQUIRE_EQUAL(updates.size(), 2);
+
+  Rib::FibUpdateList::const_iterator update = updates.begin();
+  BOOST_CHECK_EQUAL((*update)->name,  "/a");
+  BOOST_CHECK_EQUAL((*update)->faceId, 1);
+  BOOST_CHECK_EQUAL((*update)->cost, 5);
+  BOOST_CHECK_EQUAL((*update)->action, FibUpdate::ADD_NEXTHOP);
+
+  ++update;
+  BOOST_CHECK_EQUAL((*update)->name,  "/a/b");
+  BOOST_CHECK_EQUAL((*update)->faceId, 1);
+  BOOST_CHECK_EQUAL((*update)->cost, 5);
+  BOOST_CHECK_EQUAL((*update)->action, FibUpdate::ADD_NEXTHOP);
+}
+
+BOOST_AUTO_TEST_CASE(ChildInherit_NoCaptureChange_NoCaptureOnRoute)
+{
+  insertFaceEntry("/", 1, 0, 5, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertFaceEntry("/a", 2, 0, 10, 0);
+  insertFaceEntry("/a/b", 3, 0, 10, 0);
+  insertFaceEntry("/a/c", 1, 0, 100, 0);
+  insertFaceEntry("/a", 1, 128, 50, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+
+  // Clear updates generated from previous insertions
+  rib.clearFibUpdates();
+
+  // Should generate 2 updates: 2 to add face1 to /a and /a/b
+  eraseFaceEntry("/a", 1, 128);
+
+  Rib::FibUpdateList updates = getSortedFibUpdates();
+  BOOST_REQUIRE_EQUAL(updates.size(), 2);
+
+  Rib::FibUpdateList::const_iterator update = updates.begin();
+  BOOST_CHECK_EQUAL((*update)->name,  "/a");
+  BOOST_CHECK_EQUAL((*update)->faceId, 1);
+  BOOST_CHECK_EQUAL((*update)->cost, 5);
+  BOOST_CHECK_EQUAL((*update)->action, FibUpdate::ADD_NEXTHOP);
+
+  ++update;
+  BOOST_CHECK_EQUAL((*update)->name,  "/a/b");
+  BOOST_CHECK_EQUAL((*update)->faceId, 1);
+  BOOST_CHECK_EQUAL((*update)->cost, 5);
+  BOOST_CHECK_EQUAL((*update)->action, FibUpdate::ADD_NEXTHOP);
+}
+
+BOOST_AUTO_TEST_CASE(ChildInherit_NoCaptureChange_CaptureOnRoute)
+{
+  insertFaceEntry("/", 1, 0, 5, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertFaceEntry("/a", 2, 0, 10, ndn::nfd::ROUTE_FLAG_CAPTURE);
+  insertFaceEntry("/a/b", 3, 0, 10, 0);
+  insertFaceEntry("/a/c", 1, 0, 100, 0);
+  insertFaceEntry("/a", 1, 128, 50, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+
+  // Clear updates generated from previous insertions
+  rib.clearFibUpdates();
+
+  // Should generate 2 updates: 2 to remove face 1 from /a and /a/b
+  eraseFaceEntry("/a", 1, 128);
+
+  Rib::FibUpdateList updates = getSortedFibUpdates();
+  BOOST_REQUIRE_EQUAL(updates.size(), 2);
+
+  Rib::FibUpdateList::const_iterator update = updates.begin();
+  BOOST_CHECK_EQUAL((*update)->name,  "/a");
+  BOOST_CHECK_EQUAL((*update)->faceId, 1);
+  BOOST_CHECK_EQUAL((*update)->action, FibUpdate::REMOVE_NEXTHOP);
+
+  ++update;
+  BOOST_CHECK_EQUAL((*update)->name,  "/a/b");
+  BOOST_CHECK_EQUAL((*update)->faceId, 1);
+  BOOST_CHECK_EQUAL((*update)->action, FibUpdate::REMOVE_NEXTHOP);
+}
+
+BOOST_AUTO_TEST_CASE(Capture_CaptureChange_NoCaptureOnRoute)
+{
+  insertFaceEntry("/", 1, 0, 5, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertFaceEntry("/a", 2, 0, 10, 0);
+  insertFaceEntry("/a/b", 3, 0, 10, 0);
+  insertFaceEntry("/a/c", 1, 0, 100, 0);
+  insertFaceEntry("/a", 1, 128, 50, ndn::nfd::ROUTE_FLAG_CAPTURE);
+
+  // Clear updates generated from previous insertions
+  rib.clearFibUpdates();
+
+  // Should generate 2 updates: 1 to update cost on /a and
+  // 1 to add face1 to /a/b
+  eraseFaceEntry("/a", 1 ,128);
+
+  Rib::FibUpdateList updates = getSortedFibUpdates();
+  BOOST_REQUIRE_EQUAL(updates.size(), 2);
+
+  Rib::FibUpdateList::const_iterator update = updates.begin();
+  BOOST_CHECK_EQUAL((*update)->name,  "/a");
+  BOOST_CHECK_EQUAL((*update)->faceId, 1);
+  BOOST_CHECK_EQUAL((*update)->cost, 5);
+  BOOST_CHECK_EQUAL((*update)->action, FibUpdate::ADD_NEXTHOP);
+
+  ++update;
+  BOOST_CHECK_EQUAL((*update)->name,  "/a/b");
+  BOOST_CHECK_EQUAL((*update)->faceId, 1);
+  BOOST_CHECK_EQUAL((*update)->cost, 5);
+  BOOST_CHECK_EQUAL((*update)->action, FibUpdate::ADD_NEXTHOP);
+}
+
+BOOST_AUTO_TEST_CASE(Capture_NoCaptureChange_CaptureOnRoute)
+{
+  insertFaceEntry("/", 1, 0, 5, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertFaceEntry("/a", 2, 0, 10, ndn::nfd::ROUTE_FLAG_CAPTURE);
+  insertFaceEntry("/a/b", 3, 0, 10, 0);
+  insertFaceEntry("/a/c", 1, 0, 100, 0);
+  insertFaceEntry("/a", 1, 128, 50, ndn::nfd::ROUTE_FLAG_CAPTURE);
+
+  // Clear updates generated from previous insertions
+  rib.clearFibUpdates();
+
+  // Should generate 1 updates: 1 to remove face from /a
+  eraseFaceEntry("/a", 1, 128);
+
+  Rib::FibUpdateList updates = getSortedFibUpdates();
+  BOOST_REQUIRE_EQUAL(updates.size(), 1);
+
+  Rib::FibUpdateList::const_iterator update = updates.begin();
+  BOOST_CHECK_EQUAL((*update)->name,  "/a");
+  BOOST_CHECK_EQUAL((*update)->faceId, 1);
+  BOOST_CHECK_EQUAL((*update)->action, FibUpdate::REMOVE_NEXTHOP);
+}
+
+BOOST_AUTO_TEST_CASE(EraseFaceById)
+{
+  insertFaceEntry("/", 1, 0, 5, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertFaceEntry("/a", 2, 0, 10, 0);
+  insertFaceEntry("/a/b", 3, 0, 10, 0);
+  insertFaceEntry("/a/c", 4, 0, 100, 0);
+  insertFaceEntry("/a", 1, 128, 50, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+
+  // Clear updates generated from previous insertions
+  rib.clearFibUpdates();
+
+  // Should generate 4 updates: 4 to remove face ID 1 from /, /a, /a/b, and /a/c
+  rib.erase(1);
+
+  Rib::FibUpdateList updates = getSortedFibUpdates();
+  BOOST_REQUIRE_EQUAL(updates.size(), 4);
+
+  Rib::FibUpdateList::const_iterator update = updates.begin();
+  BOOST_CHECK_EQUAL((*update)->name,  "/");
+  BOOST_CHECK_EQUAL((*update)->faceId, 1);
+  BOOST_CHECK_EQUAL((*update)->action, FibUpdate::REMOVE_NEXTHOP);
+
+  ++update;
+  BOOST_CHECK_EQUAL((*update)->name,  "/a");
+  BOOST_CHECK_EQUAL((*update)->faceId, 1);
+  BOOST_CHECK_EQUAL((*update)->action, FibUpdate::REMOVE_NEXTHOP);
+
+  ++update;
+  BOOST_CHECK_EQUAL((*update)->name,  "/a/b");
+  BOOST_CHECK_EQUAL((*update)->faceId, 1);
+  BOOST_CHECK_EQUAL((*update)->action, FibUpdate::REMOVE_NEXTHOP);
+
+  ++update;
+  BOOST_CHECK_EQUAL((*update)->name,  "/a/c");
+  BOOST_CHECK_EQUAL((*update)->faceId, 1);
+  BOOST_CHECK_EQUAL((*update)->action, FibUpdate::REMOVE_NEXTHOP);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // EraseFace
+
+BOOST_AUTO_TEST_SUITE_END() // FibUpdates
+
+} // namespace tests
+} // namespace rib
+} // namespace nfd
diff --git a/NFD/tests/rib/fib-updates-new-face.cpp b/NFD/tests/rib/fib-updates-new-face.cpp
new file mode 100644
index 0000000..b8e7d69
--- /dev/null
+++ b/NFD/tests/rib/fib-updates-new-face.cpp
@@ -0,0 +1,272 @@
+/* -*- 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/>.
+ */
+
+#include "rib/rib.hpp"
+
+#include "tests/test-common.hpp"
+#include "fib-updates-common.hpp"
+
+namespace nfd {
+namespace rib {
+namespace tests {
+
+BOOST_FIXTURE_TEST_SUITE(FibUpdates, FibUpdatesFixture)
+
+BOOST_AUTO_TEST_SUITE(NewFace)
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+  // should generate 1 update
+  insertFaceEntry("/", 1, 0, 50, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+
+  Rib::FibUpdateList updates = rib.getFibUpdates();
+  BOOST_REQUIRE_EQUAL(updates.size(), 1);
+
+  Rib::FibUpdateList::const_iterator update = updates.begin();
+
+  BOOST_CHECK_EQUAL((*update)->name,  "/");
+  BOOST_CHECK_EQUAL((*update)->faceId, 1);
+  BOOST_CHECK_EQUAL((*update)->cost,   50);
+  BOOST_CHECK_EQUAL((*update)->action, FibUpdate::ADD_NEXTHOP);
+
+  // Clear any updates generated from previous insertions
+  rib.clearFibUpdates();
+
+  // should generate 2 updates
+  insertFaceEntry("/a", 2, 0, 50, 0);
+
+  updates = getSortedFibUpdates();
+  BOOST_REQUIRE_EQUAL(updates.size(), 2);
+
+  update = updates.begin();
+  BOOST_CHECK_EQUAL((*update)->name,  "/a");
+  BOOST_CHECK_EQUAL((*update)->faceId, 1);
+  BOOST_CHECK_EQUAL((*update)->cost,   50);
+  BOOST_CHECK_EQUAL((*update)->action, FibUpdate::ADD_NEXTHOP);
+
+  ++update;
+  BOOST_CHECK_EQUAL((*update)->name,  "/a");
+  BOOST_CHECK_EQUAL((*update)->faceId, 2);
+  BOOST_CHECK_EQUAL((*update)->cost,   50);
+  BOOST_CHECK_EQUAL((*update)->action, FibUpdate::ADD_NEXTHOP);
+
+  // Clear updates generated from previous insertions
+  rib.clearFibUpdates();
+
+  // should generate 2 updates
+  insertFaceEntry("/a/b", 3, 0, 10, 0);
+
+  updates = getSortedFibUpdates();
+  BOOST_REQUIRE_EQUAL(updates.size(), 2);
+
+  update = updates.begin();
+  BOOST_CHECK_EQUAL((*update)->name,  "/a/b");
+  BOOST_CHECK_EQUAL((*update)->faceId, 1);
+  BOOST_CHECK_EQUAL((*update)->cost,   50);
+  BOOST_CHECK_EQUAL((*update)->action, FibUpdate::ADD_NEXTHOP);
+
+  ++update;
+  BOOST_CHECK_EQUAL((*update)->name,  "/a/b");
+  BOOST_CHECK_EQUAL((*update)->faceId, 3);
+  BOOST_CHECK_EQUAL((*update)->cost,   10);
+  BOOST_CHECK_EQUAL((*update)->action, FibUpdate::ADD_NEXTHOP);
+}
+
+BOOST_AUTO_TEST_CASE(UpdateOnLowerCostNoChildInherit)
+{
+  insertFaceEntry("/", 1, 0, 50, 0);
+
+  // Clear any updates generated from previous insertions
+  rib.clearFibUpdates();
+
+  // Should generate 0 updates
+  insertFaceEntry("/", 1, 128, 75, 0);
+
+  BOOST_CHECK_EQUAL(rib.getFibUpdates().size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(UpdateOnLowerCostOnly)
+{
+  insertFaceEntry("/",  1, 0, 50, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertFaceEntry("/a", 2, 0, 10, 0);
+
+  // Clear updates generated from previous insertions
+  rib.clearFibUpdates();
+
+  // Should generate 2 updates: to update cost for face 1 on / and /a
+  insertFaceEntry("/", 1, 0, 25, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+
+  Rib::FibUpdateList updates = getSortedFibUpdates();
+  BOOST_REQUIRE_EQUAL(updates.size(), 2);
+
+  Rib::FibUpdateList::const_iterator update = updates.begin();
+  BOOST_CHECK_EQUAL((*update)->name,  "/");
+  BOOST_CHECK_EQUAL((*update)->faceId, 1);
+  BOOST_CHECK_EQUAL((*update)->cost,   25);
+  BOOST_CHECK_EQUAL((*update)->action, FibUpdate::ADD_NEXTHOP);
+
+  ++update;
+  BOOST_CHECK_EQUAL((*update)->name,  "/a");
+  BOOST_CHECK_EQUAL((*update)->faceId, 1);
+  BOOST_CHECK_EQUAL((*update)->cost,   25);
+  BOOST_CHECK_EQUAL((*update)->action, FibUpdate::ADD_NEXTHOP);
+
+  // Clear updates generated from previous insertions
+  rib.clearFibUpdates();
+
+  // Should generate 0 updates
+  insertFaceEntry("/", 1, 128, 50, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+
+  BOOST_CHECK_EQUAL(rib.getFibUpdates().size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(NoCaptureChangeWithoutChildInherit)
+{
+  insertFaceEntry("/",    1, 0, 50, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertFaceEntry("/a",   2, 0, 10, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertFaceEntry("/a/b", 3, 0, 10, 0);
+  insertFaceEntry("/a/c", 4, 0, 10, ndn::nfd::ROUTE_FLAG_CAPTURE);
+
+  // Clear updates generated from previous insertions
+  rib.clearFibUpdates();
+
+  // Should generate 1 update: 1 to add face 5 to /a
+  insertFaceEntry("/a", 5, 128, 50, 0);
+
+  const Rib::FibUpdateList& updates = rib.getFibUpdates();
+  BOOST_REQUIRE_EQUAL(updates.size(), 1);
+
+  Rib::FibUpdateList::const_iterator update = updates.begin();
+
+  BOOST_CHECK_EQUAL((*update)->name,  "/a");
+  BOOST_CHECK_EQUAL((*update)->faceId, 5);
+  BOOST_CHECK_EQUAL((*update)->cost,   50);
+  BOOST_CHECK_EQUAL((*update)->action, FibUpdate::ADD_NEXTHOP);
+}
+
+BOOST_AUTO_TEST_CASE(NoCaptureChangeWithChildInherit)
+{
+  insertFaceEntry("/",    1, 0, 50, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertFaceEntry("/a",   2, 0, 10, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertFaceEntry("/a/b", 3, 0, 10, 0);
+  insertFaceEntry("/a/c", 4, 0, 10, ndn::nfd::ROUTE_FLAG_CAPTURE);
+
+  // Clear updates generated from previous insertions
+  rib.clearFibUpdates();
+
+  // Should generate 2 updates: one for the inserted face and
+  // one to add face to /a/b
+  insertFaceEntry("/a", 4, 128, 5, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+
+  Rib::FibUpdateList updates = getSortedFibUpdates();
+  BOOST_REQUIRE_EQUAL(updates.size(), 2);
+
+  Rib::FibUpdateList::const_iterator update = updates.begin();
+  BOOST_CHECK_EQUAL((*update)->name,  "/a");
+  BOOST_CHECK_EQUAL((*update)->faceId, 4);
+  BOOST_CHECK_EQUAL((*update)->cost,   5);
+  BOOST_CHECK_EQUAL((*update)->action, FibUpdate::ADD_NEXTHOP);
+
+  ++update;
+  BOOST_CHECK_EQUAL((*update)->name,  "/a/b");
+  BOOST_CHECK_EQUAL((*update)->faceId, 4);
+  BOOST_CHECK_EQUAL((*update)->cost,   5);
+  BOOST_CHECK_EQUAL((*update)->action, FibUpdate::ADD_NEXTHOP);
+}
+
+BOOST_AUTO_TEST_CASE(CaptureTurnedOnWithoutChildInherit)
+{
+  insertFaceEntry("/",    1, 0, 50, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertFaceEntry("/a",   2, 0, 10, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertFaceEntry("/a/b", 3, 0, 10, 0);
+  insertFaceEntry("/a/c", 4, 0, 10, 0);
+
+  // Clear updates generated from previous insertions
+  rib.clearFibUpdates();
+
+  // Should generate 3 updates:
+  // - one for the inserted face for /a and
+  // - two to remove face1 from /a/b and /a/c
+  insertFaceEntry("/a", 1, 128, 50, ndn::nfd::ROUTE_FLAG_CAPTURE);
+
+  Rib::FibUpdateList updates = getSortedFibUpdates();
+  BOOST_REQUIRE_EQUAL(updates.size(), 3);
+
+  Rib::FibUpdateList::const_iterator update = updates.begin();
+  BOOST_CHECK_EQUAL((*update)->name,  "/a");
+  BOOST_CHECK_EQUAL((*update)->faceId, 1);
+  BOOST_CHECK_EQUAL((*update)->cost,   50);
+  BOOST_CHECK_EQUAL((*update)->action, FibUpdate::ADD_NEXTHOP);
+
+  ++update;
+  BOOST_CHECK_EQUAL((*update)->name,  "/a/b");
+  BOOST_CHECK_EQUAL((*update)->faceId, 1);
+  BOOST_CHECK_EQUAL((*update)->action, FibUpdate::REMOVE_NEXTHOP);
+
+  ++update;
+  BOOST_CHECK_EQUAL((*update)->name,  "/a/c");
+  BOOST_CHECK_EQUAL((*update)->faceId, 1);
+  BOOST_CHECK_EQUAL((*update)->action, FibUpdate::REMOVE_NEXTHOP);
+}
+
+BOOST_AUTO_TEST_CASE(CaptureTurnedOnWithChildInherit)
+{
+  insertFaceEntry("/",    1, 0, 50, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertFaceEntry("/a",   2, 0, 10, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertFaceEntry("/a/b", 3, 0, 10, 0);
+  insertFaceEntry("/a/c", 4, 0, 10, 0);
+
+  // Clear updates generated from previous insertions
+  rib.clearFibUpdates();
+
+  // Should generate 2 updates:
+  // - one for the inserted face for /a and
+  // - one to update /a/b with the new cost
+  insertFaceEntry("/a", 1, 128, 50, (ndn::nfd::ROUTE_FLAG_CAPTURE |
+                                     ndn::nfd::ROUTE_FLAG_CHILD_INHERIT));
+
+  Rib::FibUpdateList updates = getSortedFibUpdates();
+  BOOST_REQUIRE_EQUAL(updates.size(), 3);
+
+  Rib::FibUpdateList::const_iterator update = updates.begin();
+  BOOST_CHECK_EQUAL((*update)->name,  "/a");
+  BOOST_CHECK_EQUAL((*update)->faceId, 1);
+  BOOST_CHECK_EQUAL((*update)->cost,   50);
+  BOOST_CHECK_EQUAL((*update)->action, FibUpdate::ADD_NEXTHOP);
+
+  ++update;
+  BOOST_CHECK_EQUAL((*update)->name,  "/a/b");
+  BOOST_CHECK_EQUAL((*update)->faceId, 1);
+  BOOST_CHECK_EQUAL((*update)->cost,   50);
+  BOOST_CHECK_EQUAL((*update)->action, FibUpdate::ADD_NEXTHOP);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // NewFace
+
+BOOST_AUTO_TEST_SUITE_END() // FibUpdates
+
+} // namespace tests
+} // namespace rib
+} // namespace nfd
diff --git a/NFD/tests/rib/fib-updates-new-namespace.cpp b/NFD/tests/rib/fib-updates-new-namespace.cpp
new file mode 100644
index 0000000..e409c51
--- /dev/null
+++ b/NFD/tests/rib/fib-updates-new-namespace.cpp
@@ -0,0 +1,202 @@
+/* -*- 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/>.
+ */
+
+#include "rib/rib.hpp"
+
+#include "tests/test-common.hpp"
+#include "fib-updates-common.hpp"
+
+namespace nfd {
+namespace rib {
+namespace tests {
+
+BOOST_FIXTURE_TEST_SUITE(FibUpdates, FibUpdatesFixture)
+
+BOOST_AUTO_TEST_SUITE(NewNamespace)
+
+BOOST_AUTO_TEST_CASE(NoFlags)
+{
+  // No flags, empty RIB, should generate 1 update for the inserted face
+  insertFaceEntry("/a/b", 1, 0, 10, 0);
+
+  Rib::FibUpdateList updates = getSortedFibUpdates();
+  BOOST_REQUIRE_EQUAL(updates.size(), 1);
+
+  Rib::FibUpdateList::const_iterator update = updates.begin();
+  BOOST_CHECK_EQUAL((*update)->name,  "/a/b");
+  BOOST_CHECK_EQUAL((*update)->faceId, 1);
+  BOOST_CHECK_EQUAL((*update)->cost, 10);
+  BOOST_CHECK_EQUAL((*update)->action, FibUpdate::ADD_NEXTHOP);
+
+  // Reset RIB
+  eraseFaceEntry("/a/b", 1, 0);
+  rib.clearFibUpdates();
+
+  // Parent with child inherit flag
+  insertFaceEntry("/a", 2, 0, 70, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertFaceEntry("/a", 3, 0, 30, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+
+  // Clear updates generated from previous insertions
+  rib.clearFibUpdates();
+
+  // Should generate 3 updates, 1 for the inserted face and 2 from inheritance
+  insertFaceEntry("/a/b", 1, 0, 10, 0);
+
+  updates = getSortedFibUpdates();
+  BOOST_REQUIRE_EQUAL(updates.size(), 3);
+
+  update = updates.begin();
+  BOOST_CHECK_EQUAL((*update)->name,  "/a/b");
+  BOOST_CHECK_EQUAL((*update)->faceId, 1);
+  BOOST_CHECK_EQUAL((*update)->cost, 10);
+  BOOST_CHECK_EQUAL((*update)->action, FibUpdate::ADD_NEXTHOP);
+
+  ++update;
+  BOOST_CHECK_EQUAL((*update)->name,  "/a/b");
+  BOOST_CHECK_EQUAL((*update)->faceId, 2);
+  BOOST_CHECK_EQUAL((*update)->cost, 70);
+  BOOST_CHECK_EQUAL((*update)->action, FibUpdate::ADD_NEXTHOP);
+
+  ++update;
+  BOOST_CHECK_EQUAL((*update)->name,  "/a/b");
+  BOOST_CHECK_EQUAL((*update)->faceId, 3);
+  BOOST_CHECK_EQUAL((*update)->cost, 30);
+  BOOST_CHECK_EQUAL((*update)->action, FibUpdate::ADD_NEXTHOP);
+}
+
+BOOST_AUTO_TEST_CASE(BothFlags)
+{
+  // Empty RIB, should generate 1 update for the inserted face
+  insertFaceEntry("/a", 1, 0, 10, (ndn::nfd::ROUTE_FLAG_CHILD_INHERIT |
+                                   ndn::nfd::ROUTE_FLAG_CAPTURE));
+
+  Rib::FibUpdateList updates = getSortedFibUpdates();
+  BOOST_REQUIRE_EQUAL(updates.size(), 1);
+
+  Rib::FibUpdateList::const_iterator update = updates.begin();
+  BOOST_CHECK_EQUAL((*update)->name,  "/a");
+  BOOST_CHECK_EQUAL((*update)->faceId, 1);
+  BOOST_CHECK_EQUAL((*update)->cost, 10);
+  BOOST_CHECK_EQUAL((*update)->action, FibUpdate::ADD_NEXTHOP);
+
+  // Reset RIB
+  eraseFaceEntry("/a", 1, 0);
+  rib.clearFibUpdates();
+
+  insertFaceEntry("/", 2, 0, 70, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertFaceEntry("/a/b", 3, 0, 30, 0);
+
+  // Clear updates generated from previous insertions
+  rib.clearFibUpdates();
+
+  // Should generate 3 updates, 1 for the inserted face, 1 to add the face to the child,
+  // and 1 to remove the previously inherited entry
+  insertFaceEntry("/a", 1, 0, 10, (ndn::nfd::ROUTE_FLAG_CHILD_INHERIT |
+                                   ndn::nfd::ROUTE_FLAG_CAPTURE));
+
+  updates = getSortedFibUpdates();
+  BOOST_REQUIRE_EQUAL(updates.size(), 3);
+
+  update = updates.begin();
+  BOOST_CHECK_EQUAL((*update)->name,  "/a");
+  BOOST_CHECK_EQUAL((*update)->faceId, 1);
+  BOOST_CHECK_EQUAL((*update)->cost, 10);
+  BOOST_CHECK_EQUAL((*update)->action, FibUpdate::ADD_NEXTHOP);
+
+  ++update;
+  BOOST_CHECK_EQUAL((*update)->name,  "/a/b");
+  BOOST_CHECK_EQUAL((*update)->faceId, 1);
+  BOOST_CHECK_EQUAL((*update)->cost, 10);
+  BOOST_CHECK_EQUAL((*update)->action, FibUpdate::ADD_NEXTHOP);
+
+  ++update;
+  BOOST_CHECK_EQUAL((*update)->name,  "/a/b");
+  BOOST_CHECK_EQUAL((*update)->faceId, 2);
+  BOOST_CHECK_EQUAL((*update)->action, FibUpdate::REMOVE_NEXTHOP);
+}
+
+BOOST_AUTO_TEST_CASE(ChildInherit)
+{
+  insertFaceEntry("/", 1, 0, 50, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertFaceEntry("/a/b", 2, 0, 10, 0);
+  insertFaceEntry("/a/c", 3, 0, 10, ndn::nfd::ROUTE_FLAG_CAPTURE);
+
+  // Clear updates generated from previous insertions
+  rib.clearFibUpdates();
+
+  // Should generate 2 updates: 1 for the inserted face and 1 to add the face to "/a/b"
+  insertFaceEntry("/a", 1, 0, 10, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+
+  Rib::FibUpdateList updates = getSortedFibUpdates();
+  BOOST_REQUIRE_EQUAL(updates.size(), 2);
+
+  Rib::FibUpdateList::const_iterator update = updates.begin();
+  BOOST_CHECK_EQUAL((*update)->name,  "/a");
+  BOOST_CHECK_EQUAL((*update)->faceId, 1);
+  BOOST_CHECK_EQUAL((*update)->cost, 10);
+  BOOST_CHECK_EQUAL((*update)->action, FibUpdate::ADD_NEXTHOP);
+
+  ++update;
+  BOOST_CHECK_EQUAL((*update)->name,  "/a/b");
+  BOOST_CHECK_EQUAL((*update)->faceId, 1);
+  BOOST_CHECK_EQUAL((*update)->cost, 10);
+  BOOST_CHECK_EQUAL((*update)->action, FibUpdate::ADD_NEXTHOP);
+}
+
+BOOST_AUTO_TEST_CASE(Capture)
+{
+  insertFaceEntry("/", 1, 0, 50, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertFaceEntry("/a/b", 2, 0, 10, 0);
+  insertFaceEntry("/a/c", 3, 0, 10, ndn::nfd::ROUTE_FLAG_CAPTURE);
+
+  // Clear updates generated from previous insertions
+  rib.clearFibUpdates();
+
+  // Should generate 2 updates: 1 for the inserted face and
+  // 1 to remove inherited face from "/a/b"
+  insertFaceEntry("/a", 1, 0, 10, ndn::nfd::ROUTE_FLAG_CAPTURE);
+
+  Rib::FibUpdateList updates = getSortedFibUpdates();
+  BOOST_REQUIRE_EQUAL(updates.size(), 2);
+
+  Rib::FibUpdateList::const_iterator update = updates.begin();
+  BOOST_CHECK_EQUAL((*update)->name,  "/a");
+  BOOST_CHECK_EQUAL((*update)->faceId, 1);
+  BOOST_CHECK_EQUAL((*update)->cost, 10);
+  BOOST_CHECK_EQUAL((*update)->action, FibUpdate::ADD_NEXTHOP);
+
+  ++update;
+  BOOST_CHECK_EQUAL((*update)->name,  "/a/b");
+  BOOST_CHECK_EQUAL((*update)->faceId, 1);
+  BOOST_CHECK_EQUAL((*update)->action, FibUpdate::REMOVE_NEXTHOP);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // NewNamespace
+
+BOOST_AUTO_TEST_SUITE_END() // FibUpdates
+
+} // namespace tests
+} // namespace rib
+} // namespace nfd
diff --git a/NFD/tests/rib/fib-updates-update-face.cpp b/NFD/tests/rib/fib-updates-update-face.cpp
new file mode 100644
index 0000000..13f5e21
--- /dev/null
+++ b/NFD/tests/rib/fib-updates-update-face.cpp
@@ -0,0 +1,266 @@
+/* -*- 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/>.
+ */
+
+#include "rib/rib.hpp"
+
+#include "tests/test-common.hpp"
+#include "fib-updates-common.hpp"
+
+namespace nfd {
+namespace rib {
+namespace tests {
+
+BOOST_FIXTURE_TEST_SUITE(FibUpdates, FibUpdatesFixture)
+
+BOOST_AUTO_TEST_SUITE(UpdateFace)
+
+BOOST_AUTO_TEST_CASE(TurnOffChildInheritLowerCost)
+{
+  insertFaceEntry("/", 1, 0, 50, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertFaceEntry("/a", 2, 0, 10, 0);
+  insertFaceEntry("/", 1, 128, 25, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+
+  // Clear updates generated from previous insertions
+  rib.clearFibUpdates();
+
+  // Should generate 2 updates: 1 to update the cost of / face 1 to 50 and
+  // 1 to update the cost of /a face 1 to 50
+  insertFaceEntry("/", 1, 128, 75, 0);
+
+  Rib::FibUpdateList updates = getSortedFibUpdates();
+  BOOST_REQUIRE_EQUAL(updates.size(), 2);
+
+  Rib::FibUpdateList::const_iterator update = updates.begin();
+  BOOST_CHECK_EQUAL((*update)->name,  "/");
+  BOOST_CHECK_EQUAL((*update)->faceId, 1);
+  BOOST_CHECK_EQUAL((*update)->cost,   50);
+  BOOST_CHECK_EQUAL((*update)->action, FibUpdate::ADD_NEXTHOP);
+
+  ++update;
+  BOOST_CHECK_EQUAL((*update)->name,  "/a");
+  BOOST_CHECK_EQUAL((*update)->faceId, 1);
+  BOOST_CHECK_EQUAL((*update)->cost,   50);
+  BOOST_CHECK_EQUAL((*update)->action, FibUpdate::ADD_NEXTHOP);
+}
+
+BOOST_AUTO_TEST_CASE(UpdateOnLowerCostOnly)
+{
+  insertFaceEntry("/", 1, 0, 50, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertFaceEntry("/a", 2, 0, 10, 0);
+  insertFaceEntry("/", 1, 128, 100, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+
+  // Clear updates generated from previous insertions
+  rib.clearFibUpdates();
+
+  // Should generate 0 updates
+  insertFaceEntry("/", 1, 128, 75, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+
+  Rib::FibUpdateList updates = getSortedFibUpdates();
+  BOOST_REQUIRE_EQUAL(updates.size(), 0);
+
+  // Clear updates generated from previous insertions
+  rib.clearFibUpdates();
+
+  // Should generate 2 updates
+  insertFaceEntry("/", 1, 128, 25, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+
+  updates = getSortedFibUpdates();
+  BOOST_REQUIRE_EQUAL(updates.size(), 2);
+
+  Rib::FibUpdateList::const_iterator update = updates.begin();
+  BOOST_CHECK_EQUAL((*update)->name,  "/");
+  BOOST_CHECK_EQUAL((*update)->faceId, 1);
+  BOOST_CHECK_EQUAL((*update)->cost,   25);
+  BOOST_CHECK_EQUAL((*update)->action, FibUpdate::ADD_NEXTHOP);
+
+  ++update;
+  BOOST_CHECK_EQUAL((*update)->name,  "/a");
+  BOOST_CHECK_EQUAL((*update)->faceId, 1);
+  BOOST_CHECK_EQUAL((*update)->cost,   25);
+  BOOST_CHECK_EQUAL((*update)->action, FibUpdate::ADD_NEXTHOP);
+}
+
+BOOST_AUTO_TEST_CASE(NoChangeInCost)
+{
+  insertFaceEntry("/", 1, 0, 50, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertFaceEntry("/a", 2, 0, 100, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertFaceEntry("/a/b", 3, 0, 10, 0);
+  insertFaceEntry("/a/c", 4, 0, 10, ndn::nfd::ROUTE_FLAG_CAPTURE);
+
+  // Clear updates generated from previous insertions
+  rib.clearFibUpdates();
+
+  // Should generate 0 updates
+  insertFaceEntry("/a", 2, 0, 100, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+
+  Rib::FibUpdateList updates = getSortedFibUpdates();
+  BOOST_REQUIRE_EQUAL(updates.size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(ChangeCost)
+{
+  insertFaceEntry("/", 1, 0, 50, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertFaceEntry("/a", 2, 0, 100, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertFaceEntry("/a/b", 3, 0, 10, 0);
+  insertFaceEntry("/a/c", 4, 0, 10, ndn::nfd::ROUTE_FLAG_CAPTURE);
+
+  // Clear updates generated from previous insertions
+  rib.clearFibUpdates();
+
+  // Should generate 2 updates: 1 to add face2 with new cost to /a and
+  // 1 to add face2 with new cost to /a/b
+  insertFaceEntry("/a", 2, 0, 300, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+
+  Rib::FibUpdateList updates = getSortedFibUpdates();
+  BOOST_REQUIRE_EQUAL(updates.size(), 2);
+
+  Rib::FibUpdateList::const_iterator update = updates.begin();
+  BOOST_CHECK_EQUAL((*update)->name,  "/a");
+  BOOST_CHECK_EQUAL((*update)->faceId, 2);
+  BOOST_CHECK_EQUAL((*update)->cost,   300);
+  BOOST_CHECK_EQUAL((*update)->action, FibUpdate::ADD_NEXTHOP);
+
+  ++update;
+  BOOST_CHECK_EQUAL((*update)->name,  "/a/b");
+  BOOST_CHECK_EQUAL((*update)->faceId, 2);
+  BOOST_CHECK_EQUAL((*update)->cost,   300);
+  BOOST_CHECK_EQUAL((*update)->action, FibUpdate::ADD_NEXTHOP);
+}
+
+BOOST_AUTO_TEST_CASE(TurnOnChildInherit)
+{
+  insertFaceEntry("/", 1, 0, 50, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertFaceEntry("/a", 2, 0, 10, 0);
+  insertFaceEntry("/a/b", 3, 0, 10, 0);
+  insertFaceEntry("/a/c", 4, 0, 10, ndn::nfd::ROUTE_FLAG_CAPTURE);
+
+  // Clear updates generated from previous insertions
+  rib.clearFibUpdates();
+
+  // Turn on child inherit flag for the entry in /a
+  // Should generate 1 updates: 1 to add face to /a/b
+  insertFaceEntry("/a", 2, 0, 10, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+
+  Rib::FibUpdateList updates = getSortedFibUpdates();
+  BOOST_REQUIRE_EQUAL(updates.size(), 1);
+
+  Rib::FibUpdateList::const_iterator update = updates.begin();
+  BOOST_CHECK_EQUAL((*update)->name,  "/a/b");
+  BOOST_CHECK_EQUAL((*update)->faceId, 2);
+  BOOST_CHECK_EQUAL((*update)->cost,   10);
+  BOOST_CHECK_EQUAL((*update)->action, FibUpdate::ADD_NEXTHOP);
+}
+
+BOOST_AUTO_TEST_CASE(TurnOffChildInherit)
+{
+  insertFaceEntry("/", 1, 0, 50, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertFaceEntry("/a", 1, 0, 100, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertFaceEntry("/a/b", 2, 0, 10, 0);
+  insertFaceEntry("/a/c", 1, 0, 25, 0);
+
+  // Clear updates generated from previous insertions
+  rib.clearFibUpdates();
+
+  // Turn off child inherit flag for the entry in /a
+  // Should generate 1 update: 1 to add face1 to /a/b
+  insertFaceEntry("/a", 1, 0, 100, 0);
+
+  Rib::FibUpdateList updates = getSortedFibUpdates();
+  BOOST_REQUIRE_EQUAL(updates.size(), 1);
+
+  Rib::FibUpdateList::const_iterator update = updates.begin();
+  BOOST_CHECK_EQUAL((*update)->name,  "/a/b");
+  BOOST_CHECK_EQUAL((*update)->faceId, 1);
+  BOOST_CHECK_EQUAL((*update)->cost,   50);
+  BOOST_CHECK_EQUAL((*update)->action, FibUpdate::ADD_NEXTHOP);
+}
+
+BOOST_AUTO_TEST_CASE(TurnOnCapture)
+{
+  insertFaceEntry("/", 1, 0, 50, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertFaceEntry("/a", 2, 0, 10, 0);
+  insertFaceEntry("/a/b", 3, 0, 10, 0);
+  insertFaceEntry("/a/c", 1, 0, 10, 0);
+
+  // Clear updates generated from previous insertions
+  rib.clearFibUpdates();
+
+  // Turn on capture flag for the entry in /a
+  // Should generate 2 updates: 1 to remove face1 from /a and
+  // 1 to remove face1 from /a/b
+  insertFaceEntry("/a", 2, 0, 10, ndn::nfd::ROUTE_FLAG_CAPTURE);
+
+  Rib::FibUpdateList updates = getSortedFibUpdates();
+  BOOST_REQUIRE_EQUAL(updates.size(), 2);
+
+  Rib::FibUpdateList::const_iterator update = updates.begin();
+  BOOST_CHECK_EQUAL((*update)->name,  "/a");
+  BOOST_CHECK_EQUAL((*update)->faceId, 1);
+  BOOST_CHECK_EQUAL((*update)->action, FibUpdate::REMOVE_NEXTHOP);
+
+  ++update;
+  BOOST_CHECK_EQUAL((*update)->name,  "/a/b");
+  BOOST_CHECK_EQUAL((*update)->faceId, 1);
+  BOOST_CHECK_EQUAL((*update)->action, FibUpdate::REMOVE_NEXTHOP);
+}
+
+BOOST_AUTO_TEST_CASE(TurnOffCapture)
+{
+  insertFaceEntry("/", 1, 0, 50, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertFaceEntry("/a", 2, 0, 10, ndn::nfd::ROUTE_FLAG_CAPTURE);
+  insertFaceEntry("/a/b", 3, 0, 10, 0);
+  insertFaceEntry("/a/c", 1, 0, 10, 0);
+
+  // Clear updates generated from previous insertions
+  rib.clearFibUpdates();
+
+  // Turn off capture flag for the entry in /a
+  // Should generate 2 updates: 1 to add face1 to /a and
+  // 1 to add face1 to /a/b
+  insertFaceEntry("/a", 2, 0, 10, 0);
+
+  Rib::FibUpdateList updates = getSortedFibUpdates();
+  BOOST_REQUIRE_EQUAL(updates.size(), 2);
+
+  Rib::FibUpdateList::const_iterator update = updates.begin();
+  BOOST_CHECK_EQUAL((*update)->name,  "/a");
+  BOOST_CHECK_EQUAL((*update)->faceId, 1);
+  BOOST_CHECK_EQUAL((*update)->cost, 50);
+  BOOST_CHECK_EQUAL((*update)->action, FibUpdate::ADD_NEXTHOP);
+
+  ++update;
+  BOOST_CHECK_EQUAL((*update)->name,  "/a/b");
+  BOOST_CHECK_EQUAL((*update)->faceId, 1);
+  BOOST_CHECK_EQUAL((*update)->cost, 50);
+  BOOST_CHECK_EQUAL((*update)->action, FibUpdate::ADD_NEXTHOP);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // UpdateFace
+
+BOOST_AUTO_TEST_SUITE_END() // FibUpdates
+
+} // namespace tests
+} // namespace rib
+} // namespace nfd
diff --git a/NFD/tests/rib/remote-registrator.cpp b/NFD/tests/rib/remote-registrator.cpp
new file mode 100644
index 0000000..c0ef540
--- /dev/null
+++ b/NFD/tests/rib/remote-registrator.cpp
@@ -0,0 +1,520 @@
+/* -*- 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/>.
+ */
+
+#include "rib/remote-registrator.hpp"
+
+#include "tests/limited-io.hpp"
+#include "tests/identity-management-fixture.hpp"
+#include <ndn-cxx/util/dummy-client-face.hpp>
+
+namespace nfd {
+namespace rib {
+namespace tests {
+
+class RemoteRegistratorFixture : public nfd::tests::IdentityManagementFixture
+                               , public nfd::tests::UnitTestTimeFixture
+{
+public:
+  RemoteRegistratorFixture()
+    : face(ndn::util::makeDummyClientFace(getGlobalIoService()))
+    , controller(make_shared<ndn::nfd::Controller>(std::ref(*face), m_keyChain))
+    , remoteRegistrator(make_shared<RemoteRegistrator>(std::ref(*controller),
+                                                       m_keyChain,
+                                                       rib))
+    , COMMAND_PREFIX("/localhop/nfd/rib")
+    , REGISTER_VERB("register")
+    , UNREGISTER_VERB("unregister")
+  {
+    readConfig();
+
+    remoteRegistrator->enable();
+
+    advanceClocks(time::milliseconds(1));
+    face->sentInterests.clear();
+  }
+
+  void
+  readConfig(bool isSetRetry = false)
+  {
+    ConfigFile config;
+    config.addSectionHandler("remote_register",
+                             bind(&RemoteRegistrator::loadConfig, remoteRegistrator, _1));
+
+
+    if (isSetRetry)
+      {
+        const std::string CONFIG_STRING =
+        "remote_register\n"
+        "{\n"
+        "  cost 15\n"
+        "  timeout 1000\n"
+        "  retry 1\n"
+        "  refresh_interval 5\n"
+        "}";
+
+        config.parse(CONFIG_STRING, true, "test-remote-register");
+      }
+    else
+      {
+        const std::string CONFIG_STRING =
+        "remote_register\n"
+        "{\n"
+        "  cost 15\n"
+        "  timeout 100000\n"
+        "  retry 0\n"
+        "  refresh_interval 5\n"
+        "}";
+
+        config.parse(CONFIG_STRING, true, "test-remote-register");
+      }
+  }
+
+  void
+  waitForTimeout()
+  {
+    advanceClocks(time::milliseconds(100), time::seconds(1));
+  }
+
+  void
+  insertEntryWithIdentity(Name identity,
+                          name::Component appName = DEFAULT_APP_NAME,
+                          uint64_t faceId = 0)
+  {
+    BOOST_CHECK_EQUAL(addIdentity(identity), true);
+
+    FaceEntry faceEntry;
+    faceEntry.faceId = faceId;
+
+    rib.insert(identity.append(appName), faceEntry);
+
+    advanceClocks(time::milliseconds(1));
+  }
+
+  void
+  insertEntryWithoutIdentity(Name identity,
+                             name::Component appName = DEFAULT_APP_NAME,
+                             uint64_t faceId = 0)
+  {
+    FaceEntry faceEntry;
+    faceEntry.faceId = faceId;
+
+    rib.insert(identity.append(appName), faceEntry);
+
+    advanceClocks(time::milliseconds(1));
+  }
+
+  void
+  eraseEntryWithIdentity(Name identity,
+                         name::Component appName = DEFAULT_APP_NAME,
+                         uint64_t faceId = 0)
+  {
+    BOOST_CHECK_EQUAL(addIdentity(identity), true);
+
+    FaceEntry faceEntry;
+    faceEntry.faceId = faceId;
+
+    rib.erase(identity.append(appName), faceEntry);
+
+    advanceClocks(time::milliseconds(1));
+  }
+
+  void
+  eraseEntryWithoutIdentity(Name identity,
+                            name::Component appName = DEFAULT_APP_NAME,
+                            uint64_t faceId = 0)
+  {
+    FaceEntry faceEntry;
+    faceEntry.faceId = faceId;
+
+    rib.erase(identity.append(appName), faceEntry);
+
+    advanceClocks(time::milliseconds(1));
+  }
+
+  void
+  eraseFace(uint64_t faceId)
+  {
+    rib.erase(faceId);
+
+    advanceClocks(time::milliseconds(1));
+  }
+
+  void
+  connectToHub()
+  {
+    rib.insert(COMMAND_PREFIX, FaceEntry());
+
+    advanceClocks(time::milliseconds(1));
+  }
+
+  void
+  disconnectToHub()
+  {
+    rib.erase(COMMAND_PREFIX, FaceEntry());
+
+    advanceClocks(time::milliseconds(1));
+  }
+
+  void
+  extractParameters(Interest& interest, Name::Component& verb,
+                    ndn::nfd::ControlParameters& extractedParameters)
+  {
+    const Name& name = interest.getName();
+    verb = name[COMMAND_PREFIX.size()];
+    const Name::Component& parameterComponent = name[COMMAND_PREFIX.size() + 1];
+
+    Block rawParameters = parameterComponent.blockFromValue();
+    extractedParameters.wireDecode(rawParameters);
+  }
+
+public:
+  Rib rib;
+  shared_ptr<ndn::util::DummyClientFace> face;
+  shared_ptr<ndn::nfd::Controller> controller;
+  shared_ptr<RemoteRegistrator> remoteRegistrator;
+
+  const Name COMMAND_PREFIX;
+  const name::Component REGISTER_VERB;
+  const name::Component UNREGISTER_VERB;
+
+  static const name::Component DEFAULT_APP_NAME;
+};
+
+const name::Component RemoteRegistratorFixture::DEFAULT_APP_NAME("app");
+
+BOOST_FIXTURE_TEST_SUITE(RemoteRegistrator, RemoteRegistratorFixture)
+
+BOOST_FIXTURE_TEST_CASE(AutoTest, RemoteRegistratorFixture)
+{
+  BOOST_REQUIRE_EQUAL(1, 1);
+}
+
+BOOST_FIXTURE_TEST_CASE(RegisterWithoutConnection, RemoteRegistratorFixture)
+{
+  insertEntryWithIdentity("/remote/register");
+
+  BOOST_REQUIRE_EQUAL(face->sentInterests.size(), 0);
+}
+
+BOOST_FIXTURE_TEST_CASE(RegisterWithoutIdentity, RemoteRegistratorFixture)
+{
+  connectToHub();
+
+  insertEntryWithoutIdentity("/remote/register");
+
+  BOOST_REQUIRE_EQUAL(face->sentInterests.size(), 0);
+}
+
+BOOST_FIXTURE_TEST_CASE(RegisterWithHubPrefix, RemoteRegistratorFixture)
+{
+  connectToHub();
+
+  BOOST_REQUIRE_EQUAL(face->sentInterests.size(), 0);
+}
+
+BOOST_FIXTURE_TEST_CASE(RegisterWithLocalPrefix, RemoteRegistratorFixture)
+{
+  connectToHub();
+
+  insertEntryWithIdentity("/localhost/prefix");
+
+  BOOST_REQUIRE_EQUAL(face->sentInterests.size(), 0);
+}
+
+BOOST_FIXTURE_TEST_CASE(RegisterBasic, RemoteRegistratorFixture)
+{
+  connectToHub();
+
+  Name identity("/remote/register");
+  insertEntryWithIdentity(identity);
+
+  BOOST_REQUIRE_EQUAL(face->sentInterests.size(), 1);
+
+  Interest& request = face->sentInterests[0];
+
+  ndn::nfd::ControlParameters extractedParameters;
+  Name::Component verb;
+  extractParameters(request, verb, extractedParameters);
+
+  BOOST_CHECK_EQUAL(verb, REGISTER_VERB);
+  BOOST_CHECK_EQUAL(extractedParameters.getName(), identity);
+}
+
+BOOST_FIXTURE_TEST_CASE(RegisterAdvanced, RemoteRegistratorFixture)
+{
+  connectToHub();
+
+  Name identity("/remote/register");
+  Name identityAddRib("/remote/register/rib");
+  insertEntryWithIdentity(identityAddRib);
+
+  BOOST_REQUIRE_EQUAL(face->sentInterests.size(), 1);
+
+  Interest& request = face->sentInterests[0];
+
+  ndn::nfd::ControlParameters extractedParameters;
+  Name::Component verb;
+  extractParameters(request, verb, extractedParameters);
+
+  BOOST_CHECK_EQUAL(verb, REGISTER_VERB);
+  BOOST_CHECK_EQUAL(extractedParameters.getName(), identity);
+}
+
+BOOST_FIXTURE_TEST_CASE(RegisterWithRedundantCallback, RemoteRegistratorFixture)
+{
+  remoteRegistrator->enable();
+
+  connectToHub();
+
+  Name identity("/remote/register");
+  insertEntryWithIdentity(identity);
+
+  BOOST_REQUIRE_EQUAL(face->sentInterests.size(), 1);
+
+  Interest& request = face->sentInterests[0];
+
+  ndn::nfd::ControlParameters extractedParameters;
+  Name::Component verb;
+  extractParameters(request, verb, extractedParameters);
+
+  BOOST_CHECK_EQUAL(verb, REGISTER_VERB);
+  BOOST_CHECK_EQUAL(extractedParameters.getName(), identity);
+}
+
+BOOST_FIXTURE_TEST_CASE(RegisterRetry, RemoteRegistratorFixture)
+{
+  // setRetry
+  readConfig(true);
+
+  connectToHub();
+
+  Name identity("/remote/register");
+  insertEntryWithIdentity(identity);
+
+  waitForTimeout();
+
+  BOOST_REQUIRE_EQUAL(face->sentInterests.size(), 2);
+
+  Interest& requestFirst  = face->sentInterests[0];
+  Interest& requestSecond = face->sentInterests[1];
+
+  ndn::nfd::ControlParameters extractedParametersFirst, extractedParametersSecond;
+  Name::Component verbFirst, verbSecond;
+  extractParameters(requestFirst,  verbFirst,  extractedParametersFirst);
+  extractParameters(requestSecond, verbSecond, extractedParametersSecond);
+
+  BOOST_CHECK_EQUAL(verbFirst,  REGISTER_VERB);
+  BOOST_CHECK_EQUAL(verbSecond, REGISTER_VERB);
+  BOOST_CHECK_EQUAL(extractedParametersFirst.getName(),  identity);
+  BOOST_CHECK_EQUAL(extractedParametersSecond.getName(), identity);
+}
+
+BOOST_FIXTURE_TEST_CASE(UnregisterWithoutInsert, RemoteRegistratorFixture)
+{
+  connectToHub();
+
+  eraseEntryWithIdentity("/remote/register");
+
+  BOOST_REQUIRE_EQUAL(face->sentInterests.size(), 0);
+}
+
+BOOST_FIXTURE_TEST_CASE(UnregisterWithoutConnection, RemoteRegistratorFixture)
+{
+  connectToHub();
+
+  disconnectToHub();
+
+  Name indentity("/remote/register");
+  remoteRegistrator->m_regEntries.insert(
+            nfd::rib::RemoteRegistrator::RegisteredEntry(indentity, EventId()));
+
+  eraseEntryWithIdentity(indentity);
+
+  BOOST_REQUIRE_EQUAL(face->sentInterests.size(), 0);
+}
+
+BOOST_FIXTURE_TEST_CASE(UnregisterWithoutSuccessfullRegistration,
+                        RemoteRegistratorFixture)
+{
+  connectToHub();
+
+  Name identity("/remote/register");
+
+  insertEntryWithIdentity(identity);
+
+  eraseEntryWithIdentity(identity);
+
+  BOOST_REQUIRE_EQUAL(face->sentInterests.size(), 1);
+
+  Interest& request = face->sentInterests[0];
+
+  ndn::nfd::ControlParameters extractedParameters;
+  Name::Component verb;
+  extractParameters(request, verb, extractedParameters);
+
+  BOOST_CHECK_EQUAL(verb, REGISTER_VERB);
+  BOOST_CHECK_EQUAL(extractedParameters.getName(), identity);
+}
+
+BOOST_FIXTURE_TEST_CASE(UnregisterBasic, RemoteRegistratorFixture)
+{
+  connectToHub();
+
+  Name identity("/remote/register");
+
+  insertEntryWithIdentity(identity);
+
+  EventId event;
+
+  remoteRegistrator->m_regEntries.insert(
+          nfd::rib::RemoteRegistrator::RegisteredEntry(identity, event));
+
+  eraseEntryWithIdentity(identity);
+
+  BOOST_REQUIRE_EQUAL(face->sentInterests.size(), 2);
+
+  Interest& request = face->sentInterests[1];
+
+  ndn::nfd::ControlParameters extractedParameters;
+  Name::Component verb;
+  extractParameters(request, verb, extractedParameters);
+
+  BOOST_CHECK_EQUAL(verb, UNREGISTER_VERB);
+  BOOST_CHECK_EQUAL(extractedParameters.getName(), identity);
+}
+
+BOOST_FIXTURE_TEST_CASE(UnregisterAdvanced, RemoteRegistratorFixture)
+{
+  connectToHub();
+
+  Name identityShort("/remote/register");
+  Name identityLong("/remote/register/long");
+
+  EventId eventShort;
+  EventId eventLong;
+
+  insertEntryWithIdentity(identityShort, name::Component("appA"));
+
+  remoteRegistrator->m_regEntries.insert(
+          nfd::rib::RemoteRegistrator::RegisteredEntry(identityShort,
+                                                       eventShort));
+
+  insertEntryWithIdentity(identityShort, name::Component("appB"));
+
+  insertEntryWithIdentity(identityLong);
+
+  remoteRegistrator->m_regEntries.insert(
+          nfd::rib::RemoteRegistrator::RegisteredEntry(identityLong,
+                                                       eventLong));
+
+  // two registration commands are generated for identityShort and identityLong
+  BOOST_REQUIRE_EQUAL(face->sentInterests.size(), 2);
+
+  eraseEntryWithIdentity(identityShort, name::Component("appA"));
+
+  // no unregistration command is generated as appB also exists
+  BOOST_REQUIRE_EQUAL(face->sentInterests.size(), 2);
+
+  eraseEntryWithIdentity(identityShort, name::Component("appB"));
+
+  // one unregistration command is generated for identityShort
+  BOOST_REQUIRE_EQUAL(face->sentInterests.size(), 3);
+
+  Interest& request = face->sentInterests[2];
+
+  ndn::nfd::ControlParameters extractedParameters;
+  Name::Component verb;
+  extractParameters(request, verb, extractedParameters);
+
+  BOOST_CHECK_EQUAL(verb, UNREGISTER_VERB);
+  BOOST_CHECK_EQUAL(extractedParameters.getName(), identityShort);
+}
+
+BOOST_FIXTURE_TEST_CASE(EraseFace, RemoteRegistratorFixture)
+{
+  connectToHub();
+
+  Name identity("/remote/register");
+  uint64_t faceId = 517;
+
+  insertEntryWithIdentity(identity, DEFAULT_APP_NAME, faceId);
+
+  EventId event;
+
+  remoteRegistrator->m_regEntries.insert(
+          nfd::rib::RemoteRegistrator::RegisteredEntry(identity, event));
+
+  eraseFace(faceId);
+
+  BOOST_REQUIRE_EQUAL(face->sentInterests.size(), 2);
+
+  Interest& request = face->sentInterests[1];
+
+  ndn::nfd::ControlParameters extractedParameters;
+  Name::Component verb;
+  extractParameters(request, verb, extractedParameters);
+
+  BOOST_CHECK_EQUAL(verb, UNREGISTER_VERB);
+  BOOST_CHECK_EQUAL(extractedParameters.getName(), identity);
+}
+
+BOOST_FIXTURE_TEST_CASE(RebuildConnection, RemoteRegistratorFixture)
+{
+  connectToHub();
+
+  Name identity("/remote/register");
+
+  insertEntryWithIdentity(identity);
+
+  EventId event;
+
+  remoteRegistrator->m_regEntries.insert(
+          nfd::rib::RemoteRegistrator::RegisteredEntry(identity, event));
+
+  disconnectToHub();
+
+  connectToHub();
+
+  BOOST_REQUIRE_EQUAL(face->sentInterests.size(), 2);
+
+  Interest& request1 = face->sentInterests[0];
+  Interest& request2 = face->sentInterests[1];
+
+  ndn::nfd::ControlParameters extractedParameters1, extractedParameters2;
+  Name::Component verb1, verb2;
+  extractParameters(request1, verb1, extractedParameters1);
+  extractParameters(request2, verb2, extractedParameters2);
+
+  BOOST_CHECK_EQUAL(verb1, REGISTER_VERB);
+  BOOST_CHECK_EQUAL(verb2, REGISTER_VERB);
+  BOOST_CHECK_EQUAL(extractedParameters1.getName(),
+                    extractedParameters2.getName());
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace rib
+} // namespace nfd
diff --git a/NFD/tests/rib/rib-manager.cpp b/NFD/tests/rib/rib-manager.cpp
new file mode 100644
index 0000000..39a4a5d
--- /dev/null
+++ b/NFD/tests/rib/rib-manager.cpp
@@ -0,0 +1,360 @@
+/* -*- 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/>.
+ */
+
+#include "rib/rib-manager.hpp"
+#include <ndn-cxx/management/nfd-face-status.hpp>
+#include "rib/rib-status-publisher-common.hpp"
+
+#include "tests/test-common.hpp"
+#include "tests/limited-io.hpp"
+#include <ndn-cxx/util/dummy-client-face.hpp>
+
+namespace nfd {
+namespace rib {
+namespace tests {
+
+class RibManagerFixture : public nfd::tests::BaseFixture
+{
+public:
+  RibManagerFixture()
+    : COMMAND_PREFIX("/localhost/nfd/rib")
+    , ADD_NEXTHOP_VERB("add-nexthop")
+    , REMOVE_NEXTHOP_VERB("remove-nexthop")
+  {
+    face = ndn::util::makeDummyClientFace();
+
+    manager = make_shared<RibManager>(ndn::ref(*face));
+    manager->registerWithNfd();
+
+    face->processEvents(time::milliseconds(1));
+    face->sentInterests.clear();
+  }
+
+  ~RibManagerFixture()
+  {
+    manager.reset();
+    face.reset();
+  }
+
+  void extractParameters(Interest& interest, Name::Component& verb,
+                         ControlParameters& extractedParameters)
+  {
+    const Name& name = interest.getName();
+    verb = name[COMMAND_PREFIX.size()];
+    const Name::Component& parameterComponent = name[COMMAND_PREFIX.size() + 1];
+
+    Block rawParameters = parameterComponent.blockFromValue();
+    extractedParameters.wireDecode(rawParameters);
+  }
+
+  void receiveCommandInterest(Name& name, ControlParameters& parameters)
+  {
+    receiveCommandInterest(name.append(parameters.wireEncode()));
+  }
+
+  void receiveCommandInterest(const Name& name)
+  {
+    Interest command(name);
+
+    face->receive(command);
+    face->processEvents(time::milliseconds(1));
+  }
+
+public:
+  shared_ptr<RibManager> manager;
+  shared_ptr<ndn::util::DummyClientFace> face;
+
+  const Name COMMAND_PREFIX;
+  const Name::Component ADD_NEXTHOP_VERB;
+  const Name::Component REMOVE_NEXTHOP_VERB;
+};
+
+class AuthorizedRibManager : public RibManagerFixture
+{
+public:
+  AuthorizedRibManager()
+  {
+    ConfigFile config;
+    manager->setConfigFile(config);
+
+    const std::string CONFIG_STRING =
+    "rib\n"
+    "{\n"
+    "  localhost_security\n"
+    "  {\n"
+    "    trust-anchor\n"
+    "    {\n"
+    "      type any\n"
+    "    }\n"
+    "  }"
+    "}";
+
+    config.parse(CONFIG_STRING, true, "test-rib");
+  }
+};
+
+typedef RibManagerFixture UnauthorizedRibManager;
+
+BOOST_FIXTURE_TEST_SUITE(RibManager, RibManagerFixture)
+
+BOOST_FIXTURE_TEST_CASE(ShortName, AuthorizedRibManager)
+{
+  Name commandName("/localhost/nfd/rib");
+  receiveCommandInterest(commandName);
+  // TODO verify error response
+}
+
+BOOST_FIXTURE_TEST_CASE(Basic, AuthorizedRibManager)
+{
+  ControlParameters parameters;
+  parameters
+    .setName("/hello")
+    .setFaceId(1)
+    .setCost(10)
+    .setFlags(0)
+    .setOrigin(128)
+    .setExpirationPeriod(ndn::time::milliseconds::max());
+
+  Name commandName("/localhost/nfd/rib/register");
+
+  receiveCommandInterest(commandName, parameters);
+
+  BOOST_REQUIRE_EQUAL(face->sentInterests.size(), 1);
+}
+
+BOOST_FIXTURE_TEST_CASE(Register, AuthorizedRibManager)
+{
+  ControlParameters parameters;
+  parameters
+    .setName("/hello")
+    .setFaceId(1)
+    .setCost(10)
+    .setFlags(0)
+    .setOrigin(128)
+    .setExpirationPeriod(ndn::time::milliseconds::max());
+
+  Name commandName("/localhost/nfd/rib/register");
+
+  receiveCommandInterest(commandName, parameters);
+
+  BOOST_REQUIRE_EQUAL(face->sentInterests.size(), 1);
+
+  Interest& request = face->sentInterests[0];
+
+  ControlParameters extractedParameters;
+  Name::Component verb;
+  extractParameters(request, verb, extractedParameters);
+
+  BOOST_CHECK_EQUAL(verb, ADD_NEXTHOP_VERB);
+  BOOST_CHECK_EQUAL(extractedParameters.getName(), parameters.getName());
+  BOOST_CHECK_EQUAL(extractedParameters.getFaceId(), parameters.getFaceId());
+  BOOST_CHECK_EQUAL(extractedParameters.getCost(), parameters.getCost());
+}
+
+BOOST_FIXTURE_TEST_CASE(Unregister, AuthorizedRibManager)
+{
+  ControlParameters addParameters;
+  addParameters
+    .setName("/hello")
+    .setFaceId(1)
+    .setCost(10)
+    .setFlags(0)
+    .setOrigin(128)
+    .setExpirationPeriod(ndn::time::milliseconds::max());
+
+  Name registerName("/localhost/nfd/rib/register");
+
+  receiveCommandInterest(registerName, addParameters);
+  face->sentInterests.clear();
+
+  ControlParameters removeParameters;
+  removeParameters
+    .setName("/hello")
+    .setFaceId(1)
+    .setOrigin(128);
+
+  Name unregisterName("/localhost/nfd/rib/unregister");
+
+  receiveCommandInterest(unregisterName, removeParameters);
+
+  BOOST_REQUIRE_EQUAL(face->sentInterests.size(), 1);
+
+  Interest& request = face->sentInterests[0];
+
+  ControlParameters extractedParameters;
+  Name::Component verb;
+  extractParameters(request, verb, extractedParameters);
+
+  BOOST_CHECK_EQUAL(verb, REMOVE_NEXTHOP_VERB);
+  BOOST_CHECK_EQUAL(extractedParameters.getName(), removeParameters.getName());
+  BOOST_CHECK_EQUAL(extractedParameters.getFaceId(), removeParameters.getFaceId());
+}
+
+BOOST_FIXTURE_TEST_CASE(UnauthorizedCommand, UnauthorizedRibManager)
+{
+  ControlParameters parameters;
+  parameters
+    .setName("/hello")
+    .setFaceId(1)
+    .setCost(10)
+    .setFlags(0)
+    .setOrigin(128)
+    .setExpirationPeriod(ndn::time::milliseconds::max());
+
+  Name commandName("/localhost/nfd/rib/register");
+
+  BOOST_REQUIRE_EQUAL(face->sentInterests.size(), 0);
+
+  receiveCommandInterest(commandName, parameters);
+
+  BOOST_REQUIRE_EQUAL(face->sentInterests.size(), 0);
+}
+
+BOOST_FIXTURE_TEST_CASE(RibStatusRequest, AuthorizedRibManager)
+{
+  FaceEntry entry;
+  Name name("/");
+  entry.faceId = 1;
+  entry.origin = 128;
+  entry.cost = 32;
+  entry.flags = ndn::nfd::ROUTE_FLAG_CAPTURE;
+
+  ControlParameters parameters;
+  parameters
+    .setName(name)
+    .setFaceId(entry.faceId)
+    .setOrigin(entry.origin)
+    .setCost(entry.cost)
+    .setFlags(entry.flags)
+    .setExpirationPeriod(ndn::time::milliseconds::max());
+
+  Name commandName("/localhost/nfd/rib/register");
+
+  BOOST_REQUIRE_EQUAL(face->sentInterests.size(), 0);
+
+  receiveCommandInterest(commandName, parameters);
+  face->sentInterests.clear();
+  face->sentDatas.clear();
+
+  face->receive(Interest("/localhost/nfd/rib/list"));
+  face->processEvents(time::milliseconds(1));
+
+  BOOST_REQUIRE_EQUAL(face->sentDatas.size(), 1);
+  RibStatusPublisherFixture::decodeRibEntryBlock(face->sentDatas[0], name, entry);
+}
+
+BOOST_FIXTURE_TEST_CASE(CancelExpirationEvent, AuthorizedRibManager)
+{
+  // Register face
+  ControlParameters addParameters;
+  addParameters
+    .setName("/expire")
+    .setFaceId(1)
+    .setCost(10)
+    .setFlags(0)
+    .setOrigin(128)
+    .setExpirationPeriod(ndn::time::milliseconds(500));
+
+  Name registerName("/localhost/nfd/rib/register");
+
+  receiveCommandInterest(registerName, addParameters);
+  face->sentInterests.clear();
+
+  // Unregister face
+  ControlParameters removeParameters;
+  removeParameters
+    .setName("/expire")
+    .setFaceId(1)
+    .setOrigin(128);
+
+  Name unregisterName("/localhost/nfd/rib/unregister");
+
+  receiveCommandInterest(unregisterName, removeParameters);
+
+  // Reregister face
+  Name reRegisterName("/localhost/nfd/rib/register");
+  addParameters.setExpirationPeriod(ndn::time::milliseconds::max());
+  receiveCommandInterest(reRegisterName, addParameters);
+
+  nfd::tests::LimitedIo limitedIo;
+  limitedIo.run(nfd::tests::LimitedIo::UNLIMITED_OPS, time::seconds(1));
+
+  BOOST_REQUIRE_EQUAL(manager->m_managedRib.size(), 1);
+}
+
+BOOST_FIXTURE_TEST_CASE(RemoveInvalidFaces, AuthorizedRibManager)
+{
+  // Register valid face
+  ControlParameters validParameters;
+  validParameters
+    .setName("/test")
+    .setFaceId(1);
+
+  Name validName("/localhost/nfd/rib/register");
+  receiveCommandInterest(validName, validParameters);
+
+  // Register invalid face
+  ControlParameters invalidParameters;
+  invalidParameters
+    .setName("/test")
+    .setFaceId(2);
+
+  Name invalidName("/localhost/nfd/rib/register");
+  receiveCommandInterest(invalidName, invalidParameters);
+
+  BOOST_REQUIRE_EQUAL(manager->m_managedRib.size(), 2);
+
+  // Receive status with only faceId: 1
+  ndn::nfd::FaceStatus status;
+  status.setFaceId(1);
+
+  shared_ptr<Data> data = nfd::tests::makeData("/localhost/nfd/faces/list");
+  data->setContent(status.wireEncode());
+
+  shared_ptr<ndn::OBufferStream> buffer = make_shared<ndn::OBufferStream>();
+  buffer->write(reinterpret_cast<const char*>(data->getContent().value()),
+                data->getContent().value_size());
+
+  manager->removeInvalidFaces(buffer);
+
+  // Run scheduler
+  nfd::tests::LimitedIo limitedIo;
+  limitedIo.run(nfd::tests::LimitedIo::UNLIMITED_OPS, time::seconds(1));
+
+  BOOST_REQUIRE_EQUAL(manager->m_managedRib.size(), 1);
+
+  Rib::const_iterator it = manager->m_managedRib.find("/test");
+  BOOST_REQUIRE(it != manager->m_managedRib.end());
+
+  shared_ptr<RibEntry> entry = it->second;
+  BOOST_CHECK_EQUAL(entry->hasFaceId(1), true);
+  BOOST_CHECK_EQUAL(entry->hasFaceId(2), false);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace rib
+} // namespace nfd
diff --git a/NFD/tests/rib/rib-status-publisher-common.hpp b/NFD/tests/rib/rib-status-publisher-common.hpp
new file mode 100644
index 0000000..58ccd68
--- /dev/null
+++ b/NFD/tests/rib/rib-status-publisher-common.hpp
@@ -0,0 +1,93 @@
+/* -*- 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 RIB_TESTS_UNIT_TESTS_RIB_STATUS_PUBLISHER_COMMON_HPP
+#define RIB_TESTS_UNIT_TESTS_RIB_STATUS_PUBLISHER_COMMON_HPP
+
+#include "rib/rib-status-publisher.hpp"
+
+#include "tests/test-common.hpp"
+#include "rib/rib.hpp"
+
+#include <ndn-cxx/management/nfd-control-parameters.hpp>
+#include <ndn-cxx/management/nfd-rib-entry.hpp>
+#include <ndn-cxx/encoding/tlv.hpp>
+
+namespace nfd {
+namespace rib {
+namespace tests {
+
+using ndn::nfd::ControlParameters;
+
+class RibStatusPublisherFixture : public nfd::tests::BaseFixture
+{
+public:
+  static void
+  validateRibEntry(const Block& block, const Name& referenceName, const FaceEntry& referenceFace)
+  {
+    ndn::nfd::RibEntry entry;
+    BOOST_REQUIRE_NO_THROW(entry.wireDecode(block));
+
+    BOOST_CHECK_EQUAL(entry.getName(), referenceName);
+
+    std::list<ndn::nfd::Route> routes = entry.getRoutes();
+
+    std::list<ndn::nfd::Route>::iterator it = routes.begin();
+    BOOST_CHECK_EQUAL(it->getFaceId(), referenceFace.faceId);
+    BOOST_CHECK_EQUAL(it->getOrigin(), referenceFace.origin);
+    BOOST_CHECK_EQUAL(it->getCost(), referenceFace.cost);
+    BOOST_CHECK_EQUAL(it->getFlags(), referenceFace.flags);
+  }
+
+  static void
+  decodeRibEntryBlock(const Data& data, const Name& referenceName, const FaceEntry& referenceFace)
+  {
+    ndn::EncodingBuffer buffer;
+
+    Block payload = data.getContent();
+
+    buffer.appendByteArray(payload.value(), payload.value_size());
+    buffer.prependVarNumber(buffer.size());
+    buffer.prependVarNumber(tlv::Content);
+
+    ndn::Block parser(buffer.buf(), buffer.size());
+    parser.parse();
+
+    Block::element_const_iterator i = parser.elements_begin();
+
+    if (i->type() != ndn::tlv::nfd::RibEntry) {
+      BOOST_FAIL("expected RibEntry, got type #" << i->type());
+    }
+    else {
+      validateRibEntry(*i, referenceName, referenceFace);
+    }
+  }
+};
+
+#endif // RIB_TESTS_UNIT_TESTS_RIB_STATUS_PUBLISHER_COMMON_HPP
+
+} // namespace tests
+} // namespace rib
+} // namespace nfd
diff --git a/NFD/tests/rib/rib-status-publisher.cpp b/NFD/tests/rib/rib-status-publisher.cpp
new file mode 100644
index 0000000..b1414cc
--- /dev/null
+++ b/NFD/tests/rib/rib-status-publisher.cpp
@@ -0,0 +1,66 @@
+/* -*- 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/>.
+ */
+
+#include "rib/rib-status-publisher.hpp"
+#include "rib-status-publisher-common.hpp"
+
+#include "tests/test-common.hpp"
+#include <ndn-cxx/util/dummy-client-face.hpp>
+
+namespace nfd {
+namespace rib {
+namespace tests {
+
+BOOST_FIXTURE_TEST_SUITE(RibStatusPublisherSuite, RibStatusPublisherFixture)
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+  Rib rib;
+
+  FaceEntry entry;
+  Name name("/");
+  entry.faceId = 1;
+  entry.origin = 128;
+  entry.cost = 32;
+  entry.flags = ndn::nfd::ROUTE_FLAG_CAPTURE;
+  rib.insert(name, entry);
+
+  ndn::KeyChain keyChain;
+  shared_ptr<ndn::util::DummyClientFace> face = ndn::util::makeDummyClientFace();
+  RibStatusPublisher publisher(rib, *face, "/localhost/nfd/rib/list", keyChain);
+
+  publisher.publish();
+  face->processEvents(time::milliseconds(1));
+
+  BOOST_REQUIRE_EQUAL(face->sentDatas.size(), 1);
+  decodeRibEntryBlock(face->sentDatas[0], name, entry);
+}
+
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace rib
+} // namespace nfd
diff --git a/NFD/tests/rib/rib.cpp b/NFD/tests/rib/rib.cpp
new file mode 100644
index 0000000..3226e57
--- /dev/null
+++ b/NFD/tests/rib/rib.cpp
@@ -0,0 +1,335 @@
+/* -*- 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/>.
+ */
+
+#include "rib/rib.hpp"
+
+#include "tests/test-common.hpp"
+
+namespace nfd {
+namespace rib {
+namespace tests {
+
+BOOST_FIXTURE_TEST_SUITE(Rib, nfd::tests::BaseFixture)
+
+BOOST_AUTO_TEST_CASE(RibEntry)
+{
+  rib::RibEntry entry;
+
+  rib::FaceEntry face1;
+  face1.faceId = 1;
+  face1.origin = 0;
+
+  entry.insertFace(face1);
+  BOOST_CHECK_EQUAL(entry.getFaces().size(), 1);
+
+  FaceEntry face2;
+  face2.faceId = 1;
+  face2.origin = 128;
+
+  entry.insertFace(face2);
+  BOOST_CHECK_EQUAL(entry.getFaces().size(), 2);
+
+  entry.eraseFace(face1);
+  BOOST_CHECK_EQUAL(entry.getFaces().size(), 1);
+
+  BOOST_CHECK(entry.findFace(face1) == entry.getFaces().end());
+  BOOST_CHECK(entry.findFace(face2) != entry.getFaces().end());
+
+  entry.insertFace(face2);
+  BOOST_CHECK_EQUAL(entry.getFaces().size(), 1);
+
+  entry.eraseFace(face1);
+  BOOST_CHECK_EQUAL(entry.getFaces().size(), 1);
+  BOOST_CHECK(entry.findFace(face2) != entry.getFaces().end());
+}
+
+BOOST_AUTO_TEST_CASE(Parent)
+{
+  rib::Rib rib;
+
+  FaceEntry root;
+  Name name1("/");
+  root.faceId = 1;
+  root.origin = 20;
+  rib.insert(name1, root);
+
+  FaceEntry entry1;
+  Name name2("/hello");
+  entry1.faceId = 2;
+  entry1.origin = 20;
+  rib.insert(name2, entry1);
+
+  FaceEntry entry2;
+  Name name3("/hello/world");
+  entry2.faceId = 3;
+  entry2.origin = 20;
+  rib.insert(name3, entry2);
+
+  shared_ptr<rib::RibEntry> ribEntry = rib.findParent(name3);
+  BOOST_REQUIRE(static_cast<bool>(ribEntry));
+  BOOST_CHECK_EQUAL(ribEntry->getFaces().front().faceId, 2);
+
+  ribEntry = rib.findParent(name2);
+  BOOST_REQUIRE(static_cast<bool>(ribEntry));
+  BOOST_CHECK_EQUAL(ribEntry->getFaces().front().faceId, 1);
+
+  FaceEntry entry3;
+  Name name4("/hello/test/foo/bar");
+  entry2.faceId = 3;
+  entry2.origin = 20;
+  rib.insert(name4, entry3);
+
+  ribEntry = rib.findParent(name4);
+  BOOST_CHECK(ribEntry != shared_ptr<rib::RibEntry>());
+  BOOST_CHECK(ribEntry->getFaces().front().faceId == 2);
+}
+
+BOOST_AUTO_TEST_CASE(Children)
+{
+  rib::Rib rib;
+
+  FaceEntry entry1;
+  Name name1("/");
+  entry1.faceId = 1;
+  entry1.origin = 20;
+  rib.insert(name1, entry1);
+
+  FaceEntry entry2;
+  Name name2("/hello/world");
+  entry2.faceId = 2;
+  entry2.origin = 20;
+  rib.insert(name2, entry2);
+
+  FaceEntry entry3;
+  Name name3("/hello/test/foo/bar");
+  entry3.faceId = 3;
+  entry3.origin = 20;
+  rib.insert(name3, entry3);
+
+  BOOST_CHECK_EQUAL((rib.find(name1)->second)->getChildren().size(), 2);
+  BOOST_CHECK_EQUAL((rib.find(name2)->second)->getChildren().size(), 0);
+  BOOST_CHECK_EQUAL((rib.find(name3)->second)->getChildren().size(), 0);
+
+  FaceEntry entry4;
+  Name name4("/hello");
+  entry4.faceId = 4;
+  entry4.origin = 20;
+  rib.insert(name4, entry4);
+
+  BOOST_CHECK_EQUAL((rib.find(name1)->second)->getChildren().size(), 1);
+  BOOST_CHECK_EQUAL((rib.find(name2)->second)->getChildren().size(), 0);
+  BOOST_CHECK_EQUAL((rib.find(name3)->second)->getChildren().size(), 0);
+  BOOST_CHECK_EQUAL((rib.find(name4)->second)->getChildren().size(), 2);
+
+  BOOST_CHECK_EQUAL((rib.find(name1)->second)->getChildren().front()->getName(), "/hello");
+  BOOST_CHECK_EQUAL((rib.find(name4)->second)->getParent()->getName(), "/");
+
+  BOOST_REQUIRE(static_cast<bool>((rib.find(name2)->second)->getParent()));
+  BOOST_CHECK_EQUAL((rib.find(name2)->second)->getParent()->getName(), name4);
+  BOOST_REQUIRE(static_cast<bool>((rib.find(name3)->second)->getParent()));
+  BOOST_CHECK_EQUAL((rib.find(name3)->second)->getParent()->getName(), name4);
+}
+
+BOOST_AUTO_TEST_CASE(EraseFace)
+{
+  rib::Rib rib;
+
+  FaceEntry entry1;
+  Name name1("/");
+  entry1.faceId = 1;
+  entry1.origin = 20;
+  rib.insert(name1, entry1);
+
+  FaceEntry entry2;
+  Name name2("/hello/world");
+  entry2.faceId = 2;
+  entry2.origin = 20;
+  rib.insert(name2, entry2);
+
+  FaceEntry entry3;
+  Name name3("/hello/world");
+  entry3.faceId = 1;
+  entry3.origin = 20;
+  rib.insert(name3, entry3);
+
+  FaceEntry entry4;
+  Name name4("/not/inserted");
+  entry4.faceId = 1;
+  entry4.origin = 20;
+
+  rib.erase(name4, entry4);
+  rib.erase(name1, entry1);
+
+  BOOST_CHECK(rib.find(name1) == rib.end());
+  BOOST_CHECK_EQUAL((rib.find(name2)->second)->getFaces().size(), 2);
+
+  rib.erase(name2, entry2);
+
+  BOOST_CHECK_EQUAL((rib.find(name2)->second)->getFaces().size(), 1);
+  BOOST_CHECK_EQUAL((rib.find(name2)->second)->getFaces().front().faceId, 1);
+
+  rib.erase(name3, entry3);
+
+  BOOST_CHECK(rib.find(name2) == rib.end());
+
+  rib.erase(name4, entry4);
+}
+
+BOOST_AUTO_TEST_CASE(EraseRibEntry)
+{
+  rib::Rib rib;
+
+  FaceEntry entry1;
+  Name name1("/");
+  entry1.faceId = 1;
+  entry1.origin = 20;
+  rib.insert(name1, entry1);
+
+  FaceEntry entry2;
+  Name name2("/hello");
+  entry2.faceId = 2;
+  entry2.origin = 20;
+  rib.insert(name2, entry2);
+
+  FaceEntry entry3;
+  Name name3("/hello/world");
+  entry3.faceId = 1;
+  entry3.origin = 20;
+  rib.insert(name3, entry3);
+
+  shared_ptr<rib::RibEntry> ribEntry1 = rib.find(name1)->second;
+  shared_ptr<rib::RibEntry> ribEntry2 = rib.find(name2)->second;
+  shared_ptr<rib::RibEntry> ribEntry3 = rib.find(name3)->second;
+
+  BOOST_CHECK(ribEntry1->getChildren().front() == ribEntry2);
+  BOOST_CHECK(ribEntry3->getParent() == ribEntry2);
+
+  rib.erase(name2, entry2);
+  BOOST_CHECK(ribEntry1->getChildren().front() == ribEntry3);
+  BOOST_CHECK(ribEntry3->getParent() == ribEntry1);
+}
+
+BOOST_AUTO_TEST_CASE(EraseByFaceId)
+{
+  rib::Rib rib;
+
+  FaceEntry entry1;
+  Name name1("/");
+  entry1.faceId = 1;
+  entry1.origin = 20;
+  rib.insert(name1, entry1);
+
+  FaceEntry entry2;
+  Name name2("/hello/world");
+  entry2.faceId = 2;
+  entry2.origin = 20;
+  rib.insert(name2, entry2);
+
+  FaceEntry entry3;
+  Name name3("/hello/world");
+  entry3.faceId = 1;
+  entry3.origin = 20;
+  rib.insert(name3, entry3);
+
+  rib.erase(1);
+  BOOST_CHECK(rib.find(name1) == rib.end());
+  BOOST_CHECK_EQUAL((rib.find(name2)->second)->getFaces().size(), 1);
+
+  rib.erase(3);
+  BOOST_CHECK_EQUAL((rib.find(name2)->second)->getFaces().size(), 1);
+
+  rib.erase(2);
+  BOOST_CHECK(rib.find(name2) == rib.end());
+
+  rib.erase(3);
+}
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+  rib::Rib rib;
+
+  FaceEntry entry1;
+  Name name1("/hello/world");
+  entry1.faceId = 1;
+  entry1.origin = 20;
+  entry1.cost = 10;
+  entry1.flags = ndn::nfd::ROUTE_FLAG_CHILD_INHERIT | ndn::nfd::ROUTE_FLAG_CAPTURE;
+  entry1.expires = time::steady_clock::now() + time::milliseconds(1500);
+
+  rib.insert(name1, entry1);
+  BOOST_CHECK_EQUAL(rib.size(), 1);
+
+  rib.insert(name1, entry1);
+  BOOST_CHECK_EQUAL(rib.size(), 1);
+
+  FaceEntry entry2;
+  Name name2("/hello/world");
+  entry2.faceId = 1;
+  entry2.origin = 20;
+  entry2.cost = 100;
+  entry2.flags = ndn::nfd::ROUTE_FLAG_CHILD_INHERIT;
+  entry2.expires = time::steady_clock::now() + time::seconds(0);
+
+  rib.insert(name2, entry2);
+  BOOST_CHECK_EQUAL(rib.size(), 1);
+
+  entry2.faceId = 2;
+  rib.insert(name2, entry2);
+  BOOST_CHECK_EQUAL(rib.size(), 2);
+
+  BOOST_CHECK(rib.find(name1)->second->hasFaceId(entry1.faceId));
+  BOOST_CHECK(rib.find(name1)->second->hasFaceId(entry2.faceId));
+
+  Name name3("/foo/bar");
+  rib.insert(name3, entry2);
+  BOOST_CHECK_EQUAL(rib.size(), 3);
+
+  entry2.origin = 1;
+  rib.insert(name3, entry2);
+  BOOST_CHECK_EQUAL(rib.size(), 4);
+
+  rib.erase(name3, entry2);
+  BOOST_CHECK_EQUAL(rib.size(), 3);
+
+  Name name4("/hello/world");
+  rib.erase(name4, entry2);
+  BOOST_CHECK_EQUAL(rib.size(), 3);
+
+  entry2.origin = 20;
+  rib.erase(name4, entry2);
+  BOOST_CHECK_EQUAL(rib.size(), 2);
+
+  BOOST_CHECK_EQUAL(rib.find(name2, entry2), static_cast<FaceEntry*>(0));
+  BOOST_CHECK_NE(rib.find(name1, entry1), static_cast<FaceEntry*>(0));
+
+  rib.erase(name1, entry1);
+  BOOST_CHECK_EQUAL(rib.size(), 1);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace rib
+} // namespace nfd
diff --git a/NFD/tests/test-case.cpp.sample b/NFD/tests/test-case.cpp.sample
new file mode 100644
index 0000000..560add2
--- /dev/null
+++ b/NFD/tests/test-case.cpp.sample
@@ -0,0 +1,64 @@
+/* -*- 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/>.
+ */
+
+// #include "unit-under-test.hpp"
+// Unit being tested MUST be included first, to ensure header compiles on its own.
+
+#include "tests/test-common.hpp"
+
+namespace nfd {
+namespace tests {
+// Unit tests SHOULD go inside nfd::tests namespace.
+
+// Test suite SHOULD use BaseFixture or a subclass of it.
+BOOST_FIXTURE_TEST_SUITE(TestSkeleton, BaseFixture)
+
+BOOST_AUTO_TEST_CASE(Test1)
+{
+  int i = 0;
+  /**
+   * For reference of available Boost.Test macros, @see http://www.boost.org/doc/libs/1_55_0/libs/test/doc/html/utf/testing-tools/reference.html
+   */
+
+  BOOST_REQUIRE_NO_THROW(i = 1);
+  BOOST_REQUIRE_EQUAL(i, 1);
+}
+
+// Custom fixture SHOULD derive from BaseFixture.
+class Test2Fixture : protected BaseFixture
+{
+};
+
+BOOST_FIXTURE_TEST_CASE(Test2, Test2Fixture)
+{
+  // g_io is a shorthand of getGlobalIoService()
+  // resetGlobalIoService() is automatically called after each test case
+  g_io.run();
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/NFD/tests/test-common.hpp b/NFD/tests/test-common.hpp
new file mode 100644
index 0000000..247337d
--- /dev/null
+++ b/NFD/tests/test-common.hpp
@@ -0,0 +1,167 @@
+/* -*- 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_TESTS_TEST_COMMON_HPP
+#define NFD_TESTS_TEST_COMMON_HPP
+
+#include "boost-test.hpp"
+
+#include "core/global-io.hpp"
+#include "core/logger.hpp"
+
+#include <ndn-cxx/util/time-unit-test-clock.hpp>
+#include <ndn-cxx/security/key-chain.hpp>
+
+namespace nfd {
+namespace tests {
+
+/** \brief base test fixture
+ *
+ *  Every test case should be based on this fixture,
+ *  to have per test case io_service initialization.
+ */
+class BaseFixture
+{
+protected:
+  BaseFixture()
+    : g_io(getGlobalIoService())
+  {
+  }
+
+  ~BaseFixture()
+  {
+    resetGlobalIoService();
+  }
+
+protected:
+  /// reference to global io_service
+  boost::asio::io_service& g_io;
+};
+
+/** \brief a base test fixture that overrides steady clock and system clock
+ */
+class UnitTestTimeFixture : public BaseFixture
+{
+protected:
+  UnitTestTimeFixture()
+    : steadyClock(make_shared<time::UnitTestSteadyClock>())
+    , systemClock(make_shared<time::UnitTestSystemClock>())
+  {
+    time::setCustomClocks(steadyClock, systemClock);
+  }
+
+  ~UnitTestTimeFixture()
+  {
+    time::setCustomClocks(nullptr, nullptr);
+  }
+
+  /** \brief advance steady and system clocks
+   *
+   *  Clocks are advanced in increments of \p tick for \p nTicks ticks.
+   *  After each tick, global io_service is polled to process pending I/O events.
+   *
+   *  Exceptions thrown during I/O events are propagated to the caller.
+   *  Clock advancing would stop in case of an exception.
+   */
+  void
+  advanceClocks(const time::nanoseconds& tick, size_t nTicks = 1)
+  {
+    BOOST_ASSERT(nTicks >= 0);
+
+    this->advanceClocks(tick, tick * nTicks);
+  }
+
+  /** \brief advance steady and system clocks
+   *
+   *  Clocks are advanced in increments of \p tick for \p total time.
+   *  The last increment might be shorter than \p tick.
+   *  After each tick, global io_service is polled to process pending I/O events.
+   *
+   *  Exceptions thrown during I/O events are propagated to the caller.
+   *  Clock advancing would stop in case of an exception.
+   */
+  void
+  advanceClocks(const time::nanoseconds& tick, const time::nanoseconds& total)
+  {
+    BOOST_ASSERT(tick > time::nanoseconds::zero());
+    BOOST_ASSERT(total >= time::nanoseconds::zero());
+
+    time::nanoseconds remaining = total;
+    while (remaining > time::nanoseconds::zero()) {
+      if (remaining >= tick) {
+        steadyClock->advance(tick);
+        systemClock->advance(tick);
+        remaining -= tick;
+      }
+      else {
+        steadyClock->advance(remaining);
+        systemClock->advance(remaining);
+        remaining = time::nanoseconds::zero();
+      }
+
+      if (g_io.stopped())
+        g_io.reset();
+      g_io.poll();
+    }
+  }
+
+  friend class LimitedIo;
+
+protected:
+  shared_ptr<time::UnitTestSteadyClock> steadyClock;
+  shared_ptr<time::UnitTestSystemClock> systemClock;
+};
+
+inline shared_ptr<Interest>
+makeInterest(const Name& name)
+{
+  return make_shared<Interest>(name);
+}
+
+inline shared_ptr<Data>
+signData(const shared_ptr<Data>& data)
+{
+  ndn::SignatureSha256WithRsa fakeSignature;
+  fakeSignature.setValue(ndn::dataBlock(tlv::SignatureValue,
+                                        static_cast<const uint8_t*>(nullptr), 0));
+  data->setSignature(fakeSignature);
+  data->wireEncode();
+
+  return data;
+}
+
+inline shared_ptr<Data>
+makeData(const Name& name)
+{
+  shared_ptr<Data> data = make_shared<Data>(name);
+
+  return signData(data);
+}
+
+
+} // namespace tests
+} // namespace nfd
+
+#endif // NFD_TESTS_TEST_COMMON_HPP
diff --git a/NFD/tests/wscript b/NFD/tests/wscript
new file mode 100644
index 0000000..c2cc536
--- /dev/null
+++ b/NFD/tests/wscript
@@ -0,0 +1,93 @@
+# -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
+
+"""
+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
+
+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/>.
+"""
+
+top = '..'
+
+def build(bld):
+    # Unit tests
+    if bld.env['WITH_TESTS']:
+        # main()
+        unit_test_main = bld(
+            target='unit-tests-main',
+            name='unit-tests-main',
+            features='cxx',
+            use='core-objects',
+            source='main.cpp',
+          )
+
+        # common test modules
+        unit_test_base = bld(
+            target='unit-tests-base',
+            name='unit-tests-base',
+            features='cxx pch',
+            source=bld.path.ant_glob(['*.cpp'], excl='main.cpp'),
+            use='core-objects',
+            headers='../common.hpp boost-test.hpp',
+          )
+
+        # core tests
+        unit_tests_core = bld.program(
+            target='../unit-tests-core',
+            features='cxx cxxprogram',
+            source=bld.path.ant_glob(['core/**/*.cpp']),
+            use='core-objects unit-tests-base unit-tests-main',
+            includes='.',
+            install_path=None,
+          )
+
+        # NFD tests
+        unit_tests_nfd = bld.program(
+            target='../unit-tests-daemon',
+            features='cxx cxxprogram',
+            source=bld.path.ant_glob(['daemon/**/*.cpp'],
+                                     excl=['daemon/face/ethernet.cpp',
+                                           'daemon/face/unix-*.cpp',
+                                           'daemon/face/websocket*.cpp']),
+            use='daemon-objects unit-tests-base unit-tests-main',
+            includes='.',
+            install_path=None,
+          )
+
+        if bld.env['HAVE_LIBPCAP']:
+            unit_tests_nfd.source += bld.path.ant_glob('daemon/face/ethernet.cpp')
+
+        if bld.env['HAVE_UNIX_SOCKETS']:
+            unit_tests_nfd.source += bld.path.ant_glob('daemon/face/unix-*.cpp')
+
+        if bld.env['HAVE_WEBSOCKET']:
+            unit_tests_nfd.source += bld.path.ant_glob('daemon/face/websocket*.cpp')
+
+        unit_tests_rib = bld.program(
+            target='../unit-tests-rib',
+            features='cxx cxxprogram',
+            source=bld.path.ant_glob(['rib/**/*.cpp']),
+            use='rib-objects unit-tests-base unit-tests-main',
+            includes='.',
+            install_path=None,
+          )
+
+    # Other tests (e.g., stress tests that can be enabled even if unit tests are disabled)
+    if bld.env['WITH_TESTS'] or bld.env['WITH_OTHER_TESTS']:
+        bld.recurse("other")
diff --git a/NFD/tools/ndn-autoconfig-server.cpp b/NFD/tools/ndn-autoconfig-server.cpp
new file mode 100644
index 0000000..1a01061
--- /dev/null
+++ b/NFD/tools/ndn-autoconfig-server.cpp
@@ -0,0 +1,221 @@
+/* -*- 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
+ *
+ * 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 "version.hpp"
+#include <ndn-cxx/face.hpp>
+#include <ndn-cxx/security/key-chain.hpp>
+
+namespace ndn {
+
+const static Name AUTOCONFIG_PREFIX          = "/localhop/ndn-autoconf";
+const static Name LOCALHOP_HUB               = "/localhop/ndn-autoconf/hub";
+const static Name LOCALHOP_ROUTABLE_PREFIXES = "/localhop/ndn-autoconf/routable-prefixes";
+
+static void
+usage(const char* programName)
+{
+  std::cout << "Usage:\n" << programName  << " [-h] [-V] [-p prefix] [-p prefix] ... Uri \n"
+            << "   -h        - print usage and exit\n"
+            << "   -V        - print version number and exit\n"
+            << "   -p prefix - the local prefix of the hub\n"
+            << "\n"
+            << "   Uri - a FaceMgmt URI\n"
+            << std::endl;
+}
+
+class PrefixCollection : noncopyable
+{
+public:
+  bool
+  empty() const
+  {
+    return m_prefixes.empty();
+  }
+
+  void
+  add(const Name& prefix)
+  {
+    m_prefixes.push_back(prefix);
+  }
+
+  template<bool T>
+  size_t
+  wireEncode(EncodingImpl<T>& encoder) const
+  {
+    size_t totalLength = 0;
+
+    for (std::vector<Name>::const_reverse_iterator i = m_prefixes.rbegin();
+         i != m_prefixes.rend(); ++i) {
+      totalLength += i->wireEncode(encoder);
+    }
+
+    totalLength += encoder.prependVarNumber(totalLength);
+    totalLength += encoder.prependVarNumber(tlv::Content);
+    return totalLength;
+  }
+
+  Block
+  wireEncode() const
+  {
+    Block block;
+
+    EncodingEstimator estimator;
+    size_t estimatedSize = wireEncode(estimator);
+
+    EncodingBuffer buffer(estimatedSize);
+    wireEncode(buffer);
+
+    return buffer.block();
+  }
+
+private:
+  std::vector<Name> m_prefixes;
+};
+
+class NdnAutoconfigServer : noncopyable
+{
+public:
+  NdnAutoconfigServer(const std::string& hubFaceUri, const PrefixCollection& routablePrefixes)
+  {
+    KeyChain m_keyChain;
+
+    // pre-create hub Data
+    m_hubData = make_shared<Data>(Name(LOCALHOP_HUB).appendVersion());
+    m_hubData->setFreshnessPeriod(time::hours(1)); // 1 hour
+    m_hubData->setContent(dataBlock(tlv::nfd::Uri,
+                                    reinterpret_cast<const uint8_t*>(hubFaceUri.c_str()),
+                                    hubFaceUri.size()));
+    m_keyChain.sign(*m_hubData);
+
+    // pre-create routable prefix Data
+    if (!routablePrefixes.empty()) {
+      m_routablePrefixesData = make_shared<Data>(Name(LOCALHOP_ROUTABLE_PREFIXES).appendVersion());
+      m_routablePrefixesData->setContent(routablePrefixes.wireEncode());
+      m_routablePrefixesData->setFreshnessPeriod(time::seconds(5)); // 5s
+      m_keyChain.sign(*m_routablePrefixesData);
+    }
+  }
+
+  void
+  onHubInterest(const Name& name, const Interest& interest)
+  {
+    m_face.put(*m_hubData);
+  }
+
+  void
+  onRoutablePrefixesInterest(const Name& name, const Interest& interest)
+  {
+    m_face.put(*m_routablePrefixesData);
+  }
+
+  void
+  onRegisterFailed(const Name& prefix, const std::string& reason)
+  {
+    std::cerr << "ERROR: Failed to register prefix in local hub's daemon (" <<
+              reason << ")" << std::endl;
+    m_face.shutdown();
+  }
+
+  void
+  afterPrefixRegistered()
+  {
+    BOOST_ASSERT(AUTOCONFIG_PREFIX.isPrefixOf(LOCALHOP_HUB));
+    m_face.setInterestFilter(LOCALHOP_HUB,
+                             bind(&NdnAutoconfigServer::onHubInterest, this, _1, _2));
+
+    if (static_cast<bool>(m_routablePrefixesData)) {
+      BOOST_ASSERT(AUTOCONFIG_PREFIX.isPrefixOf(LOCALHOP_ROUTABLE_PREFIXES));
+      m_face.setInterestFilter(LOCALHOP_ROUTABLE_PREFIXES,
+                               bind(&NdnAutoconfigServer::onRoutablePrefixesInterest,
+                                    this, _1, _2));
+    }
+  }
+
+  void
+  run()
+  {
+    m_face.registerPrefix(AUTOCONFIG_PREFIX,
+                          bind(&NdnAutoconfigServer::afterPrefixRegistered, this),
+                          bind(&NdnAutoconfigServer::onRegisterFailed, this, _1, _2));
+
+    m_face.processEvents();
+  }
+
+private:
+  Face m_face;
+
+  shared_ptr<Data> m_hubData;
+  shared_ptr<Data> m_routablePrefixesData;
+};
+
+int
+main(int argc, char** argv)
+{
+  const char* programName = argv[0];
+
+  PrefixCollection routablePrefixes;
+
+  int opt;
+  while ((opt = getopt(argc, argv, "hVp:")) != -1) {
+    switch (opt) {
+    case 'h':
+      usage(programName);
+      return 0;
+    case 'V':
+      std::cout << NFD_VERSION_BUILD_STRING << std::endl;
+      return 0;
+    case 'p':
+      routablePrefixes.add(Name(optarg));
+      break;
+    default:
+      usage(programName);
+      return 1;
+    }
+  }
+
+  if (argc != optind + 1) {
+    usage(programName);
+    return 1;
+  }
+
+  std::string hubFaceUri = argv[optind];
+  NdnAutoconfigServer instance(hubFaceUri, routablePrefixes);
+
+  try {
+    instance.run();
+  }
+  catch (const std::exception& error) {
+    std::cerr << "ERROR: " << error.what() << std::endl;
+    return 1;
+  }
+  return 0;
+}
+
+} // namespace ndn
+
+int
+main(int argc, char** argv)
+{
+  return ndn::main(argc, argv);
+}
diff --git a/NFD/tools/ndn-autoconfig.cpp b/NFD/tools/ndn-autoconfig.cpp
new file mode 100644
index 0000000..fdd8ba9
--- /dev/null
+++ b/NFD/tools/ndn-autoconfig.cpp
@@ -0,0 +1,530 @@
+/* -*- 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/>.
+ */
+
+#include "version.hpp"
+
+#include <ndn-cxx/face.hpp>
+#include <ndn-cxx/security/key-chain.hpp>
+#include <ndn-cxx/management/nfd-controller.hpp>
+#include <ndn-cxx/management/nfd-face-status.hpp>
+#include <ndn-cxx/security/key-chain.hpp>
+#include <ndn-cxx/encoding/buffer-stream.hpp>
+#include <ndn-cxx/util/face-uri.hpp>
+
+#include <boost/lexical_cast.hpp>
+#include <boost/noncopyable.hpp>
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <resolv.h>
+
+#ifdef __APPLE__
+#include <arpa/nameser_compat.h>
+#endif
+
+namespace ndn {
+namespace tools {
+
+static const Name LOCALHOP_HUB_DISCOVERY_PREFIX = "/localhop/ndn-autoconf/hub";
+
+void
+usage(const char* programName)
+{
+  std::cout << "Usage:\n" << programName  << " [-h] [-V]\n"
+            << "   -h  - print usage and exit\n"
+            << "   -V  - print version number and exit\n"
+            << std::endl;
+}
+
+class NdnAutoconfig : boost::noncopyable
+{
+public:
+  union QueryAnswer
+  {
+    HEADER header;
+    uint8_t buf[NS_PACKETSZ];
+  };
+
+  class Error : public std::runtime_error
+  {
+  public:
+    explicit
+    Error(const std::string& what)
+      : std::runtime_error(what)
+    {
+    }
+  };
+
+  explicit
+  NdnAutoconfig()
+    : m_controller(m_face, m_keyChain)
+  {
+  }
+
+  void
+  run()
+  {
+    m_face.processEvents();
+  }
+
+  void
+  fetchSegments(const Data& data, const shared_ptr<OBufferStream>& buffer,
+                void (NdnAutoconfig::*onDone)(const shared_ptr<OBufferStream>&))
+  {
+    buffer->write(reinterpret_cast<const char*>(data.getContent().value()),
+                  data.getContent().value_size());
+
+    uint64_t currentSegment = data.getName().get(-1).toSegment();
+
+    const name::Component& finalBlockId = data.getMetaInfo().getFinalBlockId();
+    if (finalBlockId.empty() ||
+        finalBlockId.toSegment() > currentSegment)
+      {
+        m_face.expressInterest(data.getName().getPrefix(-1).appendSegment(currentSegment+1),
+                               ndn::bind(&NdnAutoconfig::fetchSegments, this, _2, buffer, onDone),
+                               ndn::bind(&NdnAutoconfig::discoverHubStage2, this, "Timeout"));
+      }
+    else
+      {
+        return (this->*onDone)(buffer);
+      }
+  }
+
+  void
+  discoverHubStage1()
+  {
+    shared_ptr<OBufferStream> buffer = make_shared<OBufferStream>();
+
+    Interest interest("/localhost/nfd/faces/list");
+    interest.setChildSelector(1);
+    interest.setMustBeFresh(true);
+
+    m_face.expressInterest(interest,
+                           ndn::bind(&NdnAutoconfig::fetchSegments, this, _2, buffer,
+                                     &NdnAutoconfig::discoverHubStage1_registerHubDiscoveryPrefix),
+                           ndn::bind(&NdnAutoconfig::discoverHubStage2, this, "Timeout"));
+  }
+
+  void
+  discoverHubStage1_registerHubDiscoveryPrefix(const shared_ptr<OBufferStream>& buffer)
+  {
+    ConstBufferPtr buf = buffer->buf();
+    std::vector<uint64_t> multicastFaces;
+
+    size_t offset = 0;
+    while (offset < buf->size())
+      {
+        Block block;
+        bool ok = Block::fromBuffer(buf, offset, block);
+        if (!ok)
+          {
+            std::cerr << "ERROR: cannot decode FaceStatus TLV" << std::endl;
+            break;
+          }
+
+        offset += block.size();
+
+        nfd::FaceStatus faceStatus(block);
+
+        ndn::util::FaceUri uri(faceStatus.getRemoteUri());
+        if (uri.getScheme() == "udp4") {
+          namespace ip = boost::asio::ip;
+          boost::system::error_code ec;
+          ip::address address = ip::address::from_string(uri.getHost(), ec);
+
+          if (!ec && address.is_multicast()) {
+            multicastFaces.push_back(faceStatus.getFaceId());
+          }
+          else
+            continue;
+        }
+      }
+
+    if (multicastFaces.empty()) {
+      discoverHubStage2("No multicast faces available, skipping stage 1");
+    }
+    else {
+      shared_ptr<nfd::Controller> controller = make_shared<nfd::Controller>(ref(m_face));
+      shared_ptr<std::pair<size_t, size_t> > nRegistrations =
+        make_shared<std::pair<size_t, size_t> >(0, 0);
+
+      nfd::ControlParameters parameters;
+      parameters
+        .setName(LOCALHOP_HUB_DISCOVERY_PREFIX)
+        .setCost(1)
+        .setExpirationPeriod(time::seconds(30));
+
+      nRegistrations->first = multicastFaces.size();
+
+      for (std::vector<uint64_t>::iterator i = multicastFaces.begin();
+           i != multicastFaces.end(); ++i) {
+        parameters.setFaceId(*i);
+
+        controller->start<nfd::RibRegisterCommand>(parameters,
+          bind(&NdnAutoconfig::discoverHubStage1_onRegisterSuccess,
+               this, controller, nRegistrations),
+          bind(&NdnAutoconfig::discoverHubStage1_onRegisterFailure,
+               this, _1, _2, controller, nRegistrations));
+      }
+    }
+  }
+
+  void
+  discoverHubStage1_onRegisterSuccess(const shared_ptr<nfd::Controller>& controller,
+                                      const shared_ptr<std::pair<size_t, size_t> >& nRegistrations)
+  {
+    nRegistrations->second++;
+
+    if (nRegistrations->first == nRegistrations->second) {
+      discoverHubStage1_setStrategy(controller);
+    }
+  }
+
+  void
+  discoverHubStage1_onRegisterFailure(uint32_t code, const std::string& error,
+                                      const shared_ptr<nfd::Controller>& controller,
+                                      const shared_ptr<std::pair<size_t, size_t> >& nRegistrations)
+  {
+    std::cerr << "ERROR: " << error << " (code: " << code << ")" << std::endl;
+    nRegistrations->first--;
+
+    if (nRegistrations->first == nRegistrations->second) {
+      if (nRegistrations->first > 0) {
+        discoverHubStage1_setStrategy(controller);
+      } else {
+        discoverHubStage2("Failed to register " + LOCALHOP_HUB_DISCOVERY_PREFIX.toUri() +
+                          " for all multicast faces");
+      }
+    }
+  }
+
+  void
+  discoverHubStage1_setStrategy(const shared_ptr<nfd::Controller>& controller)
+  {
+    nfd::ControlParameters parameters;
+    parameters
+      .setName(LOCALHOP_HUB_DISCOVERY_PREFIX)
+      .setStrategy("/localhost/nfd/strategy/broadcast");
+
+    controller->start<nfd::StrategyChoiceSetCommand>(parameters,
+      bind(&NdnAutoconfig::discoverHubStage1_onSetStrategySuccess,
+           this, controller),
+      bind(&NdnAutoconfig::discoverHubStage1_onSetStrategyFailure,
+           this, _2, controller));
+  }
+
+  void
+  discoverHubStage1_onSetStrategySuccess(const shared_ptr<nfd::Controller>& controller)
+  {
+    discoverHubStage1_requestHubData();
+  }
+
+  void
+  discoverHubStage1_onSetStrategyFailure(const std::string& error,
+                                         const shared_ptr<nfd::Controller>& controller)
+  {
+    discoverHubStage2("Failed to set broadcast strategy for " +
+                      LOCALHOP_HUB_DISCOVERY_PREFIX.toUri() + " namespace (" + error + ")");
+  }
+
+  // Start to look for a hub (NDN hub discovery first stage)
+  void
+  discoverHubStage1_requestHubData()
+  {
+    Interest interest(LOCALHOP_HUB_DISCOVERY_PREFIX);
+    interest.setInterestLifetime(time::milliseconds(4000)); // 4 seconds
+    interest.setMustBeFresh(true);
+
+    std::cerr << "Stage 1: Trying multicast discovery..." << std::endl;
+    m_face.expressInterest(interest,
+                           bind(&NdnAutoconfig::onDiscoverHubStage1Success, this, _1, _2),
+                           bind(&NdnAutoconfig::discoverHubStage2, this, "Timeout"));
+  }
+
+  // First stage OnData Callback
+  void
+  onDiscoverHubStage1Success(const Interest& interest, Data& data)
+  {
+    const Block& content = data.getContent();
+    content.parse();
+
+    // Get Uri
+    Block::element_const_iterator blockValue = content.find(tlv::nfd::Uri);
+    if (blockValue == content.elements_end())
+    {
+      discoverHubStage2("Incorrect reply to stage1");
+      return;
+    }
+    std::string faceMgmtUri(reinterpret_cast<const char*>(blockValue->value()),
+                            blockValue->value_size());
+    connectToHub(faceMgmtUri);
+  }
+
+  // First stage OnTimeout callback - start 2nd stage
+  void
+  discoverHubStage2(const std::string& message)
+  {
+    std::cerr << message << std::endl;
+    std::cerr << "Stage 2: Trying DNS query with default suffix..." << std::endl;
+
+    _res.retry = 2;
+    _res.ndots = 10;
+
+    QueryAnswer queryAnswer;
+
+    int answerSize = res_search("_ndn._udp",
+                                ns_c_in,
+                                ns_t_srv,
+                                queryAnswer.buf,
+                                sizeof(queryAnswer));
+
+    // 2nd stage failed - move on to the third stage
+    if (answerSize < 0)
+    {
+      discoverHubStage3("Failed to find NDN router using default suffix DNS query");
+    }
+    else
+    {
+      bool isParsed = parseHostAndConnectToHub(queryAnswer, answerSize);
+      if (isParsed == false)
+      {
+        // Failed to parse DNS response, try stage 3
+        discoverHubStage3("Failed to parse DNS response");
+      }
+    }
+  }
+
+  // Second stage OnTimeout callback
+  void
+  discoverHubStage3(const std::string& message)
+  {
+    std::cerr << message << std::endl;
+    std::cerr << "Stage 3: Trying to find home router..." << std::endl;
+
+    KeyChain keyChain;
+    Name identity = keyChain.getDefaultIdentity();
+    std::string serverName = "_ndn._udp.";
+
+    for (Name::const_reverse_iterator i = identity.rbegin(); i != identity.rend(); i++)
+    {
+      serverName.append(i->toUri());
+      serverName.append(".");
+    }
+    serverName += "_homehub._autoconf.named-data.net";
+    std::cerr << "Stage3: About to query for a home router: " << serverName << std::endl;
+
+    QueryAnswer queryAnswer;
+
+    int answerSize = res_query(serverName.c_str(),
+                               ns_c_in,
+                               ns_t_srv,
+                               queryAnswer.buf,
+                               sizeof(queryAnswer));
+
+
+    // 3rd stage failed - abort
+    if (answerSize < 0)
+    {
+      std::cerr << "Failed to find a home router" << std::endl;
+      std::cerr << "exit" << std::endl;
+    }
+    else
+    {
+      bool isParsed = parseHostAndConnectToHub(queryAnswer, answerSize);
+      if (isParsed == false)
+      {
+        // Failed to parse DNS response
+        throw Error("Failed to parse DNS response");
+      }
+    }
+
+  }
+
+  void
+  connectToHub(const std::string& uri)
+  {
+    std::cerr << "about to connect to: " << uri << std::endl;
+
+    m_controller.start<nfd::FaceCreateCommand>(
+      nfd::ControlParameters()
+        .setUri(uri),
+      bind(&NdnAutoconfig::onHubConnectSuccess, this, _1),
+      bind(&NdnAutoconfig::onHubConnectError, this, _1, _2)
+    );
+  }
+
+  void
+  onHubConnectSuccess(const nfd::ControlParameters& resp)
+  {
+    std::cerr << "Successfully created face: " << resp << std::endl;
+
+    // Register a prefix in RIB
+    static const Name TESTBED_PREFIX("/ndn");
+    m_controller.start<nfd::RibRegisterCommand>(
+      nfd::ControlParameters()
+        .setName(TESTBED_PREFIX)
+        .setFaceId(resp.getFaceId())
+        .setOrigin(nfd::ROUTE_ORIGIN_AUTOCONF)
+        .setCost(100)
+        .setExpirationPeriod(time::milliseconds::max()),
+      bind(&NdnAutoconfig::onPrefixRegistrationSuccess, this, _1),
+      bind(&NdnAutoconfig::onPrefixRegistrationError, this, _1, _2));
+  }
+
+  void
+  onHubConnectError(uint32_t code, const std::string& error)
+  {
+    std::ostringstream os;
+    os << "Failed to create face: " << error << " (code: " << code << ")";
+    throw Error(os.str());
+  }
+
+
+  bool parseHostAndConnectToHub(QueryAnswer& queryAnswer, int answerSize)
+  {
+    // The references of the next classes are:
+    // http://www.diablotin.com/librairie/networking/dnsbind/ch14_02.htm
+    // https://gist.github.com/mologie/6027597
+
+    struct rechdr
+    {
+      uint16_t type;
+      uint16_t iclass;
+      uint32_t ttl;
+      uint16_t length;
+    };
+
+    struct srv_t
+    {
+      uint16_t priority;
+      uint16_t weight;
+      uint16_t port;
+      uint8_t* target;
+    };
+
+    if (ntohs(queryAnswer.header.ancount) == 0)
+    {
+      std::cerr << "No records found\n" << std::endl;
+      return false;
+    }
+
+    uint8_t* blob = queryAnswer.buf + NS_HFIXEDSZ;
+
+    blob += dn_skipname(blob, queryAnswer.buf + answerSize) + NS_QFIXEDSZ;
+
+    for (int i = 0; i < ntohs(queryAnswer.header.ancount); i++)
+    {
+      char srvName[NS_MAXDNAME];
+      int serverNameSize = dn_expand(queryAnswer.buf,               // message pointer
+                                     queryAnswer.buf + answerSize,  // end of message
+                                     blob,                          // compressed server name
+                                     srvName,                       // expanded server name
+                                     NS_MAXDNAME);
+      if (serverNameSize < 0)
+      {
+        return false;
+      }
+
+      srv_t* server = reinterpret_cast<srv_t*>(&blob[sizeof(rechdr)]);
+      uint16_t convertedPort = be16toh(server->port);
+
+      blob += serverNameSize + NS_HFIXEDSZ + NS_QFIXEDSZ;
+
+      char hostName[NS_MAXDNAME];
+      int hostNameSize = dn_expand(queryAnswer.buf,               // message pointer
+                                   queryAnswer.buf + answerSize,  // end of message
+                                   blob,                          // compressed host name
+                                   hostName,                      // expanded host name
+                                   NS_MAXDNAME);
+      if (hostNameSize < 0)
+      {
+        return false;
+      }
+
+      std::string uri = "udp://";
+      uri.append(hostName);
+      uri.append(":");
+      uri.append(boost::lexical_cast<std::string>(convertedPort));
+
+      connectToHub(uri);
+      return true;
+    }
+
+    return false;
+  }
+
+  void
+  onPrefixRegistrationSuccess(const nfd::ControlParameters& commandSuccessResult)
+  {
+    std::cerr << "Successful in name registration: " << commandSuccessResult << std::endl;
+  }
+
+  void
+  onPrefixRegistrationError(uint32_t code, const std::string& error)
+  {
+    std::ostringstream os;
+    os << "Failed in name registration, " << error << " (code: " << code << ")";
+    throw Error(os.str());
+  }
+
+private:
+  Face m_face;
+  KeyChain m_keyChain;
+  nfd::Controller m_controller;
+};
+
+} // namespace tools
+} // namespace ndn
+
+int
+main(int argc, char** argv)
+{
+  int opt;
+  const char* programName = argv[0];
+
+  while ((opt = getopt(argc, argv, "hV")) != -1) {
+    switch (opt) {
+    case 'h':
+      ndn::tools::usage(programName);
+      return 0;
+    case 'V':
+      std::cout << NFD_VERSION_BUILD_STRING << std::endl;
+      return 0;
+    }
+  }
+
+  try {
+    ndn::tools::NdnAutoconfig autoConfigInstance;
+
+    autoConfigInstance.discoverHubStage1();
+    autoConfigInstance.run();
+  }
+  catch (const std::exception& error) {
+    std::cerr << "ERROR: " << error.what() << std::endl;
+    return 1;
+  }
+  return 0;
+}
diff --git a/NFD/tools/ndn-tlv-peek.cpp b/NFD/tools/ndn-tlv-peek.cpp
new file mode 100644
index 0000000..5460096
--- /dev/null
+++ b/NFD/tools/ndn-tlv-peek.cpp
@@ -0,0 +1,279 @@
+/* -*- 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/>.
+ *
+ * @author Jerald Paul Abraham <jeraldabraham@email.arizona.edu>
+ */
+
+#include "version.hpp"
+
+#include <boost/noncopyable.hpp>
+
+#include <ndn-cxx/face.hpp>
+
+namespace ndntlvpeek {
+
+class NdnTlvPeek : boost::noncopyable
+{
+public:
+  explicit
+  NdnTlvPeek(char* programName)
+    : m_programName(programName)
+    , m_mustBeFresh(false)
+    , m_isChildSelectorRightmost(false)
+    , m_minSuffixComponents(-1)
+    , m_maxSuffixComponents(-1)
+    , m_interestLifetime(-1)
+    , m_isPayloadOnlySet(false)
+    , m_timeout(-1)
+    , m_prefixName("")
+    , m_isDataReceived(false)
+  {
+  }
+
+  void
+  usage()
+  {
+    std::cout << "\n Usage:\n " << m_programName << " "
+      "[-f] [-r] [-m min] [-M max] [-l lifetime] [-p] [-w timeout] ndn:/name\n"
+      "   Get one data item matching the name prefix and write it to stdout\n"
+      "   [-f]          - set MustBeFresh\n"
+      "   [-r]          - set ChildSelector to select rightmost child\n"
+      "   [-m min]      - set MinSuffixComponents\n"
+      "   [-M max]      - set MaxSuffixComponents\n"
+      "   [-l lifetime] - set InterestLifetime in time::milliseconds\n"
+      "   [-p]          - print payload only, not full packet\n"
+      "   [-w timeout]  - set Timeout in time::milliseconds\n"
+      "   [-h]          - print help and exit\n"
+      "   [-V]          - print version and exit\n"
+      "\n";
+    exit(1);
+  }
+
+  void
+  setMustBeFresh()
+  {
+    m_mustBeFresh = true;
+  }
+
+  void
+  setRightmostChildSelector()
+  {
+    m_isChildSelectorRightmost = true;
+  }
+
+  void
+  setMinSuffixComponents(int minSuffixComponents)
+  {
+    if (minSuffixComponents < 0)
+      usage();
+    m_minSuffixComponents = minSuffixComponents;
+  }
+
+  void
+  setMaxSuffixComponents(int maxSuffixComponents)
+  {
+    if (maxSuffixComponents < 0)
+      usage();
+    m_maxSuffixComponents = maxSuffixComponents;
+  }
+
+  void
+  setInterestLifetime(int interestLifetime)
+  {
+    if (interestLifetime < 0)
+      usage();
+    m_interestLifetime = ndn::time::milliseconds(interestLifetime);
+  }
+
+  void
+  setPayloadOnly()
+  {
+    m_isPayloadOnlySet = true;
+  }
+
+  void
+  setTimeout(int timeout)
+  {
+    if (timeout < 0)
+      usage();
+    m_timeout = ndn::time::milliseconds(timeout);
+  }
+
+  void
+  setPrefixName(char* prefixName)
+  {
+    m_prefixName = prefixName;
+    if (m_prefixName.length() == 0)
+      usage();
+  }
+
+  ndn::time::milliseconds
+  getDefaultInterestLifetime()
+  {
+    return ndn::time::seconds(4);
+  }
+
+  ndn::Interest
+  createInterestPacket()
+  {
+    ndn::Name interestName(m_prefixName);
+    ndn::Interest interestPacket(interestName);
+    if (m_mustBeFresh)
+      interestPacket.setMustBeFresh(true);
+    if (m_isChildSelectorRightmost)
+      interestPacket.setChildSelector(1);
+    if (m_minSuffixComponents >= 0)
+      interestPacket.setMinSuffixComponents(m_minSuffixComponents);
+    if (m_maxSuffixComponents >= 0)
+      interestPacket.setMaxSuffixComponents(m_maxSuffixComponents);
+    if (m_interestLifetime < ndn::time::milliseconds::zero())
+      interestPacket.setInterestLifetime(getDefaultInterestLifetime());
+    else
+      interestPacket.setInterestLifetime(m_interestLifetime);
+    return interestPacket;
+  }
+
+  void
+  onData(const ndn::Interest& interest, ndn::Data& data)
+  {
+    m_isDataReceived = true;
+    if (m_isPayloadOnlySet)
+      {
+        const ndn::Block& block = data.getContent();
+        std::cout.write(reinterpret_cast<const char*>(block.value()), block.value_size());
+      }
+    else
+      {
+        const ndn::Block& block = data.wireEncode();
+        std::cout.write(reinterpret_cast<const char*>(block.wire()), block.size());
+      }
+  }
+
+  void
+  onTimeout(const ndn::Interest& interest)
+  {
+  }
+
+  void
+  run()
+  {
+    try
+      {
+        m_face.expressInterest(createInterestPacket(),
+                               bind(&NdnTlvPeek::onData, this, _1, _2),
+                               bind(&NdnTlvPeek::onTimeout, this, _1));
+        if (m_timeout < ndn::time::milliseconds::zero())
+          {
+            if (m_interestLifetime < ndn::time::milliseconds::zero())
+              m_face.processEvents(getDefaultInterestLifetime());
+            else
+              m_face.processEvents(m_interestLifetime);
+          }
+        else
+          m_face.processEvents(m_timeout);
+      }
+    catch (std::exception& e)
+      {
+        std::cerr << "ERROR: " << e.what() << "\n" << std::endl;
+        exit(1);
+      }
+  }
+
+  bool
+  isDataReceived() const
+  {
+    return m_isDataReceived;
+  }
+
+private:
+
+  std::string m_programName;
+  bool m_mustBeFresh;
+  bool m_isChildSelectorRightmost;
+  int m_minSuffixComponents;
+  int m_maxSuffixComponents;
+  ndn::time::milliseconds m_interestLifetime;
+  bool m_isPayloadOnlySet;
+  ndn::time::milliseconds m_timeout;
+  std::string m_prefixName;
+  bool m_isDataReceived;
+  ndn::Face m_face;
+};
+
+}
+
+int
+main(int argc, char* argv[])
+{
+  ndntlvpeek::NdnTlvPeek ndnTlvPeek(argv[0]);
+  int option;
+  while ((option = getopt(argc, argv, "hfrm:M:l:pw:V")) != -1) {
+    switch (option) {
+    case 'h':
+      ndnTlvPeek.usage();
+      break;
+    case 'f':
+      ndnTlvPeek.setMustBeFresh();
+      break;
+    case 'r':
+      ndnTlvPeek.setRightmostChildSelector();
+      break;
+    case 'm':
+      ndnTlvPeek.setMinSuffixComponents(atoi(optarg));
+      break;
+    case 'M':
+      ndnTlvPeek.setMaxSuffixComponents(atoi(optarg));
+      break;
+    case 'l':
+      ndnTlvPeek.setInterestLifetime(atoi(optarg));
+      break;
+    case 'p':
+      ndnTlvPeek.setPayloadOnly();
+      break;
+    case 'w':
+      ndnTlvPeek.setTimeout(atoi(optarg));
+      break;
+    case 'V':
+      std::cout << NFD_VERSION_BUILD_STRING << std::endl;
+      return 0;
+    default:
+      ndnTlvPeek.usage();
+      break;
+    }
+  }
+
+  argc -= optind;
+  argv += optind;
+
+  if (argv[0] == 0)
+    ndnTlvPeek.usage();
+
+  ndnTlvPeek.setPrefixName(argv[0]);
+  ndnTlvPeek.run();
+
+  if (ndnTlvPeek.isDataReceived())
+    return 0;
+  else
+    return 1;
+}
diff --git a/NFD/tools/ndn-tlv-poke.cpp b/NFD/tools/ndn-tlv-poke.cpp
new file mode 100644
index 0000000..45db702
--- /dev/null
+++ b/NFD/tools/ndn-tlv-poke.cpp
@@ -0,0 +1,277 @@
+/* -*- 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/>.
+ *
+ * @author Jerald Paul Abraham <jeraldabraham@email.arizona.edu>
+ */
+
+#include "version.hpp"
+
+#include <boost/noncopyable.hpp>
+
+#include <ndn-cxx/face.hpp>
+#include <ndn-cxx/security/key-chain.hpp>
+
+namespace ndntlvpoke {
+
+class NdnTlvPoke : boost::noncopyable
+{
+public:
+  explicit
+  NdnTlvPoke(char* programName)
+    : m_programName(programName)
+    , m_isForceDataSet(false)
+    , m_isUseDigestSha256Set(false)
+    , m_isLastAsFinalBlockIdSet(false)
+    , m_freshnessPeriod(-1)
+    , m_timeout(-1)
+    , m_isDataSent(false)
+  {
+  }
+
+  void
+  usage()
+  {
+    std::cout << "\n Usage:\n " << m_programName << " "
+      "[-f] [-D] [-i identity] [-F] [-x freshness] [-w timeout] ndn:/name\n"
+      "   Reads payload from stdin and sends it to local NDN forwarder as a "
+      "single Data packet\n"
+      "   [-f]          - force, send Data without waiting for Interest\n"
+      "   [-D]          - use DigestSha256 signing method instead of "
+      "SignatureSha256WithRsa\n"
+      "   [-i identity] - set identity to be used for signing\n"
+      "   [-F]          - set FinalBlockId to the last component of Name\n"
+      "   [-x]          - set FreshnessPeriod in time::milliseconds\n"
+      "   [-w timeout]  - set Timeout in time::milliseconds\n"
+      "   [-h]          - print help and exit\n"
+      "   [-V]          - print version and exit\n"
+      "\n";
+    exit(1);
+  }
+
+  void
+  setForceData()
+  {
+    m_isForceDataSet = true;
+  }
+
+  void
+  setUseDigestSha256()
+  {
+    m_isUseDigestSha256Set = true;
+  }
+
+  void
+  setIdentityName(char* identityName)
+  {
+    m_identityName = ndn::make_shared<ndn::Name>(identityName);
+  }
+
+  void
+  setLastAsFinalBlockId()
+  {
+    m_isLastAsFinalBlockIdSet = true;
+  }
+
+  void
+  setFreshnessPeriod(int freshnessPeriod)
+  {
+    if (freshnessPeriod < 0)
+      usage();
+    m_freshnessPeriod = ndn::time::milliseconds(freshnessPeriod);
+  }
+
+  void
+  setTimeout(int timeout)
+  {
+    if (timeout < 0)
+      usage();
+    m_timeout = ndn::time::milliseconds(timeout);
+  }
+
+  void
+  setPrefixName(char* prefixName)
+  {
+    m_prefixName = ndn::Name(prefixName);
+  }
+
+  ndn::time::milliseconds
+  getDefaultTimeout()
+  {
+    return ndn::time::seconds(10);
+  }
+
+  ndn::Data
+  createDataPacket()
+  {
+    ndn::Data dataPacket(m_prefixName);
+    std::stringstream payloadStream;
+    payloadStream << std::cin.rdbuf();
+    std::string payload = payloadStream.str();
+    dataPacket.setContent(reinterpret_cast<const uint8_t*>(payload.c_str()), payload.length());
+    if (m_freshnessPeriod >= ndn::time::milliseconds::zero())
+      dataPacket.setFreshnessPeriod(m_freshnessPeriod);
+    if (m_isLastAsFinalBlockIdSet)
+      {
+        if (!m_prefixName.empty())
+          dataPacket.setFinalBlockId(m_prefixName.get(-1));
+        else
+          {
+            std::cerr << "Name Provided Has 0 Components" << std::endl;
+            exit(1);
+          }
+      }
+    if (m_isUseDigestSha256Set)
+      m_keyChain.signWithSha256(dataPacket);
+    else
+      {
+        if (!static_cast<bool>(m_identityName))
+          m_keyChain.sign(dataPacket);
+        else
+          m_keyChain.signByIdentity(dataPacket, *m_identityName);
+      }
+    return dataPacket;
+  }
+
+  void
+  onInterest(const ndn::Name& name,
+             const ndn::Interest& interest,
+             const ndn::Data& dataPacket)
+  {
+    m_face.put(dataPacket);
+    m_isDataSent = true;
+    m_face.shutdown();
+  }
+
+  void
+  onRegisterFailed(const ndn::Name& prefix, const std::string& reason)
+  {
+    std::cerr << "Prefix Registration Failure." << std::endl;
+    std::cerr << "Reason = " << reason << std::endl;
+  }
+
+  void
+  run()
+  {
+    try
+      {
+        ndn::Data dataPacket = createDataPacket();
+        if (m_isForceDataSet)
+          {
+            m_face.put(dataPacket);
+            m_isDataSent = true;
+          }
+        else
+          {
+            m_face.setInterestFilter(m_prefixName,
+                                     bind(&NdnTlvPoke::onInterest, this, _1, _2, dataPacket),
+                                     ndn::RegisterPrefixSuccessCallback(),
+                                     bind(&NdnTlvPoke::onRegisterFailed, this, _1, _2));
+          }
+        if (m_timeout < ndn::time::milliseconds::zero())
+          m_face.processEvents(getDefaultTimeout());
+        else
+          m_face.processEvents(m_timeout);
+      }
+    catch (std::exception& e)
+      {
+        std::cerr << "ERROR: " << e.what() << "\n" << std::endl;
+        exit(1);
+      }
+  }
+
+  bool
+  isDataSent() const
+  {
+    return m_isDataSent;
+  }
+
+private:
+
+  ndn::KeyChain m_keyChain;
+  std::string m_programName;
+  bool m_isForceDataSet;
+  bool m_isUseDigestSha256Set;
+  ndn::shared_ptr<ndn::Name> m_identityName;
+  bool m_isLastAsFinalBlockIdSet;
+  ndn::time::milliseconds m_freshnessPeriod;
+  ndn::time::milliseconds m_timeout;
+  ndn::Name m_prefixName;
+  bool m_isDataSent;
+  ndn::Face m_face;
+
+};
+
+}
+
+int
+main(int argc, char* argv[])
+{
+  int option;
+  ndntlvpoke::NdnTlvPoke ndnTlvPoke(argv[0]);
+  while ((option = getopt(argc, argv, "hfDi:Fx:w:V")) != -1) {
+    switch (option) {
+    case 'h':
+      ndnTlvPoke.usage();
+      break;
+    case 'f':
+      ndnTlvPoke.setForceData();
+      break;
+    case 'D':
+      ndnTlvPoke.setUseDigestSha256();
+      break;
+    case 'i':
+      ndnTlvPoke.setIdentityName(optarg);
+      break;
+    case 'F':
+      ndnTlvPoke.setLastAsFinalBlockId();
+      break;
+    case 'x':
+      ndnTlvPoke.setFreshnessPeriod(atoi(optarg));
+      break;
+    case 'w':
+      ndnTlvPoke.setTimeout(atoi(optarg));
+      break;
+    case 'V':
+      std::cout << NFD_VERSION_BUILD_STRING << std::endl;
+      return 0;
+    default:
+      ndnTlvPoke.usage();
+      break;
+    }
+  }
+
+  argc -= optind;
+  argv += optind;
+
+  if (argv[0] == 0)
+    ndnTlvPoke.usage();
+
+  ndnTlvPoke.setPrefixName(argv[0]);
+  ndnTlvPoke.run();
+
+  if (ndnTlvPoke.isDataSent())
+    return 0;
+  else
+    return 1;
+}
diff --git a/NFD/tools/nfd-autoreg.cpp b/NFD/tools/nfd-autoreg.cpp
new file mode 100644
index 0000000..0ad6841
--- /dev/null
+++ b/NFD/tools/nfd-autoreg.cpp
@@ -0,0 +1,400 @@
+/* -*- 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/>.
+ */
+
+#include <ndn-cxx/face.hpp>
+#include <ndn-cxx/name.hpp>
+
+#include <ndn-cxx/security/key-chain.hpp>
+#include <ndn-cxx/util/face-uri.hpp>
+#include <ndn-cxx/management/nfd-controller.hpp>
+#include <ndn-cxx/management/nfd-face-monitor.hpp>
+#include <ndn-cxx/management/nfd-face-status.hpp>
+#include <ndn-cxx/encoding/buffer-stream.hpp>
+
+#include <boost/program_options/options_description.hpp>
+#include <boost/program_options/variables_map.hpp>
+#include <boost/program_options/parsers.hpp>
+
+#include "version.hpp"
+#include "core/network.hpp"
+
+using namespace ndn::nfd;
+using ndn::Face;
+using ndn::KeyChain;
+using ndn::nfd::FaceEventNotification;
+using ndn::util::FaceUri;
+using ::nfd::Network;
+
+namespace ndn {
+namespace nfd_autoreg {
+
+namespace po = boost::program_options;
+
+class AutoregServer : boost::noncopyable
+{
+public:
+  AutoregServer()
+    : m_controller(m_face, m_keyChain)
+    , m_faceMonitor(m_face)
+    , m_cost(255)
+  {
+  }
+
+  void
+  onRegisterCommandSuccess(uint64_t faceId, const Name& prefix)
+  {
+    std::cerr << "SUCCEED: register " << prefix << " on face " << faceId << std::endl;
+  }
+
+  void
+  onRegisterCommandFailure(uint64_t faceId, const Name& prefix,
+                           uint32_t code, const std::string& reason)
+  {
+    std::cerr << "FAILED: register " << prefix << " on face " << faceId
+              << " (code: " << code << ", reason: " << reason << ")" << std::endl;
+  }
+
+  /**
+   * \return true if uri has schema allowed to do auto-registrations
+   */
+  bool
+  hasAllowedSchema(const FaceUri& uri)
+  {
+    const std::string& scheme = uri.getScheme();
+    return (scheme == "udp4" || scheme == "tcp4" ||
+            scheme == "udp6" || scheme == "tcp6");
+  }
+
+  /**
+   * \return true if address is blacklisted
+   */
+  bool
+  isBlacklisted(const boost::asio::ip::address& address)
+  {
+    for (std::vector<Network>::const_iterator network = m_blackList.begin();
+         network != m_blackList.end();
+         ++network)
+      {
+        if (network->doesContain(address))
+          return true;
+      }
+
+    return false;
+  }
+
+  /**
+   * \return true if address is whitelisted
+   */
+  bool
+  isWhitelisted(const boost::asio::ip::address& address)
+  {
+    for (std::vector<Network>::const_iterator network = m_whiteList.begin();
+         network != m_whiteList.end();
+         ++network)
+      {
+        if (network->doesContain(address))
+          return true;
+      }
+
+    return false;
+  }
+
+  void
+  registerPrefixesForFace(uint64_t faceId,
+                          const std::vector<ndn::Name>& prefixes)
+  {
+    for (std::vector<ndn::Name>::const_iterator prefix = prefixes.begin();
+         prefix != prefixes.end();
+         ++prefix)
+      {
+        m_controller.start<RibRegisterCommand>(
+          ControlParameters()
+            .setName(*prefix)
+            .setFaceId(faceId)
+            .setOrigin(ROUTE_ORIGIN_AUTOREG)
+            .setCost(m_cost)
+            .setExpirationPeriod(time::milliseconds::max()),
+          bind(&AutoregServer::onRegisterCommandSuccess, this, faceId, *prefix),
+          bind(&AutoregServer::onRegisterCommandFailure, this, faceId, *prefix, _1, _2));
+      }
+  }
+
+  void
+  registerPrefixesIfNeeded(uint64_t faceId, const FaceUri& uri, FacePersistency facePersistency)
+  {
+    if (hasAllowedSchema(uri)) {
+      boost::system::error_code ec;
+      boost::asio::ip::address address = boost::asio::ip::address::from_string(uri.getHost(), ec);
+
+      if (!address.is_multicast()) {
+        // register all-face prefixes
+        registerPrefixesForFace(faceId, m_allFacesPrefixes);
+
+        // register autoreg prefixes if new face is on-demand and not blacklisted and whitelisted
+        if (facePersistency == FACE_PERSISTENCY_ON_DEMAND &&
+            !isBlacklisted(address) && isWhitelisted(address)) {
+          registerPrefixesForFace(faceId, m_autoregPrefixes);
+        }
+      }
+    }
+  }
+
+  void
+  onNotification(const FaceEventNotification& notification)
+  {
+    if (notification.getKind() == FACE_EVENT_CREATED &&
+        notification.getFaceScope() != FACE_SCOPE_LOCAL)
+      {
+        std::cerr << "PROCESSING: " << notification << std::endl;
+
+        registerPrefixesIfNeeded(notification.getFaceId(), FaceUri(notification.getRemoteUri()),
+                                 notification.getFacePersistency());
+      }
+    else
+      {
+        std::cerr << "IGNORED: " << notification << std::endl;
+      }
+  }
+
+
+  void
+  signalHandler()
+  {
+    m_face.shutdown();
+  }
+
+
+  void
+  usage(std::ostream& os,
+        const po::options_description& optionDesciption,
+        const char* programName)
+  {
+    os << "Usage:\n"
+       << "  " << programName << " --prefix=</autoreg/prefix> [--prefix=/another/prefix] ...\n"
+       << "\n";
+    os << optionDesciption;
+  }
+
+  void
+  startProcessing()
+  {
+    std::cerr << "AUTOREG prefixes: " << std::endl;
+    for (std::vector<ndn::Name>::const_iterator prefix = m_autoregPrefixes.begin();
+         prefix != m_autoregPrefixes.end();
+         ++prefix)
+      {
+        std::cout << "  " << *prefix << std::endl;
+      }
+    std::cerr << "ALL-FACES-AUTOREG prefixes: " << std::endl;
+    for (std::vector<ndn::Name>::const_iterator prefix = m_allFacesPrefixes.begin();
+         prefix != m_allFacesPrefixes.end();
+         ++prefix)
+      {
+        std::cout << "  " << *prefix << std::endl;
+      }
+
+    if (!m_blackList.empty())
+      {
+        std::cerr << "Blacklisted networks: " << std::endl;
+        for (std::vector<Network>::const_iterator network = m_blackList.begin();
+             network != m_blackList.end();
+             ++network)
+          {
+            std::cout << "  " << *network << std::endl;
+          }
+      }
+
+    std::cerr << "Whitelisted networks: " << std::endl;
+    for (std::vector<Network>::const_iterator network = m_whiteList.begin();
+         network != m_whiteList.end();
+         ++network)
+      {
+        std::cout << "  " << *network << std::endl;
+      }
+
+    m_faceMonitor.onNotification += bind(&AutoregServer::onNotification, this, _1);
+    m_faceMonitor.start();
+
+    boost::asio::signal_set signalSet(m_face.getIoService(), SIGINT, SIGTERM);
+    signalSet.async_wait(bind(&AutoregServer::signalHandler, this));
+
+    m_face.processEvents();
+  }
+
+
+  void
+  fetchFaceStatusSegments(const Data& data, const shared_ptr<ndn::OBufferStream>& buffer)
+  {
+    buffer->write(reinterpret_cast<const char*>(data.getContent().value()),
+                  data.getContent().value_size());
+
+    uint64_t currentSegment = data.getName().get(-1).toSegment();
+
+    const name::Component& finalBlockId = data.getMetaInfo().getFinalBlockId();
+    if (finalBlockId.empty() || finalBlockId.toSegment() > currentSegment) {
+      m_face.expressInterest(data.getName().getPrefix(-1).appendSegment(currentSegment + 1),
+                             bind(&AutoregServer::fetchFaceStatusSegments, this, _2, buffer),
+                             ndn::OnTimeout());
+    }
+    else {
+      return processFaceStatusDataset(buffer);
+    }
+  }
+
+  void
+  startFetchingFaceStatusDataset()
+  {
+    shared_ptr<ndn::OBufferStream> buffer = make_shared<ndn::OBufferStream>();
+
+    Interest interest("/localhost/nfd/faces/list");
+    interest.setChildSelector(1);
+    interest.setMustBeFresh(true);
+
+    m_face.expressInterest(interest,
+                           bind(&AutoregServer::fetchFaceStatusSegments, this, _2, buffer),
+                           ndn::OnTimeout());
+  }
+
+  void
+  processFaceStatusDataset(const shared_ptr<ndn::OBufferStream>& buffer)
+  {
+    ndn::ConstBufferPtr buf = buffer->buf();
+    std::vector<uint64_t> multicastFaces;
+
+    size_t offset = 0;
+    while (offset < buf->size())
+      {
+        Block block;
+        bool ok = Block::fromBuffer(buf, offset, block);
+        if (!ok)
+          {
+            std::cerr << "ERROR: cannot decode FaceStatus TLV" << std::endl;
+            break;
+          }
+
+        offset += block.size();
+
+        nfd::FaceStatus faceStatus(block);
+        registerPrefixesIfNeeded(faceStatus.getFaceId(), FaceUri(faceStatus.getRemoteUri()),
+                                 faceStatus.getFacePersistency());
+      }
+  }
+
+  int
+  main(int argc, char* argv[])
+  {
+    po::options_description optionDesciption;
+    optionDesciption.add_options()
+      ("help,h", "produce help message")
+      ("prefix,i", po::value<std::vector<ndn::Name> >(&m_autoregPrefixes)->composing(),
+       "prefix that should be automatically registered when new a remote non-local face is "
+       "established")
+      ("all-faces-prefix,a", po::value<std::vector<ndn::Name> >(&m_allFacesPrefixes)->composing(),
+       "prefix that should be automatically registered for all TCP and UDP non-local faces "
+       "(blacklists and whitelists do not apply to this prefix)")
+      ("cost,c", po::value<uint64_t>(&m_cost)->default_value(255),
+       "FIB cost which should be assigned to autoreg nexthops")
+      ("whitelist,w", po::value<std::vector<Network> >(&m_whiteList)->composing(),
+       "Whitelisted network, e.g., 192.168.2.0/24 or ::1/128")
+      ("blacklist,b", po::value<std::vector<Network> >(&m_blackList)->composing(),
+       "Blacklisted network, e.g., 192.168.2.32/30 or ::1/128")
+      ("version,V", "show version and exit")
+      ;
+
+    po::variables_map options;
+    try
+      {
+        po::store(po::command_line_parser(argc, argv).options(optionDesciption).run(), options);
+        po::notify(options);
+      }
+    catch (std::exception& e)
+      {
+        std::cerr << "ERROR: " << e.what() << std::endl << std::endl;
+        usage(std::cerr, optionDesciption, argv[0]);
+        return 1;
+      }
+
+    if (options.count("help"))
+      {
+        usage(std::cout, optionDesciption, argv[0]);
+        return 0;
+      }
+
+    if (options.count("version"))
+      {
+        std::cout << NFD_VERSION_BUILD_STRING << std::endl;
+        return 0;
+      }
+
+    if (m_autoregPrefixes.empty() && m_allFacesPrefixes.empty())
+      {
+        std::cerr << "ERROR: at least one --prefix or --all-faces-prefix must be specified"
+                  << std::endl << std::endl;
+        usage(std::cerr, optionDesciption, argv[0]);
+        return 2;
+      }
+
+    if (m_whiteList.empty())
+      {
+        // Allow everything
+        m_whiteList.push_back(Network::getMaxRangeV4());
+        m_whiteList.push_back(Network::getMaxRangeV6());
+      }
+
+    try
+      {
+        startFetchingFaceStatusDataset();
+        startProcessing();
+      }
+    catch (std::exception& e)
+      {
+        std::cerr << "ERROR: " << e.what() << std::endl;
+        return 2;
+      }
+
+    return 0;
+  }
+
+private:
+  Face m_face;
+  KeyChain m_keyChain;
+  Controller m_controller;
+  FaceMonitor m_faceMonitor;
+  std::vector<ndn::Name> m_autoregPrefixes;
+  std::vector<ndn::Name> m_allFacesPrefixes;
+  uint64_t m_cost;
+  std::vector<Network> m_whiteList;
+  std::vector<Network> m_blackList;
+};
+
+} // namespace nfd_autoreg
+} // namespace ndn
+
+int
+main(int argc, char* argv[])
+{
+  ndn::nfd_autoreg::AutoregServer server;
+  return server.main(argc, argv);
+}
diff --git a/NFD/tools/nfd-start.sh b/NFD/tools/nfd-start.sh
new file mode 100755
index 0000000..4df12d7
--- /dev/null
+++ b/NFD/tools/nfd-start.sh
@@ -0,0 +1,55 @@
+#!@BASH@
+
+VERSION="@VERSION@"
+
+case "$1" in
+  -h)
+    echo Usage
+    echo $0
+    echo "  Start NFD and RIB Management daemon"
+    exit 0
+    ;;
+  -V)
+    echo $VERSION
+    exit 0
+    ;;
+  "") ;; # do nothing
+  *)
+    echo "Unrecognized option $1"
+    exit 1
+    ;;
+esac
+
+hasProcess() {
+  local processName=$1
+
+  if pgrep -x $processName >/dev/null
+  then
+    echo $processName
+  fi
+}
+
+hasNFD=$(hasProcess nfd)
+hasNRD=$(hasProcess nrd)
+
+if [[ -n $hasNFD$hasNRD ]]
+then
+  echo 'NFD or NRD is already running...'
+  exit 1
+fi
+
+if ! ndnsec-get-default &>/dev/null
+then
+  ndnsec-keygen /localhost/operator | ndnsec-install-cert -
+fi
+
+if ! sudo true
+then
+  echo 'Unable to obtain superuser privilege'
+  exit 2
+fi
+
+sudo nfd &
+sleep 2
+nrd &
+sleep 2
diff --git a/NFD/tools/nfd-status-http-server-files/nfd-status.xsl b/NFD/tools/nfd-status-http-server-files/nfd-status.xsl
new file mode 100644
index 0000000..76c7004
--- /dev/null
+++ b/NFD/tools/nfd-status-http-server-files/nfd-status.xsl
@@ -0,0 +1,346 @@
+<xsl:stylesheet version="1.0"
+xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+xmlns:nfd="ndn:/localhost/nfd/status/1">
+<xsl:output method="html" encoding="utf-8" indent="yes" />
+
+<xsl:template match="/">
+  <xsl:text disable-output-escaping='yes'>&lt;!DOCTYPE html></xsl:text>
+  <html>
+  <head>
+  <title>NFD Status</title>
+  <link rel="stylesheet" type="text/css" href="style.css" />
+  </head>
+  <body>
+  <header>
+    <h1>NFD Status</h1>
+  </header>
+  <article>
+    <div id="content">
+      <xsl:apply-templates/>
+    </div>
+  </article>
+  <footer>
+    <xsl:variable name="version">
+      <xsl:apply-templates select="nfd:nfdStatus/nfd:generalStatus/nfd:version"/>
+    </xsl:variable>
+    <span class="grey">Powered by </span><a target="_blank" href="http://named-data.net/doc/NFD/{$version}/"><span class="green">NFD version <xsl:value-of select="$version"/></span></a><span class="grey">.</span>
+  </footer>
+  </body>
+  </html>
+</xsl:template>
+
+
+<xsl:template name="formatDate">
+  <xsl:param name="date" />
+  <xsl:value-of select="substring($date, 0, 11)"/>&#160;<xsl:value-of select="substring($date, 12, 8)"/>
+</xsl:template>
+
+<xsl:template name="formatDuration">
+  <xsl:param name="duration" />
+  <xsl:variable name="seconds"><xsl:value-of select="substring($duration, 3, string-length($duration)-3)" /></xsl:variable>
+  <xsl:variable name="days"><xsl:value-of select="floor($seconds div 86400)" /></xsl:variable>
+  <xsl:variable name="hours"><xsl:value-of select="floor($seconds div 3600)" /></xsl:variable>
+  <xsl:variable name="minutes"><xsl:value-of select="floor($seconds div 60)" /></xsl:variable>
+  <xsl:choose>
+    <xsl:when test="$days = 1">
+      <xsl:value-of select="$days"/> day
+    </xsl:when>
+    <xsl:when test="$days > 1">
+      <xsl:value-of select="$days"/> days
+    </xsl:when>
+    <xsl:when test="$hours = 1">
+      <xsl:value-of select="$hours"/> hour
+    </xsl:when>
+    <xsl:when test="$hours > 1">
+      <xsl:value-of select="$hours"/> hours
+    </xsl:when>
+    <xsl:when test="$minutes = 1">
+      <xsl:value-of select="$minutes"/> minute
+    </xsl:when>
+    <xsl:when test="$minutes > 1">
+      <xsl:value-of select="$minutes"/> minutes
+    </xsl:when>
+    <xsl:when test="$seconds = 1">
+      <xsl:value-of select="$seconds"/> second
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:value-of select="$seconds"/> seconds
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template match="nfd:generalStatus">
+  <h2>General NFD status</h2>
+  <table class="item-list">
+    <thead>
+      <tr>
+        <th>NFD ID</th>
+        <th>Version</th>
+        <th>Start time</th>
+        <th>Current time</th>
+        <th>Uptime</th>
+        <th>NameTree Entries</th>
+        <th>FIB entries</th>
+        <th>PIT entries</th>
+        <th>Measurements entries</th>
+        <th>CS entries</th>
+        <th>In Interests</th>
+        <th>Out Interests</th>
+        <th>In Data</th>
+        <th>Out Data</th>
+      </tr>
+    </thead>
+    <tbody>
+      <tr class="center">
+        <td><xsl:apply-templates select="nfd:nfdId"/></td>
+        <td><xsl:value-of select="nfd:version"/></td>
+        <td><xsl:call-template name="formatDate"><xsl:with-param name="date" select="nfd:startTime" /></xsl:call-template></td>
+        <td><xsl:call-template name="formatDate"><xsl:with-param name="date" select="nfd:currentTime" /></xsl:call-template></td>
+        <td><xsl:call-template name="formatDuration"><xsl:with-param name="duration" select="nfd:uptime" /></xsl:call-template></td>
+        <td><xsl:value-of select="nfd:nNameTreeEntries"/></td>
+        <td><xsl:value-of select="nfd:nFibEntries"/></td>
+        <td><xsl:value-of select="nfd:nPitEntries"/></td>
+        <td><xsl:value-of select="nfd:nMeasurementsEntries"/></td>
+        <td><xsl:value-of select="nfd:nCsEntries"/></td>
+        <td><xsl:value-of select="nfd:packetCounters/nfd:incomingPackets/nfd:nInterests"/></td>
+        <td><xsl:value-of select="nfd:packetCounters/nfd:outgoingPackets/nfd:nInterests"/></td>
+        <td><xsl:value-of select="nfd:packetCounters/nfd:incomingPackets/nfd:nDatas"/></td>
+        <td><xsl:value-of select="nfd:packetCounters/nfd:outgoingPackets/nfd:nDatas"/></td>
+      </tr>
+    </tbody>
+  </table>
+</xsl:template>
+
+<xsl:template match="nfd:channels">
+  <h2>Channels</h2>
+  <table class="item-list">
+    <thead>
+      <tr>
+        <th>Channel URI</th>
+      </tr>
+    </thead>
+    <tbody>
+      <xsl:for-each select="nfd:channel">
+        <xsl:variable name="style">
+          <xsl:choose>
+            <xsl:when test="position() mod 2 = 1">
+              <xsl:text>odd</xsl:text>
+            </xsl:when>
+            <xsl:otherwise>even</xsl:otherwise>
+          </xsl:choose>
+        </xsl:variable>
+        <tr class="{$style}">
+          <td><xsl:value-of select="nfd:localUri"/></td>
+        </tr>
+      </xsl:for-each>
+    </tbody>
+  </table>
+</xsl:template>
+
+<xsl:template match="nfd:faces">
+  <h2>Faces</h2>
+  <table class="item-list">
+    <thead>
+      <tr>
+        <th>Face ID</th>
+        <th>Remote URI</th>
+        <th>Local URI</th>
+        <th>Scope</th>
+        <th>Persistency</th>
+        <th>LinkType</th>
+        <th>Expires in</th>
+        <th>In Interests</th>
+        <th>In Data</th>
+        <th>In Bytes</th>
+        <th>Out Interests</th>
+        <th>Out Data</th>
+        <th>Out Bytes</th>
+      </tr>
+    </thead>
+    <tbody>
+      <xsl:for-each select="nfd:face">
+      <xsl:variable name="style">
+        <xsl:choose>
+          <xsl:when test="position() mod 2 = 1">
+            <xsl:text>odd</xsl:text>
+          </xsl:when>
+          <xsl:otherwise>even</xsl:otherwise>
+        </xsl:choose>
+      </xsl:variable>
+      <tr class="{$style}">
+        <td><xsl:value-of select="nfd:faceId"/></td>
+        <td><xsl:value-of select="nfd:remoteUri"/></td>
+        <td><xsl:value-of select="nfd:localUri"/></td>
+        <td><xsl:value-of select="nfd:faceScope"/></td>
+        <td><xsl:value-of select="nfd:facePersistency"/></td>
+        <td><xsl:value-of select="nfd:linkType"/></td>
+        <td>
+          <xsl:choose>
+            <xsl:when test="nfd:expirationPeriod">
+              <xsl:call-template name="formatDuration"><xsl:with-param name="duration" select="nfd:expirationPeriod" /></xsl:call-template>
+            </xsl:when>
+            <xsl:otherwise>
+              Never
+            </xsl:otherwise>
+          </xsl:choose>
+        </td>
+        <td><xsl:value-of select="nfd:packetCounters/nfd:incomingPackets/nfd:nInterests"/></td>
+        <td><xsl:value-of select="nfd:packetCounters/nfd:incomingPackets/nfd:nDatas"/></td>
+        <td><xsl:value-of select="nfd:byteCounters/nfd:incomingBytes"/></td>
+        <td><xsl:value-of select="nfd:packetCounters/nfd:outgoingPackets/nfd:nInterests"/></td>
+        <td><xsl:value-of select="nfd:packetCounters/nfd:outgoingPackets/nfd:nDatas"/></td>
+        <td><xsl:value-of select="nfd:byteCounters/nfd:outgoingBytes"/></td>
+      </tr>
+      </xsl:for-each>
+    </tbody>
+  </table>
+</xsl:template>
+
+<xsl:template match="nfd:fib">
+  <h2>FIB</h2>
+  <table class="item-list">
+    <thead>
+      <tr>
+        <th width="20%">Prefix</th>
+        <th>NextHops</th>
+      </tr>
+    </thead>
+    <tbody>
+      <xsl:for-each select="nfd:fibEntry">
+        <xsl:variable name="style">
+          <xsl:choose>
+            <xsl:when test="position() mod 2 = 1">
+              <xsl:text>odd</xsl:text>
+            </xsl:when>
+            <xsl:otherwise>even</xsl:otherwise>
+          </xsl:choose>
+        </xsl:variable>
+        <tr class="{$style}">
+        <td style="text-align:left;vertical-align:top;padding:0"><xsl:value-of select="nfd:prefix"/></td>
+        <td>
+          <table class="item-sublist">
+            <tr>
+              <th>FaceId</th>
+              <xsl:for-each select="nfd:nextHops/nfd:nextHop">
+                <td><xsl:value-of select="nfd:faceId"/></td>
+              </xsl:for-each>
+            </tr>
+            <tr>
+              <th>Cost</th>
+              <xsl:for-each select="nfd:nextHops/nfd:nextHop">
+                <td><xsl:value-of select="nfd:cost"/></td>
+              </xsl:for-each>
+            </tr>
+          </table>
+        </td>
+      </tr>
+      </xsl:for-each>
+    </tbody>
+  </table>
+</xsl:template>
+
+<xsl:template match="nfd:rib">
+  <h2>RIB</h2>
+  <table class="item-list">
+    <thead>
+      <tr>
+        <th width="20%">Prefix</th>
+        <th>Routes</th>
+      </tr>
+    </thead>
+    <tbody>
+      <xsl:for-each select="nfd:ribEntry">
+        <xsl:variable name="style">
+          <xsl:choose>
+            <xsl:when test="position() mod 2 = 1">
+              <xsl:text>odd</xsl:text>
+            </xsl:when>
+            <xsl:otherwise>even</xsl:otherwise>
+          </xsl:choose>
+        </xsl:variable>
+        <tr class="{$style}">
+        <td style="text-align:left;vertical-align:top;padding:0"><xsl:value-of select="nfd:prefix"/></td>
+        <td>
+          <table class="item-sublist">
+            <tr>
+              <th>FaceId</th>
+              <xsl:for-each select="nfd:routes/nfd:route">
+                <td><xsl:value-of select="nfd:faceId"/></td>
+              </xsl:for-each>
+            </tr>
+            <tr>
+              <th>Origin</th>
+              <xsl:for-each select="nfd:routes/nfd:route">
+                <td><xsl:value-of select="nfd:origin"/></td>
+              </xsl:for-each>
+            </tr>
+            <tr>
+              <th>Cost</th>
+              <xsl:for-each select="nfd:routes/nfd:route">
+                <td><xsl:value-of select="nfd:cost"/></td>
+              </xsl:for-each>
+            </tr>
+            <tr>
+              <th>ChildInherit</th>
+              <xsl:for-each select="nfd:routes/nfd:route">
+                <td>
+                  <xsl:if test="nfd:flags/nfd:childInherit">
+                    Y
+                  </xsl:if>
+                </td>
+              </xsl:for-each>
+            </tr>
+            <tr>
+              <th>RibCapture</th>
+              <xsl:for-each select="nfd:routes/nfd:route">
+                <td>
+                  <xsl:if test="nfd:flags/nfd:ribCapture">
+                    Y
+                  </xsl:if>
+                </td>
+              </xsl:for-each>
+            </tr>
+            <tr>
+              <th>Expires in</th>
+              <xsl:for-each select="nfd:routes/nfd:route">
+                <td>
+                  <xsl:choose>
+                    <xsl:when test="nfd:expirationPeriod">
+                      <xsl:call-template name="formatDuration"><xsl:with-param name="duration" select="nfd:expirationPeriod" /></xsl:call-template>
+                    </xsl:when>
+                    <xsl:otherwise>
+                      Never
+                    </xsl:otherwise>
+                  </xsl:choose>
+                </td>
+              </xsl:for-each>
+            </tr>
+          </table>
+        </td>
+      </tr>
+      </xsl:for-each>
+    </tbody>
+  </table>
+</xsl:template>
+
+<xsl:template match="nfd:strategyChoices">
+  <h2>Strategy Choices</h2>
+  <table class="item-list">
+    <thead>
+      <tr>
+        <th width="20%">Namespace</th>
+        <th>Strategy Name</th>
+      </tr>
+    </thead>
+    <tbody>
+      <xsl:for-each select="nfd:strategyChoice">
+      <tr>
+        <td><xsl:value-of select="nfd:namespace"/></td>
+        <td><xsl:value-of select="nfd:strategy/nfd:name"/></td>
+      </tr>
+      </xsl:for-each>
+    </tbody>
+  </table>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/NFD/tools/nfd-status-http-server-files/reset.css b/NFD/tools/nfd-status-http-server-files/reset.css
new file mode 100644
index 0000000..0af7c26
--- /dev/null
+++ b/NFD/tools/nfd-status-http-server-files/reset.css
@@ -0,0 +1,207 @@
+/* `XHTML, HTML4, HTML5 Reset
+----------------------------------------------------------------------------------------------------*/
+
+a,
+abbr,
+acronym,
+address,
+applet,
+article,
+aside,
+audio,
+b,
+big,
+blockquote,
+body,
+canvas,
+caption,
+center,
+cite,
+code,
+dd,
+del,
+details,
+dfn,
+dialog,
+div,
+dl,
+dt,
+em,
+embed,
+fieldset,
+figcaption,
+figure,
+font,
+footer,
+form,
+h1,
+h2,
+h3,
+h4,
+h5,
+h6,
+header,
+hgroup,
+hr,
+html,
+i,
+iframe,
+img,
+ins,
+kbd,
+label,
+legend,
+li,
+mark,
+menu,
+meter,
+nav,
+object,
+ol,
+output,
+p,
+pre,
+progress,
+q,
+rp,
+rt,
+ruby,
+s,
+samp,
+section,
+small,
+span,
+strike,
+strong,
+sub,
+summary,
+sup,
+table,
+tbody,
+td,
+tfoot,
+th,
+thead,
+time,
+tr,
+tt,
+u,
+ul,
+var,
+video,
+xmp {
+  border: 0;
+  margin: 0;
+  padding: 0;
+  font-size: 100%;
+}
+
+html,
+body {
+  height: 100%;
+}
+
+article,
+aside,
+details,
+figcaption,
+figure,
+footer,
+header,
+hgroup,
+menu,
+nav,
+section {
+/*
+  Override the default (display: inline) for
+  browsers that do not recognize HTML5 tags.
+
+  IE8 (and lower) requires a shiv:
+  http://ejohn.org/blog/html5-shiv
+*/
+  display: block;
+}
+
+b,
+strong {
+/*
+  Makes browsers agree.
+  IE + Opera = font-weight: bold.
+  Gecko + WebKit = font-weight: bolder.
+*/
+  font-weight: bold;
+}
+
+img {
+  color: transparent;
+  font-size: 0;
+  vertical-align: middle;
+/*
+  For IE.
+  http://css-tricks.com/ie-fix-bicubic-scaling-for-images
+*/
+  -ms-interpolation-mode: bicubic;
+}
+
+li {
+/*
+  For IE6 + IE7:
+
+  "display: list-item" keeps bullets from
+  disappearing if hasLayout is triggered.
+*/
+  display: list-item;
+  list-style: none;
+}
+
+table {
+  border-collapse: collapse;
+  border-spacing: 0;
+}
+
+th,
+td,
+caption {
+  font-weight: normal;
+  vertical-align: top;
+  text-align: left;
+}
+
+q {
+  quotes: none;
+}
+
+q:before,
+q:after {
+  content: '';
+  content: none;
+}
+
+sub,
+sup,
+small {
+  font-size: 75%;
+}
+
+sub,
+sup {
+  line-height: 0;
+  position: relative;
+  vertical-align: baseline;
+}
+
+sub {
+  bottom: -0.25em;
+}
+
+sup {
+  top: -0.5em;
+}
+
+svg {
+/*
+  For IE9. Without, occasionally draws shapes
+  outside the boundaries of <svg> rectangle.
+*/
+  overflow: hidden;
+}
\ No newline at end of file
diff --git a/NFD/tools/nfd-status-http-server-files/robots.txt b/NFD/tools/nfd-status-http-server-files/robots.txt
new file mode 100644
index 0000000..1f53798
--- /dev/null
+++ b/NFD/tools/nfd-status-http-server-files/robots.txt
@@ -0,0 +1,2 @@
+User-agent: *
+Disallow: /
diff --git a/NFD/tools/nfd-status-http-server-files/style.css b/NFD/tools/nfd-status-http-server-files/style.css
new file mode 100644
index 0000000..176da83
--- /dev/null
+++ b/NFD/tools/nfd-status-http-server-files/style.css
@@ -0,0 +1,183 @@
+@import 'reset.css';
+@import 'text.css';
+
+@charset "utf-8";
+
+body {
+    background-color: #ffffff;
+    color: #000000;
+    font-family: sans-serif;
+}
+
+li {
+    margin: 0;
+}
+
+header, nav, article, footer, address {
+    display: block;
+}
+
+header {
+    margin: 20px;
+    width: 90%;
+}
+
+header h1 {
+    height: 50px;
+    padding-left: 55px;
+    padding-top: 6px;
+}
+
+article {
+    margin: 20px auto 20px;
+    width: 90%;
+    padding-bottom: 20px;
+}
+
+footer {
+    margin: 20px auto 0;
+    width: 90%;
+
+    padding-bottom: 2px;
+    text-align: right;
+
+    position: fixed;
+    font-height: 10px;
+    bottom: 0;
+    left: 5%;
+}
+
+h1 {
+    font-family: sans-serif;
+}
+
+h3 {
+    font-family: sans-serif;
+}
+
+h5 {
+    font-family: sans-serif;
+    color: #727272;
+}
+
+/* MISC */
+.grey {
+    color: #727272;
+    font-weight: 200;
+}
+
+.green {
+    color: #2D9A65;
+    font-weight: 200;
+}
+
+.red {
+    color: red;
+    font-weight: 200;
+    font-size: 2.4em;
+}
+
+.hidden {
+    display: none;
+}
+
+/* */
+.item-list
+{
+    border-radius: 6px;
+
+    margin-top: 10px;
+    margin-bottom: 10px;
+    font-family: sans-serif;
+    width: 100%;
+    text-align: left;
+}
+
+.item-list th
+{
+    font-weight: normal;
+    padding: 8px;
+    background: #EAF4EF;
+    border-top: 1px solid #99CCB2;
+    color: #727272;
+    text-align: left;
+}
+
+.item-list th.border-left {
+}
+
+.item-list td
+{
+    padding: 2px;
+    border-bottom: 1px solid #fff;
+    color: #000;
+    border-top: 1px solid transparent;
+}
+
+.item-sublist
+{
+    border-collapse:collapse;
+    border:1px solid gray;
+
+    padding: 4px;
+    font-family: sans-serif;
+    text-align: center;
+    margin-bottom: 0;
+}
+
+.item-sublist th
+{
+    background-color: transparent;
+    font-weight: bold;
+    padding: 4px;
+    text-align: center;
+
+    border-collapse:collapse;
+    border:1px solid gray;
+}
+
+.item-sublist th.border-left {
+}
+
+.item-sublist td
+{
+    border-collapse:collapse;
+    border:1px solid gray;
+
+    padding: 2px;
+    color: #000;
+    text-align: center;
+    min-width: 20px;
+}
+
+
+tr.center td
+{
+    text-align: center;
+}
+
+.border-left {
+    border-left: 1px solid #99CCB2;
+}
+
+.border-right {
+    border-right: 1px solid #99CCB2;
+}
+
+tfoot {
+    border-bottom: 2px solid #99CCB2;
+    background: #EAF4EF;
+}
+
+.item-list tfoot td {
+    padding: 0;
+}
+
+.odd {
+    background-color: #eeeeee;
+}
+
+.highlighted {
+    background-color: #cccccc;
+    cursor: pointer;
+}
diff --git a/NFD/tools/nfd-status-http-server-files/text.css b/NFD/tools/nfd-status-http-server-files/text.css
new file mode 100644
index 0000000..b115a70
--- /dev/null
+++ b/NFD/tools/nfd-status-http-server-files/text.css
@@ -0,0 +1,106 @@
+/*
+  960 Grid System ~ Text CSS.
+  Learn more ~ http://960.gs/
+
+  Licensed under GPL and MIT.
+*/
+
+/* `Basic HTML
+----------------------------------------------------------------------------------------------------*/
+
+body {
+    font: 13px Arial, Helvetica, sans-serif;
+}
+
+hr {
+  border: 0 #ccc solid;
+  border-top-width: 1px;
+  clear: both;
+  height: 0;
+}
+
+/* `Headings
+----------------------------------------------------------------------------------------------------*/
+
+h1 {
+	font-size: 2.4em;
+}
+
+h2 {
+	font-size: 1.8em;
+}
+
+h3 {
+	font-size: 1.4em;
+}
+
+/* h1 { */
+/*   font-size: 25px; */
+/* } */
+
+/* h2 { */
+/*   font-size: 23px; */
+/* } */
+
+/* h3 { */
+/*   font-size: 21px; */
+/* } */
+
+h4 {
+  font-size: 19px;
+}
+
+h5 {
+  font-size: 17px;
+}
+
+h6 {
+  font-size: 15px;
+}
+
+/* `Spacing
+----------------------------------------------------------------------------------------------------*/
+
+/* ol { */
+/*   list-style: decimal; */
+/* } */
+
+/* ul { */
+/*   list-style: disc; */
+/* } */
+
+/* li { */
+/*   margin-left: 30px; */
+/* } */
+
+p,
+dl,
+hr,
+h1,
+h2,
+h3,
+h4,
+h5,
+h6,
+ol,
+ul,
+pre,
+table,
+address,
+fieldset,
+figure {
+  margin-bottom: 10px;
+}
+
+pre {
+    /* padding: 0px 24px; */
+    white-space: pre-wrap;
+    background-color: #F9F9F9;
+    border: 1px dashed #2F6FAB;
+    color: black;
+    padding: 1em;
+    font-size: 13px;
+}
+code {
+    font-family: Consolas, Monaco, Andale Mono, monospace;
+}
\ No newline at end of file
diff --git a/NFD/tools/nfd-status-http-server.py b/NFD/tools/nfd-status-http-server.py
new file mode 100755
index 0000000..3d34d73
--- /dev/null
+++ b/NFD/tools/nfd-status-http-server.py
@@ -0,0 +1,199 @@
+#!/usr/bin/env python2.7
+# -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
+
+"""
+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
+
+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 BaseHTTPServer import HTTPServer
+from SimpleHTTPServer import SimpleHTTPRequestHandler
+from SocketServer import ThreadingMixIn
+import sys
+import subprocess
+import StringIO
+import urlparse
+import logging
+import cgi
+import argparse
+import socket
+import os
+
+
+class StatusHandler(SimpleHTTPRequestHandler):
+    """ The handler class to handle requests."""
+    def do_GET(self):
+        # get the url info to decide how to respond
+        parsedPath = urlparse.urlparse(self.path)
+        if parsedPath.path == "/":
+            # get current nfd status, and use it as result message
+            (res, resultMessage) = self.getNfdStatus()
+            self.send_response(200)
+            if res == 0:
+                self.send_header("Content-type", "text/xml; charset=UTF-8")
+            else:
+                self.send_header("Content-type", "text/html; charset=UTF-8")
+
+            self.end_headers()
+            self.wfile.write(resultMessage)
+        elif parsedPath.path == "/robots.txt" and self.server.robots == True:
+            resultMessage = ""
+            self.send_response(200)
+            self.send_header("Content-type", "text/plain")
+            self.end_headers()
+            self.wfile.write(resultMessage)
+        else:
+            SimpleHTTPRequestHandler.do_GET(self)
+
+    def log_message(self, format, *args):
+        if self.server.verbose:
+            logging.info("%s - %s\n" % (self.address_string(),
+                        format % args))
+
+    def getNfdStatus(self):
+        """
+        This function is to call nfd-status command
+        to get xml format output
+        """
+        sp = subprocess.Popen(['nfd-status', '-x'], stdout=subprocess.PIPE, close_fds=True)
+        output = sp.communicate()[0]
+        if sp.returncode == 0:
+            # add the xml-stylesheet processing instruction after the 1st '>' symbol
+            newLineIndex = output.index('>') + 1
+            resultStr = output[:newLineIndex]\
+                      + "<?xml-stylesheet type=\"text/xsl\" href=\"nfd-status.xsl\"?>"\
+                      + output[newLineIndex:]
+            return (sp.returncode, resultStr)
+        else:
+            htmlStr = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional'\
+                    + '//EN" "http://www.w3.org/TR/html4/loose.dtd">\n'\
+                    + '<html><head><title>NFD status</title>'\
+                    + '<meta http-equiv="Content-type" content="text/html;'\
+                    + 'charset=UTF-8"></head>\n<body>'
+            # return connection error code
+            htmlStr = htmlStr + "<p>Cannot connect to NFD,"\
+                    + " Code = " + str(sp.returncode) + "</p>\n"
+            htmlStr = htmlStr + "</body></html>"
+            return (sp.returncode, htmlStr)
+
+class ThreadHttpServer(ThreadingMixIn, HTTPServer):
+    """ Handle requests using threads """
+    def __init__(self, server, handler, verbose=False, robots=False):
+        serverAddr = server[0]
+        # socket.AF_UNSPEC is not supported, check whether it is v6 or v4
+        ipType = self.getIpType(serverAddr)
+        if ipType == socket.AF_INET6:
+            self.address_family = socket.AF_INET6
+        elif ipType == socket.AF_INET:
+            self.address_family == socket.AF_INET
+        else:
+            logging.error("The input IP address is neither IPv6 nor IPv4")
+            sys.exit(2)
+
+        try:
+            HTTPServer.__init__(self, server, handler)
+        except Exception as e:
+            logging.error(str(e))
+            sys.exit(2)
+        self.verbose = verbose
+        self.robots = robots
+
+    def getIpType(self, ipAddr):
+        """ Get ipAddr's address type """
+        # if ipAddr is an IPv6 addr, return AF_INET6
+        try:
+            socket.inet_pton(socket.AF_INET6, ipAddr)
+            return socket.AF_INET6
+        except socket.error:
+            pass
+        # if ipAddr is an IPv4 addr return AF_INET, if not, return None
+        try:
+            socket.inet_pton(socket.AF_INET, ipAddr)
+            return socket.AF_INET
+        except socket.error:
+            return None
+
+
+# main function to start
+def httpServer():
+    parser = argparse.ArgumentParser()
+    parser.add_argument("-p", type=int, metavar="port number",
+                        help="Specify the HTTP server port number, default is 8080.",
+                        dest="port", default=8080)
+    # if address is not specified, use 127.0.0.1
+    parser.add_argument("-a", default="127.0.0.1", metavar="IP address", dest="addr",
+                        help="Specify the HTTP server IP address.")
+    parser.add_argument("-r", default=False, dest="robots", action="store_true",
+                        help="Enable HTTP robots to crawl; disabled by default.")
+    parser.add_argument("-f", default="@DATAROOTDIR@/ndn", metavar="Server Directory", dest="serverDir",
+                        help="Specify the working directory of nfd-status-http-server, default is @DATAROOTDIR@/ndn.")
+    parser.add_argument("-v", default=False, dest="verbose", action="store_true",
+                        help="Verbose mode.")
+    parser.add_argument("--version", default=False, dest="version", action="store_true",
+                        help="Show version and exit")
+
+    args = vars(parser.parse_args())
+
+    if args['version']:
+        print "@VERSION@"
+        return
+
+    localPort = args["port"]
+    localAddr = args["addr"]
+    verbose = args["verbose"]
+    robots = args["robots"]
+    serverDirectory = args["serverDir"]
+
+    os.chdir(serverDirectory)
+
+    # setting log message format
+    logging.basicConfig(format='%(asctime)s [%(levelname)s] %(message)s',
+                        level=logging.INFO)
+
+    # if port is invalid, exit
+    if localPort <= 0 or localPort > 65535:
+        logging.error("Specified port number is invalid")
+        sys.exit(2)
+
+    httpd = ThreadHttpServer((localAddr, localPort), StatusHandler,
+                             verbose, robots)
+    httpServerAddr = ""
+    if httpd.address_family == socket.AF_INET6:
+        httpServerAddr = "http://[%s]:%s" % (httpd.server_address[0],
+                                             httpd.server_address[1])
+    else:
+        httpServerAddr = "http://%s:%s" % (httpd.server_address[0],
+                                           httpd.server_address[1])
+
+    logging.info("Server started - at %s" % httpServerAddr)
+
+    try:
+        httpd.serve_forever()
+    except KeyboardInterrupt:
+        pass
+
+    httpd.server_close()
+
+    logging.info("Server stopped")
+
+
+if __name__ == '__main__':
+    httpServer()
diff --git a/NFD/tools/nfd-status.cpp b/NFD/tools/nfd-status.cpp
new file mode 100644
index 0000000..42a35be
--- /dev/null
+++ b/NFD/tools/nfd-status.cpp
@@ -0,0 +1,925 @@
+/* -*- 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/>.
+ *
+ * @author Jerald Paul Abraham <jeraldabraham@email.arizona.edu>
+ */
+
+#include "version.hpp"
+
+#include <ndn-cxx/face.hpp>
+#include <ndn-cxx/name.hpp>
+#include <ndn-cxx/interest.hpp>
+#include <ndn-cxx/encoding/buffer-stream.hpp>
+
+#include <ndn-cxx/management/nfd-forwarder-status.hpp>
+#include <ndn-cxx/management/nfd-channel-status.hpp>
+#include <ndn-cxx/management/nfd-face-status.hpp>
+#include <ndn-cxx/management/nfd-fib-entry.hpp>
+#include <ndn-cxx/management/nfd-rib-entry.hpp>
+#include <ndn-cxx/management/nfd-strategy-choice.hpp>
+
+#include <boost/algorithm/string/replace.hpp>
+#include <list>
+
+namespace ndn {
+
+class NfdStatus
+{
+public:
+  explicit
+  NfdStatus(char* toolName)
+    : m_toolName(toolName)
+    , m_needVersionRetrieval(false)
+    , m_needChannelStatusRetrieval(false)
+    , m_needFaceStatusRetrieval(false)
+    , m_needFibEnumerationRetrieval(false)
+    , m_needRibStatusRetrieval(false)
+    , m_needStrategyChoiceRetrieval(false)
+    , m_isOutputXml(false)
+  {
+  }
+
+  void
+  usage()
+  {
+    std::cout << "Usage: \n  " << m_toolName << " [options]\n\n"
+      "Show NFD version and status information\n\n"
+      "Options:\n"
+      "  [-h] - print this help message\n"
+      "  [-v] - retrieve version information\n"
+      "  [-c] - retrieve channel status information\n"
+      "  [-f] - retrieve face status information\n"
+      "  [-b] - retrieve FIB information\n"
+      "  [-r] - retrieve RIB information\n"
+      "  [-s] - retrieve configured strategy choice for NDN namespaces\n"
+      "  [-x] - output NFD status information in XML format\n"
+      "\n"
+      "  [-V] - show version information of nfd-status and exit\n"
+      "\n"
+      "If no options are provided, all information is retrieved.\n"
+      "If -x is provided, other options(-v, -c, etc.) are ignored, and all information is printed in XML format.\n"
+      ;
+  }
+
+  void
+  enableVersionRetrieval()
+  {
+    m_needVersionRetrieval = true;
+  }
+
+  void
+  enableChannelStatusRetrieval()
+  {
+    m_needChannelStatusRetrieval = true;
+  }
+
+  void
+  enableFaceStatusRetrieval()
+  {
+    m_needFaceStatusRetrieval = true;
+  }
+
+  void
+  enableFibEnumerationRetrieval()
+  {
+    m_needFibEnumerationRetrieval = true;
+  }
+
+  void
+  enableStrategyChoiceRetrieval()
+  {
+    m_needStrategyChoiceRetrieval = true;
+  }
+
+  void
+  enableRibStatusRetrieval()
+  {
+    m_needRibStatusRetrieval = true;
+  }
+
+  void
+  enableXmlOutput()
+  {
+    m_isOutputXml = true;
+  }
+
+  void
+  onTimeout()
+  {
+    std::cerr << "Request timed out" << std::endl;
+
+    runNextStep();
+  }
+
+  void
+  fetchSegments(const Data& data, void (NfdStatus::*onDone)())
+  {
+    m_buffer->write((const char*)data.getContent().value(),
+                    data.getContent().value_size());
+
+    uint64_t currentSegment = data.getName().get(-1).toSegment();
+
+    const name::Component& finalBlockId = data.getMetaInfo().getFinalBlockId();
+    if (finalBlockId.empty() ||
+        finalBlockId.toSegment() > currentSegment)
+      {
+        m_face.expressInterest(data.getName().getPrefix(-1).appendSegment(currentSegment+1),
+                               bind(&NfdStatus::fetchSegments, this, _2, onDone),
+                               bind(&NfdStatus::onTimeout, this));
+      }
+    else
+      {
+        return (this->*onDone)();
+      }
+  }
+
+  void
+  escapeSpecialCharacters(std::string *data)
+  {
+    using boost::algorithm::replace_all;
+    replace_all(*data, "&",  "&amp;");
+    replace_all(*data, "\"", "&quot;");
+    replace_all(*data, "\'", "&apos;");
+    replace_all(*data, "<",  "&lt;");
+    replace_all(*data, ">",  "&gt;");
+  }
+
+  //////////////////////////////////////////////////////////////////////////////////
+  //////////////////////////////////////////////////////////////////////////////////
+
+  void
+  fetchVersionInformation()
+  {
+    Interest interest("/localhost/nfd/status");
+    interest.setChildSelector(1);
+    interest.setMustBeFresh(true);
+    m_face.expressInterest(
+                           interest,
+                           bind(&NfdStatus::afterFetchedVersionInformation, this, _2),
+                           bind(&NfdStatus::onTimeout, this));
+  }
+
+  void
+  afterFetchedVersionInformation(const Data& data)
+  {
+    nfd::ForwarderStatus status(data.getContent());
+    std::string nfdId;
+    if (data.getSignature().hasKeyLocator())
+      {
+        const ndn::KeyLocator& locator = data.getSignature().getKeyLocator();
+        if (locator.getType() == KeyLocator::KeyLocator_Name)
+          nfdId = locator.getName().toUri();
+        //todo: KeyDigest supporting
+      }
+
+    if (m_isOutputXml)
+      {
+        std::cout << "<generalStatus>";
+        std::cout << "<nfdId>"
+                  << nfdId << "</nfdId>";
+        std::cout << "<version>"
+                  << status.getNfdVersion() << "</version>";
+        std::cout << "<startTime>"
+                  << time::toString(status.getStartTimestamp(), "%Y-%m-%dT%H:%M:%S%F")
+                  << "</startTime>";
+        std::cout << "<currentTime>"
+                  << time::toString(status.getCurrentTimestamp(), "%Y-%m-%dT%H:%M:%S%F")
+                  << "</currentTime>";
+        std::cout << "<uptime>PT"
+                  << time::duration_cast<time::seconds>(status.getCurrentTimestamp()
+                                                        - status.getStartTimestamp()).count()
+                  << "S</uptime>";
+        std::cout << "<nNameTreeEntries>"     << status.getNNameTreeEntries()
+                  << "</nNameTreeEntries>";
+        std::cout << "<nFibEntries>"          << status.getNFibEntries()
+                  << "</nFibEntries>";
+        std::cout << "<nPitEntries>"          << status.getNPitEntries()
+                  << "</nPitEntries>";
+        std::cout << "<nMeasurementsEntries>" << status.getNMeasurementsEntries()
+                  << "</nMeasurementsEntries>";
+        std::cout << "<nCsEntries>"           << status.getNCsEntries()
+                  << "</nCsEntries>";
+        std::cout << "<packetCounters>";
+        std::cout << "<incomingPackets>";
+        std::cout << "<nInterests>"           << status.getNInInterests()
+                  << "</nInterests>";
+        std::cout << "<nDatas>"               << status.getNInDatas()
+                  << "</nDatas>";
+        std::cout << "</incomingPackets>";
+        std::cout << "<outgoingPackets>";
+        std::cout << "<nInterests>"           << status.getNOutInterests()
+                  << "</nInterests>";
+        std::cout << "<nDatas>"               << status.getNOutDatas()
+                  << "</nDatas>";
+        std::cout << "</outgoingPackets>";
+        std::cout << "</packetCounters>";
+        std::cout << "</generalStatus>";
+      }
+    else
+      {
+        std::cout << "General NFD status:" << std::endl;
+        std::cout << "                 nfdId="
+                  << nfdId << std::endl;
+        std::cout << "               version="
+                  << status.getNfdVersion() << std::endl;
+        std::cout << "             startTime="
+                  << time::toIsoString(status.getStartTimestamp()) << std::endl;
+        std::cout << "           currentTime="
+                  << time::toIsoString(status.getCurrentTimestamp()) << std::endl;
+        std::cout << "                uptime="
+                  << time::duration_cast<time::seconds>(status.getCurrentTimestamp()
+                                                        - status.getStartTimestamp()) << std::endl;
+
+        std::cout << "      nNameTreeEntries=" << status.getNNameTreeEntries()     << std::endl;
+        std::cout << "           nFibEntries=" << status.getNFibEntries()          << std::endl;
+        std::cout << "           nPitEntries=" << status.getNPitEntries()          << std::endl;
+        std::cout << "  nMeasurementsEntries=" << status.getNMeasurementsEntries() << std::endl;
+        std::cout << "            nCsEntries=" << status.getNCsEntries()           << std::endl;
+        std::cout << "          nInInterests=" << status.getNInInterests()         << std::endl;
+        std::cout << "         nOutInterests=" << status.getNOutInterests()        << std::endl;
+        std::cout << "              nInDatas=" << status.getNInDatas()             << std::endl;
+        std::cout << "             nOutDatas=" << status.getNOutDatas()            << std::endl;
+      }
+
+    runNextStep();
+  }
+
+  //////////////////////////////////////////////////////////////////////////////////
+  //////////////////////////////////////////////////////////////////////////////////
+
+  void
+  fetchChannelStatusInformation()
+  {
+    m_buffer = make_shared<OBufferStream>();
+
+    Interest interest("/localhost/nfd/faces/channels");
+    interest.setChildSelector(1);
+    interest.setMustBeFresh(true);
+
+    m_face.expressInterest(interest,
+                           bind(&NfdStatus::fetchSegments, this, _2,
+                                &NfdStatus::afterFetchedChannelStatusInformation),
+                           bind(&NfdStatus::onTimeout, this));
+  }
+
+  void
+  afterFetchedChannelStatusInformation()
+  {
+    ConstBufferPtr buf = m_buffer->buf();
+    if (m_isOutputXml)
+      {
+        std::cout << "<channels>";
+
+        Block block;
+        size_t offset = 0;
+        while (offset < buf->size())
+          {
+            bool ok = Block::fromBuffer(buf, offset, block);
+            if (!ok)
+              {
+                std::cerr << "ERROR: cannot decode ChannelStatus TLV" << std::endl;
+                break;
+              }
+
+            offset += block.size();
+
+            nfd::ChannelStatus channelStatus(block);
+
+            std::cout << "<channel>";
+
+            std::string localUri(channelStatus.getLocalUri());
+            escapeSpecialCharacters(&localUri);
+            std::cout << "<localUri>" << localUri << "</localUri>";
+            std::cout << "</channel>";
+          }
+        std::cout << "</channels>";
+      }
+    else
+      {
+        std::cout << "Channels:" << std::endl;
+
+        Block block;
+        size_t offset = 0;
+        while (offset < buf->size())
+          {
+            bool ok = Block::fromBuffer(buf, offset, block);
+            if (!ok)
+              {
+                std::cerr << "ERROR: cannot decode ChannelStatus TLV" << std::endl;
+                break;
+              }
+
+            offset += block.size();
+
+            nfd::ChannelStatus channelStatus(block);
+            std::cout << "  " << channelStatus.getLocalUri() << std::endl;
+          }
+       }
+
+    runNextStep();
+  }
+
+  //////////////////////////////////////////////////////////////////////////////////
+  //////////////////////////////////////////////////////////////////////////////////
+
+  void
+  fetchFaceStatusInformation()
+  {
+    m_buffer = make_shared<OBufferStream>();
+
+    Interest interest("/localhost/nfd/faces/list");
+    interest.setChildSelector(1);
+    interest.setMustBeFresh(true);
+
+    m_face.expressInterest(interest,
+                           bind(&NfdStatus::fetchSegments, this, _2,
+                                &NfdStatus::afterFetchedFaceStatusInformation),
+                           bind(&NfdStatus::onTimeout, this));
+  }
+
+  void
+  afterFetchedFaceStatusInformation()
+  {
+    ConstBufferPtr buf = m_buffer->buf();
+    if (m_isOutputXml)
+      {
+        std::cout << "<faces>";
+
+        Block block;
+        size_t offset = 0;
+        while (offset < buf->size())
+          {
+            bool ok = Block::fromBuffer(buf, offset, block);
+            if (!ok)
+              {
+                std::cerr << "ERROR: cannot decode FaceStatus TLV" << std::endl;
+                break;
+              }
+
+            offset += block.size();
+
+            nfd::FaceStatus faceStatus(block);
+
+            std::cout << "<face>";
+            std::cout << "<faceId>" << faceStatus.getFaceId() << "</faceId>";
+
+            std::string remoteUri(faceStatus.getRemoteUri());
+            escapeSpecialCharacters(&remoteUri);
+            std::cout << "<remoteUri>" << remoteUri << "</remoteUri>";
+
+            std::string localUri(faceStatus.getLocalUri());
+            escapeSpecialCharacters(&localUri);
+            std::cout << "<localUri>" << localUri << "</localUri>";
+
+            if (faceStatus.hasExpirationPeriod()) {
+              std::cout << "<expirationPeriod>PT"
+                        << time::duration_cast<time::seconds>(faceStatus.getExpirationPeriod())
+                             .count() << "S"
+                        << "</expirationPeriod>";
+            }
+
+            std::cout << "<faceScope>" << faceStatus.getFaceScope()
+                      << "</faceScope>";
+            std::cout << "<facePersistency>" << faceStatus.getFacePersistency()
+                      << "</facePersistency>";
+            std::cout << "<linkType>" << faceStatus.getLinkType()
+                      << "</linkType>";
+
+            std::cout << "<packetCounters>";
+            std::cout << "<incomingPackets>";
+            std::cout << "<nInterests>"       << faceStatus.getNInInterests()
+                      << "</nInterests>";
+            std::cout << "<nDatas>"           << faceStatus.getNInDatas()
+                      << "</nDatas>";
+            std::cout << "</incomingPackets>";
+            std::cout << "<outgoingPackets>";
+            std::cout << "<nInterests>"       << faceStatus.getNOutInterests()
+                      << "</nInterests>";
+            std::cout << "<nDatas>"           << faceStatus.getNOutDatas()
+                      << "</nDatas>";
+            std::cout << "</outgoingPackets>";
+            std::cout << "</packetCounters>";
+
+            std::cout << "<byteCounters>";
+            std::cout << "<incomingBytes>"    << faceStatus.getNInBytes()
+                      << "</incomingBytes>";
+            std::cout << "<outgoingBytes>"    << faceStatus.getNOutBytes()
+                      << "</outgoingBytes>";
+            std::cout << "</byteCounters>";
+
+            std::cout << "</face>";
+          }
+        std::cout << "</faces>";
+      }
+    else
+      {
+        std::cout << "Faces:" << std::endl;
+
+        Block block;
+        size_t offset = 0;
+        while (offset < buf->size())
+          {
+            bool ok = Block::fromBuffer(buf, offset, block);
+            if (!ok)
+              {
+                std::cerr << "ERROR: cannot decode FaceStatus TLV" << std::endl;
+                break;
+              }
+
+            offset += block.size();
+
+            nfd::FaceStatus faceStatus(block);
+
+            std::cout << "  faceid=" << faceStatus.getFaceId()
+                      << " remote=" << faceStatus.getRemoteUri()
+                      << " local=" << faceStatus.getLocalUri();
+            if (faceStatus.hasExpirationPeriod()) {
+              std::cout  << " expires="
+                         << time::duration_cast<time::seconds>(faceStatus.getExpirationPeriod())
+                              .count() << "s";
+            }
+            std::cout << " counters={"
+                      << "in={" << faceStatus.getNInInterests() << "i "
+                      << faceStatus.getNInDatas() << "d "
+                      << faceStatus.getNInBytes() << "B}"
+                      << " out={" << faceStatus.getNOutInterests() << "i "
+                      << faceStatus.getNOutDatas() << "d "
+                      << faceStatus.getNOutBytes() << "B}"
+                      << "}";
+            std::cout << " " << faceStatus.getFaceScope()
+                      << " " << faceStatus.getFacePersistency()
+                      << " " << faceStatus.getLinkType();
+            std::cout << std::endl;
+          }
+       }
+
+    runNextStep();
+  }
+
+  //////////////////////////////////////////////////////////////////////////////////
+  //////////////////////////////////////////////////////////////////////////////////
+
+  void
+  fetchFibEnumerationInformation()
+  {
+    m_buffer = make_shared<OBufferStream>();
+
+    Interest interest("/localhost/nfd/fib/list");
+    interest.setChildSelector(1);
+    interest.setMustBeFresh(true);
+    m_face.expressInterest(interest,
+                           bind(&NfdStatus::fetchSegments, this, _2,
+                                &NfdStatus::afterFetchedFibEnumerationInformation),
+                           bind(&NfdStatus::onTimeout, this));
+  }
+
+  void
+  afterFetchedFibEnumerationInformation()
+  {
+    ConstBufferPtr buf = m_buffer->buf();
+    if (m_isOutputXml)
+      {
+        std::cout << "<fib>";
+
+        Block block;
+        size_t offset = 0;
+        while (offset < buf->size())
+          {
+            bool ok = Block::fromBuffer(buf, offset, block);
+            if (!ok)
+              {
+                std::cerr << "ERROR: cannot decode FibEntry TLV";
+                break;
+              }
+            offset += block.size();
+
+            nfd::FibEntry fibEntry(block);
+
+            std::cout << "<fibEntry>";
+            std::string prefix(fibEntry.getPrefix().toUri());
+            escapeSpecialCharacters(&prefix);
+            std::cout << "<prefix>" << prefix << "</prefix>";
+            std::cout << "<nextHops>";
+            for (std::list<nfd::NextHopRecord>::const_iterator
+                   nextHop = fibEntry.getNextHopRecords().begin();
+                 nextHop != fibEntry.getNextHopRecords().end();
+                 ++nextHop)
+              {
+                std::cout << "<nextHop>" ;
+                std::cout << "<faceId>"  << nextHop->getFaceId() << "</faceId>";
+                std::cout << "<cost>"    << nextHop->getCost()   << "</cost>";
+                std::cout << "</nextHop>";
+              }
+            std::cout << "</nextHops>";
+            std::cout << "</fibEntry>";
+          }
+
+        std::cout << "</fib>";
+      }
+    else
+      {
+        std::cout << "FIB:" << std::endl;
+
+        Block block;
+        size_t offset = 0;
+        while (offset < buf->size())
+          {
+            bool ok = Block::fromBuffer(buf, offset, block);
+            if (!ok)
+              {
+                std::cerr << "ERROR: cannot decode FibEntry TLV" << std::endl;
+                break;
+              }
+            offset += block.size();
+
+            nfd::FibEntry fibEntry(block);
+
+            std::cout << "  " << fibEntry.getPrefix() << " nexthops={";
+            for (std::list<nfd::NextHopRecord>::const_iterator
+                   nextHop = fibEntry.getNextHopRecords().begin();
+                 nextHop != fibEntry.getNextHopRecords().end();
+                 ++nextHop)
+              {
+                if (nextHop != fibEntry.getNextHopRecords().begin())
+                  std::cout << ", ";
+                std::cout << "faceid=" << nextHop->getFaceId()
+                          << " (cost=" << nextHop->getCost() << ")";
+              }
+            std::cout << "}" << std::endl;
+          }
+      }
+
+    runNextStep();
+  }
+
+  //////////////////////////////////////////////////////////////////////////////////
+  //////////////////////////////////////////////////////////////////////////////////
+
+  void
+  fetchStrategyChoiceInformation()
+  {
+    m_buffer = make_shared<OBufferStream>();
+
+    Interest interest("/localhost/nfd/strategy-choice/list");
+    interest.setChildSelector(1);
+    interest.setMustBeFresh(true);
+    m_face.expressInterest(interest,
+                           bind(&NfdStatus::fetchSegments, this, _2,
+                                &NfdStatus::afterFetchedStrategyChoiceInformationInformation),
+                           bind(&NfdStatus::onTimeout, this));
+  }
+
+  void
+  afterFetchedStrategyChoiceInformationInformation()
+  {
+    ConstBufferPtr buf = m_buffer->buf();
+    if (m_isOutputXml)
+      {
+        std::cout << "<strategyChoices>";
+
+        Block block;
+        size_t offset = 0;
+        while (offset < buf->size())
+          {
+            bool ok = Block::fromBuffer(buf, offset, block);
+            if (!ok)
+              {
+                std::cerr << "ERROR: cannot decode StrategyChoice TLV";
+                break;
+              }
+            offset += block.size();
+
+            nfd::StrategyChoice strategyChoice(block);
+
+            std::cout << "<strategyChoice>";
+
+            std::string name(strategyChoice.getName().toUri());
+            escapeSpecialCharacters(&name);
+            std::cout << "<namespace>" << name << "</namespace>";
+            std::cout << "<strategy>";
+
+            std::string strategy(strategyChoice.getStrategy().toUri());
+            escapeSpecialCharacters(&strategy);
+
+            std::cout << "<name>" << strategy << "</name>";
+            std::cout << "</strategy>";
+            std::cout << "</strategyChoice>";
+          }
+
+        std::cout << "</strategyChoices>";
+      }
+    else
+      {
+        std::cout << "Strategy choices:" << std::endl;
+
+        Block block;
+        size_t offset = 0;
+        while (offset < buf->size())
+          {
+            bool ok = Block::fromBuffer(buf, offset, block);
+            if (!ok)
+              {
+                std::cerr << "ERROR: cannot decode StrategyChoice TLV" << std::endl;
+                break;
+              }
+            offset += block.size();
+
+            nfd::StrategyChoice strategyChoice(block);
+
+            std::cout << "  " << strategyChoice.getName()
+                      << " strategy=" << strategyChoice.getStrategy() << std::endl;
+          }
+      }
+
+    runNextStep();
+  }
+
+  void
+  fetchRibStatusInformation()
+  {
+    m_buffer = make_shared<OBufferStream>();
+
+    Interest interest("/localhost/nfd/rib/list");
+    interest.setChildSelector(1);
+    interest.setMustBeFresh(true);
+
+    m_face.expressInterest(interest,
+                           bind(&NfdStatus::fetchSegments, this, _2,
+                                &NfdStatus::afterFetchedRibStatusInformation),
+                           bind(&NfdStatus::onTimeout, this));
+  }
+
+  void
+  afterFetchedRibStatusInformation()
+  {
+    ConstBufferPtr buf = m_buffer->buf();
+    if (m_isOutputXml)
+      {
+        std::cout << "<rib>";
+
+        Block block;
+        size_t offset = 0;
+        while (offset < buf->size())
+          {
+            bool ok = Block::fromBuffer(buf, offset, block);
+            if (!ok)
+              {
+                std::cerr << "ERROR: cannot decode RibEntry TLV";
+                break;
+              }
+            offset += block.size();
+
+            nfd::RibEntry ribEntry(block);
+
+            std::cout << "<ribEntry>";
+            std::string prefix(ribEntry.getName().toUri());
+            escapeSpecialCharacters(&prefix);
+            std::cout << "<prefix>" << prefix << "</prefix>";
+            std::cout << "<routes>";
+            for (std::list<nfd::Route>::const_iterator
+                   nextRoute = ribEntry.begin();
+                 nextRoute != ribEntry.end();
+                 ++nextRoute)
+              {
+                std::cout << "<route>" ;
+                std::cout << "<faceId>"  << nextRoute->getFaceId() << "</faceId>";
+                std::cout << "<origin>"  << nextRoute->getOrigin() << "</origin>";
+                std::cout << "<cost>"    << nextRoute->getCost()   << "</cost>";
+
+                std::cout << "<flags>";
+                if (nextRoute->isChildInherit())
+                  std::cout << "<childInherit/>";
+                if (nextRoute->isRibCapture())
+                  std::cout << "<ribCapture/>";
+                std::cout << "</flags>";
+
+                if (!nextRoute->hasInfiniteExpirationPeriod()) {
+                  std::cout << "<expirationPeriod>PT"
+                            << time::duration_cast<time::seconds>(nextRoute->getExpirationPeriod())
+                                 .count() << "S"
+                            << "</expirationPeriod>";
+                }
+                std::cout << "</route>";
+              }
+            std::cout << "</routes>";
+            std::cout << "</ribEntry>";
+          }
+
+        std::cout << "</rib>";
+      }
+    else
+      {
+        std::cout << "RIB:" << std::endl;
+
+        Block block;
+        size_t offset = 0;
+        while (offset < buf->size())
+          {
+            bool ok = Block::fromBuffer(buf, offset, block);
+            if (!ok)
+              {
+                std::cerr << "ERROR: cannot decode RibEntry TLV" << std::endl;
+                break;
+              }
+
+            offset += block.size();
+
+            nfd::RibEntry ribEntry(block);
+
+            std::cout << "  " << ribEntry.getName().toUri() << " route={";
+            for (std::list<nfd::Route>::const_iterator
+                   nextRoute = ribEntry.begin();
+                 nextRoute != ribEntry.end();
+                 ++nextRoute)
+              {
+                if (nextRoute != ribEntry.begin())
+                  std::cout << ", ";
+                std::cout << "faceid="   << nextRoute->getFaceId()
+                          << " (origin="  << nextRoute->getOrigin()
+                          << " cost="    << nextRoute->getCost();
+                if (!nextRoute->hasInfiniteExpirationPeriod()) {
+                  std::cout << " expires="
+                            << time::duration_cast<time::seconds>(nextRoute->getExpirationPeriod())
+                               .count() << "s";
+                }
+
+                if (nextRoute->isChildInherit())
+                  std::cout << " ChildInherit";
+                if (nextRoute->isRibCapture())
+                  std::cout << " RibCapture";
+
+                std::cout << ")";
+              }
+            std::cout << "}" << std::endl;
+          }
+       }
+
+    runNextStep();
+  }
+
+
+  void
+  fetchInformation()
+  {
+    if (m_isOutputXml ||
+        (!m_needVersionRetrieval &&
+         !m_needChannelStatusRetrieval &&
+         !m_needFaceStatusRetrieval &&
+         !m_needFibEnumerationRetrieval &&
+         !m_needRibStatusRetrieval &&
+         !m_needStrategyChoiceRetrieval))
+      {
+        enableVersionRetrieval();
+        enableChannelStatusRetrieval();
+        enableFaceStatusRetrieval();
+        enableFibEnumerationRetrieval();
+        enableRibStatusRetrieval();
+        enableStrategyChoiceRetrieval();
+      }
+
+    if (m_isOutputXml)
+      m_fetchSteps.push_back(bind(&NfdStatus::printXmlHeader, this));
+
+    if (m_needVersionRetrieval)
+      m_fetchSteps.push_back(bind(&NfdStatus::fetchVersionInformation, this));
+
+    if (m_needChannelStatusRetrieval)
+      m_fetchSteps.push_back(bind(&NfdStatus::fetchChannelStatusInformation, this));
+
+    if (m_needFaceStatusRetrieval)
+      m_fetchSteps.push_back(bind(&NfdStatus::fetchFaceStatusInformation, this));
+
+    if (m_needFibEnumerationRetrieval)
+      m_fetchSteps.push_back(bind(&NfdStatus::fetchFibEnumerationInformation, this));
+
+    if (m_needRibStatusRetrieval)
+      m_fetchSteps.push_back(bind(&NfdStatus::fetchRibStatusInformation, this));
+
+    if (m_needStrategyChoiceRetrieval)
+      m_fetchSteps.push_back(bind(&NfdStatus::fetchStrategyChoiceInformation, this));
+
+    if (m_isOutputXml)
+      m_fetchSteps.push_back(bind(&NfdStatus::printXmlFooter, this));
+
+    runNextStep();
+    m_face.processEvents();
+  }
+
+private:
+  void
+  printXmlHeader()
+  {
+    std::cout << "<?xml version=\"1.0\"?>";
+    std::cout << "<nfdStatus xmlns=\"ndn:/localhost/nfd/status/1\">";
+
+    runNextStep();
+  }
+
+  void
+  printXmlFooter()
+  {
+    std::cout << "</nfdStatus>";
+
+    runNextStep();
+  }
+
+  void
+  runNextStep()
+  {
+    if (m_fetchSteps.empty())
+      return;
+
+    function<void()> nextStep = m_fetchSteps.front();
+    m_fetchSteps.pop_front();
+    nextStep();
+  }
+
+private:
+  std::string m_toolName;
+  bool m_needVersionRetrieval;
+  bool m_needChannelStatusRetrieval;
+  bool m_needFaceStatusRetrieval;
+  bool m_needFibEnumerationRetrieval;
+  bool m_needRibStatusRetrieval;
+  bool m_needStrategyChoiceRetrieval;
+  bool m_isOutputXml;
+  Face m_face;
+
+  shared_ptr<OBufferStream> m_buffer;
+
+  std::deque<function<void()> > m_fetchSteps;
+};
+
+}
+
+int main(int argc, char* argv[])
+{
+  int option;
+  ndn::NfdStatus nfdStatus(argv[0]);
+
+  while ((option = getopt(argc, argv, "hvcfbrsxV")) != -1) {
+    switch (option) {
+    case 'h':
+      nfdStatus.usage();
+      return 0;
+    case 'v':
+      nfdStatus.enableVersionRetrieval();
+      break;
+    case 'c':
+      nfdStatus.enableChannelStatusRetrieval();
+      break;
+    case 'f':
+      nfdStatus.enableFaceStatusRetrieval();
+      break;
+    case 'b':
+      nfdStatus.enableFibEnumerationRetrieval();
+      break;
+    case 'r':
+      nfdStatus.enableRibStatusRetrieval();
+      break;
+    case 's':
+      nfdStatus.enableStrategyChoiceRetrieval();
+      break;
+    case 'x':
+      nfdStatus.enableXmlOutput();
+      break;
+    case 'V':
+      std::cout << NFD_VERSION_BUILD_STRING << std::endl;
+      return 0;
+    default:
+      nfdStatus.usage();
+      return 1;
+    }
+  }
+
+  try {
+    nfdStatus.fetchInformation();
+  }
+  catch (std::exception& e) {
+    std::cerr << "ERROR: " << e.what() << std::endl;
+    return 2;
+  }
+
+  return 0;
+}
diff --git a/NFD/tools/nfd-stop.sh b/NFD/tools/nfd-stop.sh
new file mode 100755
index 0000000..ed55929
--- /dev/null
+++ b/NFD/tools/nfd-stop.sh
@@ -0,0 +1,24 @@
+#!@BASH@
+
+VERSION="@VERSION@"
+
+case "$1" in
+  -h)
+    echo Usage
+    echo $0
+    echo "  Stop NFD and RIB Management daemon"
+    exit 0
+    ;;
+  -V)
+    echo $VERSION
+    exit 0
+    ;;
+  "") ;; # do nothing
+  *)
+    echo "Unrecognized option $1"
+    exit 1
+    ;;
+esac
+
+sudo killall nrd
+sudo killall nfd
diff --git a/NFD/tools/nfdc.cpp b/NFD/tools/nfdc.cpp
new file mode 100644
index 0000000..dcb228c
--- /dev/null
+++ b/NFD/tools/nfdc.cpp
@@ -0,0 +1,619 @@
+/* -*- 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/>.
+ */
+
+#include "nfdc.hpp"
+#include "version.hpp"
+
+#include <boost/lexical_cast.hpp>
+#include <boost/algorithm/string.hpp>
+#include <boost/algorithm/string/regex_find_format.hpp>
+#include <boost/regex.hpp>
+
+#include <ndn-cxx/management/nfd-face-query-filter.hpp>
+#include <ndn-cxx/management/nfd-face-status.hpp>
+#include <ndn-cxx/util/segment-fetcher.hpp>
+
+void
+usage(const char* programName)
+{
+  std::cout << "Usage:\n" << programName  << " [-h] [-V] COMMAND [<Command Options>]\n"
+    "       -h print usage and exit\n"
+    "       -V print version and exit\n"
+    "\n"
+    "   COMMAND can be one of the following:\n"
+    "       register [-I] [-C] [-c cost] [-e expiration time] [-o origin] name <faceId | faceUri>\n"
+    "           register name to the given faceId or faceUri\n"
+    "           -I: unset CHILD_INHERIT flag\n"
+    "           -C: set CAPTURE flag\n"
+    "           -c: specify cost (default 0)\n"
+    "           -e: specify expiration time in ms\n"
+    "               (by default the entry remains in FIB for the lifetime of the associated face)\n"
+    "           -o: specify origin\n"
+    "               0 for Local producer applications, 128 for NLSR, 255(default) for static routes\n"
+    "       unregister [-o origin] name <faceId | faceUri>\n"
+    "           unregister name from the given faceId\n"
+    "       create <faceUri> \n"
+    "           Create a face in one of the following formats:\n"
+    "           UDP unicast:    udp[4|6]://<remote-IP-or-host>[:<remote-port>]\n"
+    "           TCP:            tcp[4|6]://<remote-IP-or-host>[:<remote-port>] \n"
+    "       destroy <faceId | faceUri> \n"
+    "           Destroy a face\n"
+    "       set-strategy <name> <strategy> \n"
+    "           Set the strategy for a namespace \n"
+    "       unset-strategy <name> \n"
+    "           Unset the strategy for a namespace \n"
+    "       add-nexthop [-c <cost>] <name> <faceId | faceUri>\n"
+    "           Add a nexthop to a FIB entry\n"
+    "           -c: specify cost (default 0)\n"
+    "       remove-nexthop <name> <faceId | faceUri> \n"
+    "           Remove a nexthop from a FIB entry\n"
+    << std::endl;
+}
+
+namespace nfdc {
+
+using std::bind;
+
+const ndn::time::milliseconds Nfdc::DEFAULT_EXPIRATION_PERIOD = ndn::time::milliseconds::max();
+const uint64_t Nfdc::DEFAULT_COST = 0;
+
+Nfdc::FaceIdFetcher::FaceIdFetcher(ndn::Face& face,
+                                   Controller& controller,
+                                   bool allowCreate,
+                                   const SuccessCallback& onSucceed,
+                                   const FailureCallback& onFail)
+  : m_face(face)
+  , m_controller(controller)
+  , m_allowCreate(allowCreate)
+  , m_onSucceed(onSucceed)
+  , m_onFail(onFail)
+{
+}
+
+void
+Nfdc::FaceIdFetcher::start(ndn::Face& face,
+                           Controller& controller,
+                           const std::string& input,
+                           bool allowCreate,
+                           const SuccessCallback& onSucceed,
+                           const FailureCallback& onFail)
+{
+  // 1. Try parse input as FaceId, if input is FaceId, succeed with parsed FaceId
+  // 2. Try parse input as FaceUri, if input is not FaceUri, fail
+  // 3. Canonize faceUri
+  // 4. If canonization fails, fail
+  // 5. Query for face
+  // 6. If query succeeds and finds a face, succeed with found FaceId
+  // 7. Create face
+  // 8. If face creation succeeds, succeed with created FaceId
+  // 9. Fail
+
+  boost::regex e("^[a-z0-9]+\\:.*");
+  if (!boost::regex_match(input, e)) {
+    try
+      {
+        u_int32_t faceId = boost::lexical_cast<uint32_t>(input);
+        onSucceed(faceId);
+        return;
+      }
+    catch (boost::bad_lexical_cast&)
+      {
+        onFail("No valid faceId or faceUri is provided");
+        return;
+      }
+  }
+  else {
+    ndn::util::FaceUri faceUri;
+    if (!faceUri.parse(input)) {
+      onFail("FaceUri parse failed");
+      return;
+    }
+
+    auto fetcher = new FaceIdFetcher(std::ref(face), std::ref(controller),
+                                     allowCreate, onSucceed, onFail);
+
+    fetcher->startGetFaceId(faceUri);
+  }
+}
+
+void
+Nfdc::FaceIdFetcher::startGetFaceId(const ndn::util::FaceUri& faceUri)
+{
+  faceUri.canonize(bind(&FaceIdFetcher::onCanonizeSuccess, this, _1),
+                   bind(&FaceIdFetcher::onCanonizeFailure, this, _1),
+                   m_face.getIoService(), ndn::time::seconds(4));
+}
+
+void
+Nfdc::FaceIdFetcher::onCanonizeSuccess(const ndn::util::FaceUri& canonicalUri)
+{
+  ndn::Name queryName("/localhost/nfd/faces/query");
+  ndn::nfd::FaceQueryFilter queryFilter;
+  queryFilter.setRemoteUri(canonicalUri.toString());
+  queryName.append(queryFilter.wireEncode());
+
+  ndn::Interest interestPacket(queryName);
+  interestPacket.setMustBeFresh(true);
+  interestPacket.setInterestLifetime(ndn::time::milliseconds(4000));
+  auto interest = std::make_shared<ndn::Interest>(interestPacket);
+
+  ndn::util::SegmentFetcher::fetch(m_face, *interest,
+                                   ndn::util::DontVerifySegment(),
+                                   bind(&FaceIdFetcher::onQuerySuccess,
+                                        this, _1, canonicalUri),
+                                   bind(&FaceIdFetcher::onQueryFailure,
+                                        this, _1, canonicalUri));
+}
+
+void
+Nfdc::FaceIdFetcher::onCanonizeFailure(const std::string& reason)
+{
+  fail("Canonize faceUri failed : " + reason);
+}
+
+void
+Nfdc::FaceIdFetcher::onQuerySuccess(const ndn::ConstBufferPtr& data,
+                                    const ndn::util::FaceUri& canonicalUri)
+{
+  size_t offset = 0;
+  ndn::Block block;
+  bool ok = ndn::Block::fromBuffer(data, offset, block);
+
+  if (!ok) {
+    if (m_allowCreate) {
+      startFaceCreate(canonicalUri);
+    }
+    else {
+      fail("Fail to find faceId");
+    }
+  }
+  else {
+    try {
+      FaceStatus status(block);
+      succeed(status.getFaceId());
+    }
+    catch (const ndn::tlv::Error& e) {
+      std::string errorMessage(e.what());
+      fail("ERROR: " + errorMessage);
+    }
+  }
+}
+
+void
+Nfdc::FaceIdFetcher::onQueryFailure(uint32_t errorCode,
+                                    const ndn::util::FaceUri& canonicalUri)
+{
+  std::stringstream ss;
+  ss << "Cannot fetch data (code " << errorCode << ")";
+  fail(ss.str());
+}
+
+void
+Nfdc::FaceIdFetcher::onFaceCreateError(uint32_t code,
+                                       const std::string& error,
+                                       const std::string& message)
+{
+  std::stringstream ss;
+  ss << message << " : " << error << " (code " << code << ")";
+  fail(ss.str());
+}
+
+void
+Nfdc::FaceIdFetcher::startFaceCreate(const ndn::util::FaceUri& canonicalUri)
+{
+  ControlParameters parameters;
+  parameters.setUri(canonicalUri.toString());
+
+  m_controller.start<FaceCreateCommand>(parameters,
+                                        [this] (const ControlParameters& result) {
+                                          succeed(result.getFaceId());
+                                        },
+                                        bind(&FaceIdFetcher::onFaceCreateError, this, _1, _2,
+                                             "Face creation failed"));
+}
+
+void
+Nfdc::FaceIdFetcher::succeed(uint32_t faceId)
+{
+  m_onSucceed(faceId);
+  delete this;
+}
+
+void
+Nfdc::FaceIdFetcher::fail(const std::string& reason)
+{
+  m_onFail(reason);
+  delete this;
+}
+
+Nfdc::Nfdc(ndn::Face& face)
+  : m_flags(ROUTE_FLAG_CHILD_INHERIT)
+  , m_cost(DEFAULT_COST)
+  , m_origin(ROUTE_ORIGIN_STATIC)
+  , m_expires(DEFAULT_EXPIRATION_PERIOD)
+  , m_face(face)
+  , m_controller(face, m_keyChain)
+  , m_ioService(face.getIoService())
+{
+}
+
+Nfdc::~Nfdc()
+{
+}
+
+bool
+Nfdc::dispatch(const std::string& command)
+{
+  if (command == "add-nexthop") {
+    if (m_nOptions != 2)
+      return false;
+    fibAddNextHop();
+  }
+  else if (command == "remove-nexthop") {
+    if (m_nOptions != 2)
+      return false;
+    fibRemoveNextHop();
+  }
+  else if (command == "register") {
+    if (m_nOptions != 2)
+      return false;
+    ribRegisterPrefix();
+  }
+  else if (command == "unregister") {
+    if (m_nOptions != 2)
+      return false;
+    ribUnregisterPrefix();
+  }
+  else if (command == "create") {
+    if (m_nOptions != 1)
+      return false;
+    faceCreate();
+  }
+  else if (command == "destroy") {
+    if (m_nOptions != 1)
+      return false;
+    faceDestroy();
+  }
+  else if (command == "set-strategy") {
+    if (m_nOptions != 2)
+      return false;
+    strategyChoiceSet();
+  }
+  else if (command == "unset-strategy") {
+    if (m_nOptions != 1)
+      return false;
+    strategyChoiceUnset();
+  }
+  else
+    usage(m_programName);
+
+  return true;
+}
+
+void
+Nfdc::fibAddNextHop()
+{
+  m_name = m_commandLineArguments[0];
+  const std::string& faceName = m_commandLineArguments[1];
+
+  FaceIdFetcher::start(m_face, m_controller, faceName, true,
+                       [this] (const uint32_t faceId) {
+                         ControlParameters parameters;
+                         parameters
+                           .setName(m_name)
+                           .setCost(m_cost)
+                           .setFaceId(faceId);
+
+                         m_controller
+                           .start<FibAddNextHopCommand>(parameters,
+                                                        bind(&Nfdc::onSuccess, this, _1,
+                                                             "Nexthop insertion succeeded"),
+                                                        bind(&Nfdc::onError, this, _1, _2,
+                                                             "Nexthop insertion failed"));
+                       },
+                       bind(&Nfdc::onObtainFaceIdFailure, this, _1));
+}
+
+void
+Nfdc::fibRemoveNextHop()
+{
+  m_name = m_commandLineArguments[0];
+  const std::string& faceName = m_commandLineArguments[1];
+
+  FaceIdFetcher::start(m_face, m_controller, faceName, false,
+                       [this] (const uint32_t faceId) {
+                         ControlParameters parameters;
+                         parameters
+                           .setName(m_name)
+                           .setFaceId(faceId);
+
+                         m_controller
+                           .start<FibRemoveNextHopCommand>(parameters,
+                                                           bind(&Nfdc::onSuccess, this, _1,
+                                                                "Nexthop removal succeeded"),
+                                                           bind(&Nfdc::onError, this, _1, _2,
+                                                                "Nexthop removal failed"));
+                       },
+                       bind(&Nfdc::onObtainFaceIdFailure, this, _1));
+}
+
+void
+Nfdc::ribRegisterPrefix()
+{
+  m_name = m_commandLineArguments[0];
+  const std::string& faceName = m_commandLineArguments[1];
+
+  FaceIdFetcher::start(m_face, m_controller, faceName, true,
+                       [this] (const uint32_t faceId) {
+                         ControlParameters parameters;
+                         parameters
+                           .setName(m_name)
+                           .setCost(m_cost)
+                           .setFlags(m_flags)
+                           .setOrigin(m_origin)
+                           .setFaceId(faceId);
+
+                         if (m_expires != DEFAULT_EXPIRATION_PERIOD)
+                           parameters.setExpirationPeriod(m_expires);
+
+                         m_controller
+                           .start<RibRegisterCommand>(parameters,
+                                                      bind(&Nfdc::onSuccess, this, _1,
+                                                           "Successful in name registration"),
+                                                      bind(&Nfdc::onError, this, _1, _2,
+                                                           "Failed in name registration"));
+                       },
+                       bind(&Nfdc::onObtainFaceIdFailure, this, _1));
+}
+
+void
+Nfdc::ribUnregisterPrefix()
+{
+  m_name = m_commandLineArguments[0];
+  const std::string& faceName = m_commandLineArguments[1];
+
+  FaceIdFetcher::start(m_face, m_controller, faceName, false,
+                       [this] (const uint32_t faceId) {
+                         ControlParameters parameters;
+                         parameters
+                           .setName(m_name)
+                           .setFaceId(faceId)
+                           .setOrigin(m_origin);
+
+                         m_controller
+                           .start<RibUnregisterCommand>(parameters,
+                                                        bind(&Nfdc::onSuccess, this, _1,
+                                                             "Successful in unregistering name"),
+                                                        bind(&Nfdc::onError, this, _1, _2,
+                                                             "Failed in unregistering name"));
+                       },
+                       bind(&Nfdc::onObtainFaceIdFailure, this, _1));
+}
+
+void
+Nfdc::onCanonizeFailure(const std::string& reason)
+{
+  std::cerr << reason << std::endl;
+}
+
+void
+Nfdc::onObtainFaceIdFailure(const std::string& message)
+{
+  std::cerr << "Obtain faceId failure: " << message << std::endl;
+}
+
+void
+Nfdc::faceCreate()
+{
+  boost::regex e("^[a-z0-9]+\\:.*");
+  if (!boost::regex_match(m_commandLineArguments[0], e))
+    throw Error("invalid uri format");
+
+  ndn::util::FaceUri faceUri;
+  faceUri.parse(m_commandLineArguments[0]);
+
+  faceUri.canonize(bind(&Nfdc::startFaceCreate, this, _1),
+                   bind(&Nfdc::onCanonizeFailure, this, _1),
+                   m_ioService, ndn::time::seconds(4));
+}
+
+void
+Nfdc::startFaceCreate(const ndn::util::FaceUri& canonicalUri)
+{
+  ControlParameters parameters;
+  parameters.setUri(canonicalUri.toString());
+
+  m_controller.start<FaceCreateCommand>(parameters,
+                                        bind(&Nfdc::onSuccess, this, _1,
+                                             "Face creation succeeded"),
+                                        bind(&Nfdc::onError, this, _1, _2,
+                                             "Face creation failed"));
+}
+
+void
+Nfdc::faceDestroy()
+{
+  ControlParameters parameters;
+  const std::string& faceName = m_commandLineArguments[0];
+
+  FaceIdFetcher::start(m_face, m_controller, faceName, false,
+                       [this] (const uint32_t faceId) {
+                         ControlParameters faceParameters;
+                         faceParameters.setFaceId(faceId);
+
+                         m_controller.start<FaceDestroyCommand>(faceParameters,
+                                                                bind(&Nfdc::onSuccess, this, _1,
+                                                                     "Face destroy succeeded"),
+                                                                bind(&Nfdc::onError, this, _1, _2,
+                                                                     "Face destroy failed"));
+                       },
+                       bind(&Nfdc::onObtainFaceIdFailure, this, _1));
+}
+
+void
+Nfdc::strategyChoiceSet()
+{
+  const std::string& name = m_commandLineArguments[0];
+  const std::string& strategy = m_commandLineArguments[1];
+
+  ControlParameters parameters;
+  parameters
+    .setName(name)
+    .setStrategy(strategy);
+
+  m_controller.start<StrategyChoiceSetCommand>(parameters,
+                                               bind(&Nfdc::onSuccess, this, _1,
+                                                    "Successfully set strategy choice"),
+                                               bind(&Nfdc::onError, this, _1, _2,
+                                                    "Failed to set strategy choice"));
+}
+
+void
+Nfdc::strategyChoiceUnset()
+{
+  const std::string& name = m_commandLineArguments[0];
+
+  ControlParameters parameters;
+  parameters.setName(name);
+
+  m_controller.start<StrategyChoiceUnsetCommand>(parameters,
+                                                 bind(&Nfdc::onSuccess, this, _1,
+                                                      "Successfully unset strategy choice"),
+                                                 bind(&Nfdc::onError, this, _1, _2,
+                                                      "Failed to unset strategy choice"));
+}
+
+void
+Nfdc::onSuccess(const ControlParameters& commandSuccessResult, const std::string& message)
+{
+  std::cout << message << ": " << commandSuccessResult << std::endl;
+}
+
+void
+Nfdc::onError(uint32_t code, const std::string& error, const std::string& message)
+{
+  std::ostringstream os;
+  os << message << ": " << error << " (code: " << code << ")";
+  throw Error(os.str());
+}
+
+} // namespace nfdc
+
+int
+main(int argc, char** argv)
+{
+  ndn::Face face;
+  nfdc::Nfdc p(face);
+
+  p.m_programName = argv[0];
+
+  if (argc < 2) {
+    usage(p.m_programName);
+    return 0;
+  }
+
+  if (!strcmp(argv[1], "-h")) {
+    usage(p.m_programName);
+    return 0;
+  }
+
+  if (!strcmp(argv[1], "-V")) {
+    std::cout << NFD_VERSION_BUILD_STRING << std::endl;
+    return 0;
+  }
+
+  ::optind = 2; //start reading options from 2nd argument i.e. Command
+  int opt;
+  while ((opt = ::getopt(argc, argv, "ICc:e:o:")) != -1) {
+    switch (opt) {
+    case 'I':
+      p.m_flags =  p.m_flags & ~(nfdc::ROUTE_FLAG_CHILD_INHERIT);
+      break;
+
+    case 'C':
+      p.m_flags =  p.m_flags | nfdc::ROUTE_FLAG_CAPTURE;
+      break;
+
+    case 'c':
+      try {
+        p.m_cost = boost::lexical_cast<uint64_t>(::optarg);
+      }
+      catch (boost::bad_lexical_cast&) {
+        std::cerr << "Error: cost must be in unsigned integer format" << std::endl;
+        return 1;
+      }
+      break;
+
+    case 'e':
+      uint64_t expires;
+      try {
+        expires = boost::lexical_cast<uint64_t>(::optarg);
+      }
+      catch (boost::bad_lexical_cast&) {
+        std::cerr << "Error: expiration time must be in unsigned integer format" << std::endl;
+        return 1;
+      }
+      p.m_expires = ndn::time::milliseconds(expires);
+      break;
+
+    case 'o':
+      try {
+        p.m_origin = boost::lexical_cast<uint64_t>(::optarg);
+      }
+      catch (boost::bad_lexical_cast&) {
+        std::cerr << "Error: origin must be in unsigned integer format" << std::endl;
+        return 1;
+      }
+      break;
+
+    default:
+      usage(p.m_programName);
+      return 1;
+    }
+  }
+
+  if (argc == ::optind) {
+    usage(p.m_programName);
+    return 1;
+  }
+
+  try {
+    p.m_commandLineArguments = argv + ::optind;
+    p.m_nOptions = argc - ::optind;
+
+    //argv[1] points to the command, so pass it to the dispatch
+    bool isOk = p.dispatch(argv[1]);
+    if (!isOk) {
+      usage(p.m_programName);
+      return 1;
+    }
+    face.processEvents();
+  }
+  catch (const std::exception& e) {
+    std::cerr << "ERROR: " << e.what() << std::endl;
+    return 2;
+  }
+  return 0;
+}
diff --git a/NFD/tools/nfdc.hpp b/NFD/tools/nfdc.hpp
new file mode 100644
index 0000000..f8895b5
--- /dev/null
+++ b/NFD/tools/nfdc.hpp
@@ -0,0 +1,257 @@
+/* -*- 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_TOOLS_NFDC_HPP
+#define NFD_TOOLS_NFDC_HPP
+
+#include <ndn-cxx/face.hpp>
+#include <ndn-cxx/security/key-chain.hpp>
+#include <ndn-cxx/util/time.hpp>
+#include <ndn-cxx/management/nfd-controller.hpp>
+#include <ndn-cxx/util/face-uri.hpp>
+#include <memory>
+
+namespace nfdc {
+
+using namespace ndn::nfd;
+
+class Nfdc : boost::noncopyable
+{
+public:
+
+  static const ndn::time::milliseconds DEFAULT_EXPIRATION_PERIOD;
+  static const uint64_t DEFAULT_COST;
+
+  class Error : public std::runtime_error
+  {
+  public:
+    explicit
+    Error(const std::string& what)
+      : std::runtime_error(what)
+    {
+    }
+  };
+
+  class FaceIdFetcher
+  {
+  public:
+    typedef std::function<void(uint32_t)> SuccessCallback;
+    typedef std::function<void(const std::string&)> FailureCallback;
+
+    /** \brief obtain FaceId from input
+     *  \param face Reference to the Face that should be used to fetch data
+     *  \param controller Reference to the controller that should be used to sign the Interest
+     *  \param input User input, either FaceId or FaceUri
+     *  \param allowCreate Whether creating face is allowed
+     *  \param onSucceed Callback to be fired when faceId is obtained
+     *  \param onFail Callback to be fired when an error occurs
+     */
+    static void
+    start(ndn::Face& face,
+          Controller& controller,
+          const std::string& input,
+          bool allowCreate,
+          const SuccessCallback& onSucceed,
+          const FailureCallback& onFail);
+
+  private:
+    FaceIdFetcher(ndn::Face& face,
+                  Controller& controller,
+                  bool allowCreate,
+                  const SuccessCallback& onSucceed,
+                  const FailureCallback& onFail);
+
+    void
+    onQuerySuccess(const ndn::ConstBufferPtr& data,
+                   const ndn::util::FaceUri& canonicalUri);
+
+    void
+    onQueryFailure(uint32_t errorCode,
+                   const ndn::util::FaceUri& canonicalUri);
+
+    void
+    onCanonizeSuccess(const ndn::util::FaceUri& canonicalUri);
+
+    void
+    onCanonizeFailure(const std::string& reason);
+
+    void
+    startGetFaceId(const ndn::util::FaceUri& faceUri);
+
+    void
+    startFaceCreate(const ndn::util::FaceUri& canonicalUri);
+
+    void
+    onFaceCreateError(uint32_t code,
+                      const std::string& error,
+                      const std::string& message);
+
+    void
+    succeed(uint32_t faceId);
+
+    void
+    fail(const std::string& reason);
+
+  private:
+    ndn::Face& m_face;
+    Controller& m_controller;
+    bool m_allowCreate;
+    SuccessCallback m_onSucceed;
+    FailureCallback m_onFail;
+  };
+
+  explicit
+  Nfdc(ndn::Face& face);
+
+  ~Nfdc();
+
+  bool
+  dispatch(const std::string& cmd);
+
+  /**
+   * \brief Adds a nexthop to a FIB entry
+   *
+   * If the FIB entry does not exist, it is inserted automatically
+   *
+   * cmd format:
+   *  [-c cost]  name faceId|faceUri
+   *
+   */
+  void
+  fibAddNextHop();
+
+  /**
+   * \brief Removes a nexthop from an existing FIB entry
+   *
+   * If the last nexthop record in a FIB entry is removed, the FIB entry is also deleted
+   *
+   * cmd format:
+   *  name faceId
+   *
+   */
+  void
+  fibRemoveNextHop();
+
+  /**
+   * \brief Registers name to the given faceId or faceUri
+   *
+   * cmd format:
+   *  [-I] [-C] [-c cost] name faceId|faceUri
+   */
+  void
+  ribRegisterPrefix();
+
+  /**
+   * \brief Unregisters name from the given faceId/faceUri
+   *
+   * cmd format:
+   *  name faceId/faceUri
+   */
+  void
+  ribUnregisterPrefix();
+
+  /**
+   * \brief Creates new face
+   *
+   * This command allows creation of UDP unicast and TCP faces only
+   *
+   * cmd format:
+   *  uri
+   *
+   */
+  void
+  faceCreate();
+
+  /**
+   * \brief Destroys face
+   *
+   * cmd format:
+   *  faceId|faceUri
+   *
+   */
+  void
+  faceDestroy();
+
+  /**
+   * \brief Sets the strategy for a namespace
+   *
+   * cmd format:
+   *  name strategy
+   *
+   */
+  void
+  strategyChoiceSet();
+
+  /**
+   * \brief Unset the strategy for a namespace
+   *
+   * cmd format:
+   *  name strategy
+   *
+   */
+  void
+  strategyChoiceUnset();
+
+private:
+
+  void
+  onSuccess(const ControlParameters& commandSuccessResult,
+            const std::string& message);
+
+  void
+  onError(uint32_t code, const std::string& error, const std::string& message);
+
+  void
+  onCanonizeFailure(const std::string& reason);
+
+  void
+  startFaceCreate(const ndn::util::FaceUri& canonicalUri);
+
+  void
+  onObtainFaceIdFailure(const std::string& message);
+
+public:
+  const char* m_programName;
+
+  // command parameters without leading 'cmd' component
+  const char* const* m_commandLineArguments;
+  int m_nOptions;
+  uint64_t m_flags;
+  uint64_t m_cost;
+  uint64_t m_faceId;
+  uint64_t m_origin;
+  ndn::time::milliseconds m_expires;
+  std::string m_name;
+
+private:
+  ndn::KeyChain m_keyChain;
+  ndn::Face& m_face;
+  Controller m_controller;
+  boost::asio::io_service& m_ioService;
+};
+
+} // namespace nfdc
+
+#endif // NFD_TOOLS_NFDC_HPP
diff --git a/NFD/unit-tests.conf.sample b/NFD/unit-tests.conf.sample
new file mode 100644
index 0000000..d75c19c
--- /dev/null
+++ b/NFD/unit-tests.conf.sample
@@ -0,0 +1,31 @@
+log
+{
+  ; default_level specifies the logging level for modules
+  ; that are not explicitly named. All debugging levels
+  ; listed above the selected value are enabled.
+  ;
+  ; Valid values:
+  ;
+  ;  NONE ; no messages
+  ;  ERROR ; error messages
+  ;  WARN ; warning messages
+  ;  INFO ; informational messages (default)
+  ;  DEBUG ; debugging messages
+  ;  TRACE ; trace messages (most verbose)
+  ;  ALL ; all messages
+
+  ; default_level INFO
+
+  ; You may override default_level by assigning a logging level
+  ; to the desired module name. Module names can be found in two ways:
+  ;
+  ; Run:
+  ;   nfd --modules
+  ;
+  ; Or look for NFD_LOG_INIT(<module name>) statements in .cpp files
+  ;
+  ; Example module-level settings:
+  ;
+  ; FibManager DEBUG
+  ; Forwarder INFO
+}
\ No newline at end of file
diff --git a/NFD/version.hpp.in b/NFD/version.hpp.in
new file mode 100644
index 0000000..13027d4
--- /dev/null
+++ b/NFD/version.hpp.in
@@ -0,0 +1,74 @@
+/* -*- 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_VERSION_HPP
+#define NFD_VERSION_HPP
+
+namespace nfd {
+
+/** NFD version follows Semantic Versioning 2.0.0 specification
+ *  http://semver.org/
+ */
+
+// To change version number, modify VERSION variable in top-level wscript.
+
+/** \brief NFD version represented as an integer
+ *
+ *  MAJOR*1000000 + MINOR*1000 + PATCH
+ */
+#define NFD_VERSION @VERSION@
+
+/** \brief NFD version represented as a string
+ *
+ *  MAJOR.MINOR.PATCH
+ */
+#define NFD_VERSION_STRING "@VERSION_STRING@"
+
+/** \brief NFD version string, including git commit information, if NFD is build from
+ *         specific git commit
+ *
+ * NFD_VERSION_BUILD_STRING is obtained using the following command (`NFD-` prefix is
+ * afterwards removed):
+ *
+ *    `git describe --match 'NFD-*'`
+ *
+ * When NFD is built not from git, NFD_VERSION_BUILD_STRING equals NFD_VERSION_STRING
+ *
+ * MAJOR.MINOR.PATCH(-release-candidate-tag)(-(number-of-commits-since-tag)-COMMIT-HASH)
+ *
+ * Example, 0.1.0-rc1-1-g5c86570
+ */
+#define NFD_VERSION_BUILD_STRING "@VERSION_BUILD@"
+
+/// MAJOR version
+#define NFD_VERSION_MAJOR @VERSION_MAJOR@
+/// MINOR version
+#define NFD_VERSION_MINOR @VERSION_MINOR@
+/// PATCH version
+#define NFD_VERSION_PATCH @VERSION_PATCH@
+
+} // namespace nfd
+
+#endif // NFD_VERSION_HPP
diff --git a/NFD/waf b/NFD/waf
new file mode 100755
index 0000000..ef9df38
--- /dev/null
+++ b/NFD/waf
@@ -0,0 +1,168 @@
+#!/usr/bin/env python
+# encoding: ISO8859-1
+# Thomas Nagy, 2005-2014
+
+"""
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+
+3. The name of the author may not be used to endorse or promote products
+   derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+"""
+
+import os, sys, inspect
+
+VERSION="1.8.2"
+REVISION="a01aa13963437a19e5d8748cf2afd71b"
+INSTALL=''
+C1='#/'
+C2='#.'
+C3='#)'
+cwd = os.getcwd()
+join = os.path.join
+
+
+WAF='waf'
+def b(x):
+	return x
+if sys.hexversion>0x300000f:
+	WAF='waf3'
+	def b(x):
+		return x.encode()
+
+def err(m):
+	print(('\033[91mError: %s\033[0m' % m))
+	sys.exit(1)
+
+def unpack_wafdir(dir, src):
+	f = open(src,'rb')
+	c = 'corrupt archive (%d)'
+	while 1:
+		line = f.readline()
+		if not line: err('run waf-light from a folder containing waflib')
+		if line == b('#==>\n'):
+			txt = f.readline()
+			if not txt: err(c % 1)
+			if f.readline() != b('#<==\n'): err(c % 2)
+			break
+	if not txt: err(c % 3)
+	txt = txt[1:-1].replace(b(C1), b('\n')).replace(b(C2), b('\r')).replace(b(C3), b('\x00'))
+
+	import shutil, tarfile
+	try: shutil.rmtree(dir)
+	except OSError: pass
+	try:
+		for x in ('Tools', 'extras'):
+			os.makedirs(join(dir, 'waflib', x))
+	except OSError:
+		err("Cannot unpack waf lib into %s\nMove waf in a writable directory" % dir)
+
+	os.chdir(dir)
+	tmp = 't.bz2'
+	t = open(tmp,'wb')
+	try: t.write(txt)
+	finally: t.close()
+
+	try:
+		t = tarfile.open(tmp)
+	except:
+		try:
+			os.system('bunzip2 t.bz2')
+			t = tarfile.open('t')
+			tmp = 't'
+		except:
+			os.chdir(cwd)
+			try: shutil.rmtree(dir)
+			except OSError: pass
+			err("Waf cannot be unpacked, check that bzip2 support is present")
+
+	try:
+		for x in t: t.extract(x)
+	finally:
+		t.close()
+
+	for x in ('Tools', 'extras'):
+		os.chmod(join('waflib',x), 493)
+
+	if sys.hexversion<0x300000f:
+		sys.path = [join(dir, 'waflib')] + sys.path
+		import fixpy2
+		fixpy2.fixdir(dir)
+
+	os.remove(tmp)
+	os.chdir(cwd)
+
+	try: dir = unicode(dir, 'mbcs')
+	except: pass
+	try:
+		from ctypes import windll
+		windll.kernel32.SetFileAttributesW(dir, 2)
+	except:
+		pass
+
+def test(dir):
+	try:
+		os.stat(join(dir, 'waflib'))
+		return os.path.abspath(dir)
+	except OSError:
+		pass
+
+def find_lib():
+	src = os.path.abspath(inspect.getfile(inspect.getmodule(err)))
+	base, name = os.path.split(src)
+
+	#devs use $WAFDIR
+	w=test(os.environ.get('WAFDIR', ''))
+	if w: return w
+
+	#waf-light
+	if name.endswith('waf-light'):
+		w = test(base)
+		if w: return w
+		err('waf-light requires waflib -> export WAFDIR=/folder')
+
+	dirname = '%s-%s-%s' % (WAF, VERSION, REVISION)
+	for i in (INSTALL,'/usr','/usr/local','/opt'):
+		w = test(i + '/lib/' + dirname)
+		if w: return w
+
+	#waf-local
+	dir = join(base, (sys.platform != 'win32' and '.' or '') + dirname)
+	w = test(dir)
+	if w: return w
+
+	#unpack
+	unpack_wafdir(dir, src)
+	return dir
+
+wafdir = find_lib()
+sys.path.insert(0, wafdir)
+
+if __name__ == '__main__':
+
+	from waflib import Scripting
+	Scripting.waf_entry_point(cwd, VERSION, wafdir)
+
+#==>
+#BZh91AY&SYÉLt"ïÿÿÿ¼@ÿÿÿÿÿÿÿÿÿÿÿ†¢¦x€#)!2„Xaé\÷x¬#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)/ºúÕ=[Ý{{žÒá¯Z»må˳]9y›o3ÏZÝ»½õ÷SÒû5Km»¶÷מ£ß4àÖL՗sª_^/^Ü&.ëm˜}+Üe#):z{·¡íªì÷ºìUg­•:vÚi>{׍˜×ÍÞçÁï>jWlúûí÷Ɔ»·}Ýw½öpôYÛÆÞÚîÇ>ï¯m¼Øv,¶uÈ#)#)#.#)#)yì#)ƒÉl€#È(MÛÞœÀYÆ`Ë'F]Ûg¹î¡ ¶ƒ\ê¶#.Bí©¡I#)×Cvf(’ªR]íÒOcAT””•QZÉI#.7 mƒËmªéíÜ<z÷Ž½[vÎìê¬d¶hÒMyU,j¦zS/ݽ{wß};}ÛºÜ{|ûŸ^ÖY}k¼íŽó7Zö³Nì»[Vónßb¾÷s­¯WÛ¶hä#)R¦Æ¨#/¢‚{$Q‡•›™Ü»¯3ƀ#)¡{w³%¶d‰k{Û½Ûp $UPÞg»š][i­Ž™½º'¹ÚíÎÞðÉÞ·‡[½n÷ÝæÛ·ÎßYUH’:lVÖíԚ˜×mòÝom;gps³:ÉßõOkl®õãxß{Ü÷ÚµÞÞõXZcʞÜצ¼J@#/U&ûÞ<™5õöó½=P®“۽Îo³¾òv}{Öw\ç{ÛÞíu=鯽ï5>÷w³×¾ÖiÛ{º­gº»Þì}ëÜoJíâ®yžíÅv;·‘ëÛ6÷¼jöª´¥¼u¶Îí¾ª•B’n7nï}Ís³ïžï]{çÝÙïI^ÞÎ>ÙãåÛ4¸·Ý£]´wvÙ]˞î6½ç]½wÓ0zOžó{ÌöoƒÛƒtÜӀP¡P ”Øwf²i»rŽ¶#/¹u´œçFæù=öò<º—Uó½ç•ÛÒ¥8­¬Á¥—¶ÆJ¾,¶Þ|sß4PSß\èT#)‡ÞÍîå}ÝÞw¾>ûÛ\àxï¾îñ{D½Ø[®ºÓ›ïmµowbn£#/6Ï^—»µÙÉ«É½¾ïuWÑ_{®ó>öñ2}ëáô€Õëï®ïW¯Q×»Ð=öo©_}»°zµ»j[}híîç½÷}öíÝ.·ºÜ÷[Çw®u·´ßl6µçw9á{[NOrÙµäﶪÒ6önæPO¬Ó•,ë}x>&ƒ	ÓCê¼5[Œ´ØÕ÷lóž{ÞÁð¢@iñ#.:ù¦}s{¸>ÊvçNÞös›Ó›Å[Ÿqœ«ßmn÷w½ÙõõióÎ7½'ßX}÷»A·\¥Ë)¥¢ûío}bù÷T‰õ½€ń)õ¡î¶Èè)¥³%Ú³Þó»°6Nîwž¬i¼û[ï¼à2BÍT+g!÷°ãåӇ·¬vîw4=.s›¥soºYÝÖÝé½´(ªH½vâv‹·*Ù÷›šìçÓݬ«¯s{¬®ûÙïww¯¾õÞ;_ëßN¥_z·fçÞÚ÷n©Nœ;ÞÝäS½ŠÓªÕé^¾Þ2Ýv{ÞíÉ÷šûìKQm\¯#½zÝä݃êø´%4B#) #)#) É£@#)&L&¦ž©£O(PÓMSÐÔšši)'¦5£M©é=!§¨hz¡ê#)#)#)#)#)	ˆ‚#)"b2#jTü§µSÍҙêi”zFÔòS ¨#)È€#)#)Iꔒ	ªj~Tª=¥6§¦¡§©£Ò#)#)#)#)#)=@#)#)#)‘#) ¦šM#)M`M&OLžQ²ž“iP#)#)#)&¢ €€#@&™4 éSõOh¦ÔeêÕzž¦›Ò›P#)#)#)Ð#)#)û|Ûú¶ÛWtI?³’wV5sZÍßom©»%‰£á¶Ôå)) ŠZ$‹‚¢#){V@åöüòÎX?æì¿eB?8ý0LØ㩇š.$…5dÆ•ˆƒŒÅ3d‚oç÷ŸAÿ´“ŸRù¤ :‚]XA#)»/¾_݄sE¾M̺Ôq·ª¥ŒŽÒ8æœÈó+¹IŽ0zË|-8Þ]Y‰ÔÁt>±q/eBœJµJ”ÄâqQa³[à’;Æöu8@!ÿÄð‹¿äÈx±!4…(ÒDa†*„¨¥"CJˆÌJƒ@Ä"›*„ˆ® ‰ÞI#)iD(ÞE(#¢UH€û! °¤ª8šÂ•¤#/AÔ  )æUÛ%€P`ÌJC3LH„È…Í£fh‰)©±‘2RŒÅ-¤¨je,P4$&©Q´”DJ™¢l¡l–JÌ ‹L)A±¤Œ›hÅI)iª$%–’5$‚`Ê"F¶“T,šliblŒPRJ)¤HÄÓ6-SU¦ª¥$ÖXŒ“$j6‚š¥«™³4¦©,™fª%M±1³T’T†™’I5̍HÍhԄlšf‚‰(Ñd´Z)J¤†lb`…‚‰šLM6(`›)°L ‘f*d™ˆ“%²@‘KŠÉdI„)%%6)2AQ)Iµ	j1¨lŠ†,L(¢”%iDÆ,LTl™M¢’e¤‘4²4‘’T¢Šf¤*iM†$Â@¦ÄjK#.›Dl‚‘&**"#/ɐ¦l`’MIE$dBDÌˆÍ I™31’³)¡,b$"HJ“a	I4ÒÐbLš,¥0lÚ#M"¢¤€J3A¤I›RÍ,¥”†“K$Ù¦Q 5,RY‘±£+$ÌI"ŠÀ3c,¨’È*#/Yš1´YCd£2›4IKZl„Êc$jL”™…2RÍF’Ê•L ¢“QdA%() FHi‘¤H4–5¤Z¬„34‹&aJB›I²E²E£I2I(J,”4`"“,E#.‘6k1“RXÐJl2™¥1	#.…¦MRÆ,ÔؤÊ,ÒbJÙ$…”T‘bSLh Å&M)&a²lš2d*#)„k$ÊÄÃh,›%!K&QŠi ÉL¤eˆÁi)TÙ¬mˆL‚dJHŠÑ±¤™¬ŠLhÂHaQ¤2@KÑ´jR1EbKI‘™¢“	,ˆ”!ŠY#)Z4™eFi#.›U-m ¤ª(É© k%ÊHÚ)’™&1¬–‹4HÓLÓM’”¢ÔÈÆͱ(Щ²ÊFh¡K_‚­Ñ²VÈK(ÉMb֊Ţ£BfTDÓl–ƒTÆ¥ªb(lXŒ4¤IÌld1¢¡–¡5E‘MdÈle•E¡bdÌjZYŒ¨°ÌÊj³F™Y©¥°Ö4ÙD–Te*±MZM¤+6V³RRÃm1B™b´i©Q%¦QDe¢±¶f²UfjV*J6ØÚ6%CDB¨¤*#.±ª5ԆÖ!5’E´Ñ¥QH&ˆÃ5(”Ú•F£d“hƤ,jšLÚµ›Vš±ašÆË"eMdI–šaI3j¥¨¦ÌÔÒ¦kJ¥d­J˜ÛJ™JM©¦ÔÙlR#.lÖfd’²ÅÁ©-¥´²ZšFÄ@`i±¤£H„Q°D1mÉ0)µRD¢Å2šÆғMŠŒ’h4”Â…ŠeDX¤–cBi¢™)“ÊlÐƉ	Cbř!ŒR³RXm™dË,Ì!#/Rɍƒ	±QFÆĔ”dh4‹2‹S1³¤¬i„̓FM‹lËP)¢„±DE")´d,͢ōŠ4"¢4––H¦`´)e ÂH“šJFS`ÉlB’Ò$“f¥š¨Ò©™‹#.Š”Òl¤Ê,ÑV(Ì(’‰I$ÙI4•%£)3H†•5–lme6V*16)4ԓ,É¢¦[R2e-%%²$”K1 Ó(ÑD#.e“d¥LDԐ‘h¬FØM‹EFŠ6ÄVšL2LÑ%&‘h¦#)6)"±`Û-4’T†£Y3#.%,Èl…hÑ°†Ñ¨­“F„Ë#/ÐDEd¶*c()5JQ5cXÊfhB¢¤“i…ID–ÑY¶©‹2j,Q‰+%¡¥4±¥%4IRÉ*JR”£VÄ¥"‹%+e%²i*,m¶f)”l¦²¨i&l‘I±aB"ŋLÉC4`ÚL‰L±"Õ(0™Kf"”j6¢ØªHB¡’c@FÊj£i’X©#)ÐbŠL"MZPQiMR›IQ[ÆÖf+%¬È#/Ñlš’‹Å(#4cf„I	’˜4`ͱ‹hØ¥6ÚJh‚±€É¨¨­6Ë-FÑX±kI¶¥³d4²±*ŒZ)–*¦¤Ò²Œ£E(–LYR¢ÒVM¤Û%dՊM¥3(²mEIF‹F¶Û5­4f¡²X‰*eEF° Z#.&¥³VLS*ÑDUŠÑ¶Å¢ÛhÚ¡–Mkjš••DÙH6’cÊÂÊQdšTEÌlII”“"!jMb̵L|>ó~i¯éyÛÇð‘{ßÍ\«E½fý;i6N£#ý¿òþÞS´W	d]j¢$LLY¢ÒE˜pÿ«ýT|#.¥)°T\AȟúgQ”lÿ©ö+Û=i÷é8ÈnT-ƒ²Q¥U¥uI¦éDn™#/·ÿ5_Ͼ!kÒîƒÿFÃû>û®ÃüëI2h•ÌäDL³¶»Yb"<ŽÂÂ	#.¶nà]uø^Ì?v§;q0ž¶ã?¨…NéÇX;£QLC§6íˆéTf¥5Fª2Ù$”¤©·_Bðjck´ðb­¡Ø£Â¢5Š UwCkJÉTe0ÀÙ¤ŽJ’5äØ2¨…*(Ä´ÊÍh¥P5W³Ñü½Jô·³ÍÍFÉk1Q$âQb-ÀFiw.Š‹u@(þó«ÝçlËÙ¹I¼ëÅ4â" ÿïCýª0+1‡j¢Ağ6.ZÑ1íÃxÑ#.-WÇýº4=þÂv#/…•µÚ*6P…²P€§†t+üs’ãLãPºˆÑˆí}[ï ÿi<;ð:N¤¥!V&CS‰MõlJ=i¦Žäâ+kqeœÿ7ñgH\Zšp»PD(aB1¼Üó®ît]Ɗæå&¤ÊDUCBWœŽÂyChJ;sѧÓ	¡ì•(*H#(k‡=?#/\‡nÔØ28¸o}up#§²®ó@’ñkñÛ^éé{š®¥·³PhmØ£Ö21ðùÞޖH‚ê4|:EZ9`ˆÜZ©R²ä 7n\“w]‡uÓEA½ê·žw÷/š«H(¡ä•š%€‰³[5¸ND”Å~uKꢱü+LySÿ5é‚eƒÿB©6J®YÆ*%¡x¨ÈI+ù1Ï³ iuÔÎ%ɅV’QøUkÛrú#/ç÷EÏèñé™ìâ£Ær2?f_C<ᎲB8ãzçÿRƒ¹5NÆ¡ßƗdÏ>5~ĝ±qC¡.çíí}ËP˜Îo¢÷Ìå!³P‘ö`rÆw¸C:¥ÓÖèÛAÙÙW0´d;Ò×¢ÆÑX»0¬MÖ/R„PX%ç¥ãèü1F‰*'Mh³f Ø҃Æ絗S+áîß¿Zě#.KHíÿ—!¾ŽëØc$4Ù&27åúí221ÀÍì¢aÍ×e/)Qì´½ÅFv“¤8ÆäÈÈY[i¯ÙҔcB'ë!î.êm+Ç3,›Ås]He ¡»¬ªJ]§<0=–¨™ÿ³wîÄê71)¬‡$LÀÅ¢šÑ`Æ#	Rzrå}_?·ª¶±¡[ByÖ	’'ˆ…	-%$"˜K<P4€«µËcIÌŽ2ø3\σ%²aj#/Î,³$©±6„ؘÀî5Œ#CÅ TX]Q4£¶ôõ挃þ‘¢è)€,šùPö͏åD咡„DJÚ͛éjõ(µïï´ò76êo„º‘‚…4£†Üîœq¤Þ¨.´BÒ$¸àPj‹{)˜²#¿¦kJlÍFd@îª-¥Q4í»¿‡—,ªJgoeYRLHÀ]ë²áͺ$cb}¤e|¦x<C%¨÷b#.²µ¶–œe‘L„5|HÙæᶺk#/ÓÙÖDVxvT<Y€Q⁵ôáF$Nú¦ûª_…Qš‘Ñ}ÎS3–»{ÚG½W'²¬äé•Z …WµÍòÂ?ÁV‹S:Ð¸í»™áW'h#.l̂MˆZÆÕåPý¤óژ†‘¦Ã¬!açž7?Òú_}ÓGúzô¾m.¨l8ã]Y„?ÇÒ¹3L‚4‚ÃÉ XŸm½§"úr‡±5¸uì‘FÀ*”Ð!¡ÈiZ¬m؍\¤<ad¶AëÙÙY.ÞðÁx#/åÄÈq0”ÝM6á¯GʏŸCay¯õ'h	L~²XG=ñö«>^3ßa·ÊÌT;ï’Lq»óÆV1^75&}U4B?->Ó36T	¿sžª#!slÏdª¤?¦Bü†”<ªô©‡«(uÉ=Rò‡x6Ÿ£WÍüIFY¨è_Ún‘õÐh¸ùyϳwkŽÇ'~jKOWáÇY/(yå…ÂHÎl4c™xf|%£>ïÎìË[mƒâg …ª¦nìeÕDî©H/ÃÝVûÚ¾6Ý$µEX¹ñycüõÎ`æÕ.9=}T…̝¡LÆR–…Ö—š•¿…ú´£GÐp\¥dFz}yp2f({¥Oì8kÏ|zm¼!ý¸iŠÒ#.4ÞþüY‡4P¹ãs]#/<oµ—Á*>ík¯0ãUƒÂ¶&#/j•b<}–L¹yò©¨È ‡´ã\\¢;mã‰Û3FSj´Bþÿ׉ý92±|pÆ8¡KŠj¹æ˝õwŖù´‘IGAAH¬ŸÂäûÔëPsçFÔú?Ó¡ÑÙÑc Óó†:ߔ(õ‘&#.êë&s!檟¨MFT¹¥M϶*¢u!;®¡Â>xÃIj²<n+t6=ݎè±	0aZho/3S%»Îø¶PýÓ0ðJmb¢iSvÁwnÄ4ønîþû™}¯‹H՟ŽE×éŸvCf8&gKU߆tÍúãdÔ¤æHÄF‹Üñy½:#ÌËâÝë»by±Á…"nÞ4Aoå.^&z´Ñi¦4?eÿ†)³ü•P†Ç—P(u’Å#.ÄÑòµXÜöTWX­¯«_2ÏÉÿ)có{	!Zè\G”7®pckçká³+|ÓgÞ«$lõž#.Ù¸¸jÉÄ<Ó†›kªbð{5÷E|¡¶:ñÖðÁµ‹œù£2˜ÈÑù:qøQ’ʦ	QШ|¸[:8txSíÌU5ú:ô«ÙýJ,å¥y÷ G›z°ü$lÏv»àiÕ ¦$EüÛá‹O¾¦÷QbêÀA‚€˜¤nI q1ðˆ=éèü§Þ(T:pIG<}/\±5áÖÔûxãòoi_¤Ãæ{÷¹©F0lÅŸÖ­1ÓuDÏëe5iUB‚5âÑkٜl8ÉQýÕ{玐ÌtÅ<­6Ðg‹#<gZ·HßyùJØtéjA;œ3bìÄÀŠ(‹) ^‰Êª0ófp4(ǹ<w/¹‡w||¥;¸Æ.e¿Ã®ÎÞÜÄ̜r²í£’‘¦6mꑱ!Âò…©Æ»é˜RAv¦”ÿ½g…ií©<— æ‡iaR˜òXDHw!ÿróÖ"ˆÉº<ُ/ázwÉ;˜oÂ)àò“Óï¢Ã…æ}5¬Ò¥YQaàG£PEbY‘Ž‹¿yê?¦2…q<Á÷$¥ûç[¦t@"xÑ#)x±ü–HªšÊ„ßL¿¨Â®·+­o~AlÔlŸƒ¾œžü6þRdSÕûEˑü%§†äÊ4$!£y¨¤g ìz	ÿì¹zowtK™¹	ÃR(Î:EÚéŽú\*;ÕI'˯,ÓS4!7#‘•Ï6b®˜Ž,Áf®xlU%jòkғSÂéݾ#.sW¥z©" ¦–Ì<tsÒâ䣖ò½¡ÒúÂ6=4A›‘}_QÝFï¿ޘxÓWtã6L­'•&çU‘ñö2Ü÷Ìz£(ÝÁ'\£7˜g^¯öÍÐW`s^ï#/™ƒíýQvúúÀÆpóÆA«åýEêªù~[«=^áÕneø¾aÚÔ.?#×}$çÛ~Œ „aJkH§®^”‹äᅴ±øשÀìès»¿¼ê¼Š«=?}U½C™‡dòDöTÓêåGŽÄY	ç£TŽ~{¨]Úk¿ Z‘8¿tµ¯N‡2[ªº4nzž^œoß­ÿbmÙ¨&i£lÜñ©³oÜ Å÷u3™ýÃfèóéA@ÕòJQ!«»Ä8e6'Íà¬BPzvþîß®œXãÊO@ÇDÐÁÇ8×ñ! +j‡>ÞÞºv2xñÁd”‡&Cçì°³#.$7ùÔÖôzÖ³ã9˜™Z¹#/ë;ëLG‹Á½g&B6È[)ÒÄú¸úïç­i™"zös lÍ"8ÈÚîglÁ®={“r¬øHÐ<ˆ×ç¦#«=…#â8mÓDDm´›¶Òùõ¨¸#.“öð³··¦}š:Rn`P°Œþ®Mh:ˆ¸uÞà§mÀ“~#/cõkúš÷=cåïÿ	,s5¥TÔV¯†3Œ˜Å#3Ò±š*(âŠ&zQqdUAL¡ª’šéüjϣݤ â†`¨Áé„ÉՍ§vö=Œ&·«}	1wdŸ¿[&­\2¦/Íéš1È߶„Ï#/uµÚÜ#bÓGáôÂÉTŒS‘ææzPC¹ÿ#)ìUoü5úñƒ…½¹®\¨vÑÚ%ËPöùÑG„ªQÄب%–û“9׆Ä´¥ö­™ÊçãˆMbü“Ù$6Ñ(üiÝ!7	OZü¡î<ÊÌÖÖV(bVõB‰øRÊùm~uÂò?	ÏW5¨ÖOVcŽµwÙçQY€Æšlck ÍƒðËWÜçl¿§"½ofÚE`ٛf-±dÉGåj}½U&<*‚YÁ¯šQiJݝRx7=Ž×†>6¼Ýx4 À¶#/å5âçM?„~äß$Þ#˜ÔÁÉTãÇ.}T¯'rÅ8¸ÃAôx@„šPè¸s®wábìm	š(¶À˜B#0[²óMÌ&0]ÄCµ£¡G£Í՘ªœ†ÆFŒ>ä5ß3Qžýyã#/³¢vT¡›ÕTڍÒcùeld/g²Êba¢“vœbYíᙄAöí#/öpyÜm,r2óÝTúĵ¬V¹¬m›ÕË?XfÝÙ4ÃT†Ú#.}ù)qSA}ZQ›ŸZï§éÂYì‰|ùÀë'®ÈF>öJz5z÷[R‚°*¨¶]Ê>§=kX¢hŒÐè²V-<M`T¢i¦¤ :5™ ÓEŠðÅe	™©(R¨”(¢7ŠWRõn©*JR¯<\®FV–€i¢ ›`%­1ª²Ëúû»=1£¤”)ôw|ü +îNjµÑuÂIyÓ¤’Ý„•$›«Vßu–U f%fv‹™!Ë¥ºï»íffY™‚Ý4¼í$awÄø³zM”œq3IìŽßöü•ÿ4I·[œÖ»5Ñùj×ãÓ­».ß:§°ï‡æm_{®hš¤‡µ…±ŽÉ¿ÆVE½¹Š’<íÔÉ›ÝL1¤Î¬½Ùhm[¶#/9º)l¤J¢j…Ý$nMD»dYdoM£_”=ßîŽvæ#)x~ÝX«WY!8ÊÞ¶úWZƒ)—	ñ˶ÓoRõè“EOY›Ï$‰àóR£Žšü\_Ü¿®WüWê¤:?ªZ…ê6Lñ±£Ï¬hƵ;4Lœœ÷á-•?¢„sל*r¾ªÓTbuúëÔ¬¿/1Â|Ý&Ëgí¤(“ÆG#.ßfŽ}›Mê»%®\Ü×G«3·ÌU¾¬;g«û¹ƒQú·©uôïvŸÚ¿Ó¹Ü´+×¼4&á4SöNºS|Y>	Í»–¸‹†«#ÛõÌЀ§t‚»Í©6»hõuÊ->§Çßø½ÛÇoõ”öâë̽Z‰©{ˆEöÃ^³äÈÒm%m¢¢x(6˜ßH†»k@\ŠöK”É+uý« K<¹·PùYÃöã<çœã¶Ìô\0úT嚓)L†ïW֚°ßJø5­e… 3ävi't:«Êû^w¡¨‘fo~÷ŸZ(›4Ÿ²áž+ጶ#)²9eKuoÛ[z³-™uð($!ݵ¨?J°ï撍Iy)­Kyê!w6¿AeR(;Cój®G#/d–Ò_LY+Óñß\¤›„;+Øbï¢}eYؘñ>Zø֓@¥qFZHœ×‘ä م‡ˆ‘K‹Ú µŒ9z{Ž‘ÊТø—#/M'cxgcBªAgÆƒ'Þ×Y¦wå^(’#.ÆCäã#.P˜k#.•Î,$ŸÛ]Ðìæ­±ŽŒž;4閞œ^jÙ¿Zœr¶0‘luÎ¥²t5ô<VúÐК§ér$³–ª»øèÝKÝú4G7>­„õ¥ÑË7Ä̞¼Lß	=·¹ÂuWE²Uï¿Kݶxðۇ_£)ùÿ#/€ùÕBê8rþO€úx$ìéœuR¡G‚K:<HU#ƒö,µEó_Ù®Dܚìc;/D³í„ªÒç£ð(ÿŸéY{ºlÝëåҏ‹|$¤0}1C‡úpóû¾šQâ’'÷ᘻKŠB­k :H#.C›«ûüÜûíêæJQ­µRöàùÞsâ½Lgò_m»I8ôÂë.®œWl˜Fçù<ZôÆWæO·ûc®7øUaŸP_2ô¤ÊòƒSTZG.æxD|Λó(‹vòî:ë¤Å"ðöC®ïóóxíœfºiÛ馚ì¬[M…	Ÿ5_„Yiۖ§ÀèÉ«9L|³ŠÌ6,KϺº´”þõ÷a Ôó2I^´Ãf-ͪ«E%Š…#/¹<w;§™·EæŸ]ÆÝ/‚µÃ;gX²$…Mðbrzc©Ô~ÒÇ´°ƒš•ÒÓF­vÈÀ×[’Wv+6œ­Ò¬É_§Zìwt̹Œ“UT¬°‰ÆnüÜo%¢§Ô0›²nqݺU0µ·µòU*⭄¥–×\u¢•ðˆ$ŠþšàÇ#š²Iû¶Õ¢tK\4ûËoyXo:<ìԔÍùý{³ñG2µ›“\t¥§G˜ÿ *Ý'×þå6qÍJBa2–m¬c5ÅæGÉªÇUÐÅɸ»Èïü‹­ûÄb¹º1Á—.¼9ٙ‚ï{30]ØÄ‘Øl|>é‚L	0‘I±¯ü®ÓÖÌNÖ«¤¨üþKwYkS²õsèÞ`u"I¥Èƒ¸6ÓËÒl×meQËDvzõz}_U§Wfãh3‰Ïs»÷}“=É´®Eh‘l‹D ±3¡ËFCŸc£–Ø|Žb¨háÇ¥UL*ÊÓakÍ[3ö]u¨ŸŽvÞÒ`҆Urn!ªLÀßóýõÅÏù^ë®(4h`tÀÈsR>{e;œ«Îª`1ˆíTj„ÐEŠ =¯íA!ŸŽõtšVùp†qWÛ*ÔÎÞSµ†iWùù\•÷¨HU{®áÏÇJ¤gÜ_É3)$⮯š½™èJ½›ëÕ*úAËërˆb¤‹«p EáѸüLHíð×y´'~{šc;um˜»D®œMŽßø;·“ˆÛTËu±Fìꉵ—y	xcnßM=4²•]dóù#)ÿm<ä&Bmv6ˆz»óõ9mA…v^®rjQêCÎ	¸Þþ˜éâMµYPö4?…ãu‰šÒwæÕÆRV7\ÿWÅ<m›¿Å^Ï»Æe{­ÇäòàÆê³TÙk]”³^2yþ‡$”ÿOôÛ#/í}?íiÓ¸—û.֍yxÊæìý^n©dƒT]1­³Eù•è[•&_Ǫ¬6(hrBƒäxÙ¡óšzñÄ0HÍþßw=³ª£$2HǓæ?È¢ö›Ë®Ò™¿æƒçŸFt¶fÍ2²eCŠ¤ï“¨UªGû_†l:›—¡nî•÷ÛôVÝ»áˆBq0‘{‚zÔaLt¹Se¿Ãøçé,ÔoT¾Ï óÞWl^”ÎԒ@cRÜjKÏdê‹MmýHvhíÇû}M[5`ÂôçښŽ»#>^lÎðÖû™fo	#)Ü«·»_ÄñðöÀfW1¦!´-8éÏHþg¨25|b”tx;²ï9¥?§Ö3ðëaÙ4$u^0x>ÐhlŒËãìgï¹i-¶]¹7å՜xü{þ§‹H@û|)_.4´Œ'?â…2kÖÃ?=Nñƒ~96àh­ê4ÆúESƒõ]5ϞžÞ.Ò=ñþþèá±}Z¬3;Šºb~‡œ[}íÝ>P™ž4?EÄ[c&´”9÷;Q#¦çÒò{iËóý¶qn,.ñ€•'º#/¥D0ÿ¯’#/j{äW¸za«IÅÃÔj-;–xÇ40öýùÅj4Ð-DZp¶[êÁ~šêÑyE9z¦ótÕv­+ÅV¾×úrÿH,ÙÉÌ.Ÿ¦F4Ï¡o²ºm4Ê#.؝°­MÚóq]sû,•Ccèumîäg#âU,z¢é†ü¹‰é®ÿcºk\.L!#„šú~˜#.mþK楽“cú”šv]îA¤u&#)íùMžÿáño§u¥Ðê5)hXÙÖ#)bE#ÌuLœíŸeDš÷”"m„ÎÑoCMKš¢¬k!˜SÇ0öèê}Îef»¨%sÐß;ÀAÁ(Q^y¼=ÝÂo²ºjË'a²˜aØ–÷<wˆœÑÆìzbºFõaöïXΰäC¿v¤Øè$LˆLÀâÏÀ#.U•ÑnúaÄՖ´’„a‘ÔZž¬½åšHD—Ë»ûÞÕ¾þvÝ3}Z™„&6é<74©êUܦÓÀ×DÒn‡!•Y¹"i@IêØsá‰÷¼õ’5ç·nniݓÙÚ¡ÙðÎ7¢‰¥æX-‰$KY>sòûÍk܈–†m‚'¢‹”ë­ª4ÄjÐX–΍o‹‚¬Å)#/Åf3#/-€Æ¤q1Û4ZÍ6´Ò'N÷ø^¦qðºüü%‚D!,wÍP„%‘£`°ùšã8o°ß§QƆÿ·ûd=ûä$ÜvÜQ`’ß>Ü\#. Îb§û^Y;I¤cé†8㚰ù#.BÔºË;üœíØfÌ°D„Ì 2Tˆ!3÷·ZÓéyÔ0òÜëßԅ'’gÏê^õÛâ|bôvéCòîZ)63ðԒ¿Wöt3ÆÚí§×ü±ƒqé­K¥.`M¹ØEý?:ØL+´s¦ò8êlLhÏZ‹”¦],ÓԚTq~Ñ»bçÎâÔóåÒ+#.Œæz¶F-ڟåâæoÝxì`ÌõM„4?%Â4ªaœB<Æòé£×ÏԗÄÜüšS§“°=,óÈß·6åÂwÏ]ôbôÍ{oN	ŒMc:¶be¦áÈ#.pöÌ(éäkÏÅ4j 4™9³/;D·„Z$:ÈpR)Â2¨ØӓRÂI98h‡¹·#\åêÄz£	¥U—b¥çÙô¹Tþ_?±ˆ~Û=®’®Ó½tccÍÈHâ*½ÁÑÜۂ½º	Úë#J»‹)™ÂVäkqZ×±†;ïÎæÓ${ôtCúu‚I©Ôgøêî˜Ü·×íº8˜st€1øÿœ¤­Ì›5óÞu|„=d™ëEr•{^…¹r{o¨!ßTÉÇ_UÆÙ7–(®‘	‡þT¢\“¾Éɤ³ԆŠnöòޗL0‰Ý5ÝWÈÓF‡vì9nTX«Åðñôùî–tk_~·Ÿ—ۜ=Ú|8ñCCCšJB(±«ÆÚ¤ÀxŠ`ÆŒL>:òÀÍmImª(¸$]JÜËy.†a<¹òÎ+#.ß;-‰%Ú$ÒîqC#/ÔQ‘êRrIþÿåElÀþ"²QùaÀäº#…‡oš"1ë$‹­†kƒÖØEÊb¯Š=c»cƒ²êýÙªÊÎZ<#/¸‰´“h‘n­`»É«ª#/wCYqˆ¢y™†óӎóFKn9ˆ†¦µJ:¸ج'O´c\C¦L릉7ñ‹ü{dž¢ùó“œ$Qߏî#/yöõ4Qµ§fv×Tÿë[ã¿®´5q}y¾ÛÀfHÍzø¿Ç.÷÷M$t#/wfqø3ñ‰¡Ø¸":à1QÁ‚ÖmhÌj|o†6°e†Öh:YšÐ˜¾)ªÐжªq’L™	oZc'0'cÔ¤êƒ% ØÌ ?†1QëU	MC¤Ó‚¢KE	ÎUî±ÓL¬–†’/¿ÃZM3ÏéS?v·§àAz°#/(œ#/”00Åkç[x$¨´S65š•¶¢S%i9Í'Ú#.f	´¦³2C…Œj]âá€è! Z¥{Ócl樺¹ZÒ#¿V›‰áÇl@JÄ㫳íS½Ê¢¼Öåׅzð*¿‰2ºcv+l¾‚$ø"¤¬úgÌã^v¶ô|Þ`¹‰¥Ž\·—;7”Ë·g몽øõýûۅÁ¶#/Tðk#y—“~ZGý®Ä«C¹ÛÚ÷¢ÿŠ¬¾¯W«NY¢üݏyµ›fȓ:nê9_ǐ3¹º’‡¬˜f‚Æ‘FF6164XDVBð=ì³åó¿¶z²n¦+Fª%ºUn]¢ÑÁpÊÃ1LáÏ52ZíT¿åIõÌÅ`œ(5"˜ŸÉÎ$%›Çúa¹¬|·çË¢Xû>«œå°îDpÚL/Ë1§¨óP‚ÉL78•œ=:ڑ¶sê ÚAÐÛÞqO㤇åü;<uZ¶çl»úOř˜1>½s1Cš8ñ³ƒe	°î1˜3wAH¶j*à—?»¦Í<#.ˆã‚U6üd’Q7Ôô3’XÇ÷j·c™H?³L<1ác¤€ëk«înؽµUR!ù2`«_¼úã<5éÚp:ف姼Œ¡ê°À’å!†Ð¯Iº?R1 èÂF¾/R8d[ÝDƒp}ï»ËázP©ÿy³ªKLÓ´Ó(4”íe²*Òè2ך†%¦ºaŠ¬F‹rV­‰í­&cE{O^têivºv#.{¼f¨ÝPél"~2 cHÆŒ  dF¦—Z7ÒbÕ0ÊZ#.³#/$ȘM̋"ÂÈÐ	’ SW,*+)„ƒI´ÐĞlã>›×ž§ff„’ÜW,מªWÀé=Ä[ùœÍUî×v÷Øñ˜èƿȆè#ÎsD»OGÉùbъq7‰"Ã8‰Õ_fúêð#œ9´.«ÛËNÌ=Ϫd>?«ìåPɎäm3	7“Ô-‘k5Ét…‰M#/ì—Y5¬ß¦}¹AÐ8^͐&ó:Ô¿NTxÿXàÁ¹räC“¼öºaaÔ±o#)£±RŠ­0Úz¡ð¿â¹‚º|rԙQQLÉgCö+¦ºvÆ0ùJ#¨¶ãĊÝL{Q)[Ɇã%kš0_¶œ}}Ր»wJ*øÚIU´h“dÔï¶%89<®œ«¥Ö§:¾;>¾8IÝ0mæI)‚x|Sœ2Î1B55#/³ü/Q1#)òjyš!¼“ó(LídŠGŠŒu÷YÙ=øÖ3FÙL#.3ãè¯'®ßgùÁo|'hÆ)t:ãKk*RDÐîԕR~G2óÛÁ^ÌÌÖ%jcƒñ;žÞ<Þmì9dÄoe+<îìŒG8ª×Þٌ}u"¡`ë‰O,çò½`¥„?°ð¢x³éè¾R–)c.DýØø˜aSaÿ«%:ÒöLÅÁ:ñ9÷ëÖÿaÛg܁ª`“i#)ž×bŽÖ–kã>4q³+Ó옶Y~šQ%Œeuc›4é´¶=‡*˜Î¨hNaŠ¥ ²(¦Æsm®m$UѶ孄¡2l¸m³„EYdôÍi(b²P¸î¸%¤]~\sƒ‰Ì>YÆӌ(@G‰IÍv1˜cjíuüÎÆn«);~!Sä|ŽUqff¤‡ff#.:Œºþ|J4´SP}-Ìyýä¹q'÷sšfÝ/-ß±•øt–1Õ=fFƒ'4LÖuÕcZ6Ó|?.æüõµì=š:ð;˜Ò›d݇ÚíЎn†ôs–wº·˚é͊üÕpòójùµ™o¥rŸfrØ#õª½;†PÿuAéG+–5:Ø»#/üèâfY×åÆq‰µúå	Q¹NÉ9Uu:ú‰êà;rIÞ°ò#/G_²xË<ñȬ¸¤ÂB6Ô8„ç»/·DLÆûú%,¤â½žy&˜S¦®ž¼iè½â{Ÿë¼b¤Wäæj>¿&ž1ÏYŸ Ög4Û³Ï“?®©×ò@dДª¥Ÿ•½›;¬«Í‡ó¹¦Å#/¦ôz»;{·h¿ô.®cÃ#)𼿦¿Å\gQp„a´ü‡Wºu[øpüü4Hü§¤î÷Ÿ‹³—Zþ¾?ǏÉöýÒm:#.=ÇÃÕMô¡Ü½ÍÕ|G±Ü‹-¨˜l”„¤*^Õ´ÓG ðÀîÝåøm¶eå©d2ӑ_ÏîÓCrqy+#.ük˕ŽhŠBSNXZ²ž†"Ië¯cÓLè´~XüoŽ¦»šëOí³¡ÚZ³D!Ód‚j'ôjÕ瞪¼–cҋP'oá(ކZF@²RF(K¬#?Oò#/¡ÕûCäþ¬Ú2®_¿ý_Ëçåðff#/µZ0;	a+K¥Ìv¿8¿Ϻ,ù[Ãe9sžÿ~¡_W­zû¾»b,*kž§kAËRY>…s&$T©bTŸ]Æbi-*6¿SíÖ.A@Á‘™†8@äªÿ™Ôš?‹ö%ÑãÚ`]=ßôd~U`W…¶’…\'IÝa-˜X'ïCgá}nh¬¶(¢´Qf™€o¤~¶45ål‚Mš™yÇ®‡­w³ÉÈa¤”5êHú‘Œ,Kövý÷õ÷¾mõ†ÏPHò„’·ÝnW«÷uÖ×ÜõÔFÚ#,-ˆx=áe’eÿ‰V6F†Ð¦sBØÚv€ÂbC]¢"‘x²É؅w#.ñ0(€7—Ún?âü6us:ÌÕQ²]G6(Ðm8&ðä”jqÙÆеˆbb%ûÊ3©ëmDÿ¾DL~"RÎç#.PQ¿a(y·Jeß°üîÞæx"R;æÎ͑³ªm À!³LScñ˱ÿpO¦%3¾T}™IgÉ=Ÿæ¼V¹?[:$?qŠ…ÑAC×휍k§¥\	Ûí&1Ž˜JÌ)$8³¬ÁBúƒâ…ØÒUJæñÓ·ë‹7=ÕЎþ‚$Ø¿^‡¸é›\H2* è¼Èf7Ñ1™ä¥ÈzxÜÿl&©ø?Vc¶%ü°ÂΒ(óÙT¥'م?wì¢ÖOÕa]YÒ9fm!ƒ¡ÓAzlP™„€’»³ýÖ)§=°:Qïcåhùiïü]öuïô{CçÌùðý¾xþúsÛÄ]GDº%䋹¤X^:~J[øÏÏ_ïúðê;ɜËк×ã}z5Jr«êªÅ÷SŸç>"²2‚ýo§ð¹óçùú-%¢þþæ3²¨é¸îýô€éA nìó"³¦­õ¼nÏ?„aŽW×YDë¬UB³£>Ý}š,§ë©Ã¬Uœ¶c~!ˆ”9ÊõÃã-ÙÛQùâ¥Ém‡¯ë??–®U|q»­'yúe©#/Îh7hÙær–ÂÔ±IØ#*Ù÷aØʂGñgTÐj^Œ3¬>f£ÚÎÎ~].K£Ø£L>{€64#)Ô%í3#/NÁðñ2EŒQ5|&ä~«l«TDsêè¾ÆS¶;ºßVZ½œÛÆÈltjl9§Þ®Yƒ*8èò<ËM†:U*â#ŒÜkÐ}·oåsšÓ~#´ôŸJ ÷Ÿ)Í#.·qåÔÒ!‰ŽÒn«øêö¯²ȁR‹ތ&퓳ŽDV±M^½™@R}™¸´¤j¡¥ƒy„ÚvŸãì\ÙΗÂÿ_¥øj„É¿N˜µ¹NÆSž˜¶Æ—î$ð‡èã©ŸËÆôԝ9m’&={‰p#I¯·×O'©G÷ýp\֊ٮ,7Á°éûŒª…±p‡R?¦>ˆ,ùÚ]+d›Wz"¨>Tà"“˜ŠNVœ_**5•L‘æ,v!c6xØ>É<Œ|>šhi§É”ʪc:´!<‰DøÙVgáX3L 4B@a"Úg·™÷½}g[ϗä	²s™f´Û®Çb¸?Ó¦Eø'nC¸¡ˆ$˜‘[ì½ç±ÛõŸ#)È×Ý¡$±@b’d˜u°i’̜•À)F"*«Ýíó&ÉáÞhçT–ƒF«˜ýÌ°üðR-C3Íx3þ,ö3lLÅUàØ[%6Û%Œ,@.K÷¾R0@d¤›Zç'‰¶2&1&É6— úºË#/~Tõ¯“øpþ›óû»ø>ú¹ëöy²o³Ö}>À;yíëÉw·Å}¿#.Ÿí¿ß¯â¾óŸç5†Ü`÷éÂÌMò5z*¤ªÛ£”ý#Ð؏gyú¶nÓ¨ÃÇn5߆®¿ÅJ³çÛö½Ê–§ý+φüiUÇ	~ÿíŸ1Lú#.½ÜÔþü;é§L­Z±Ý©È—Ó)b½ŠÑC~2ŸÎ|Û»þÍÜǛ/Zú5ïü„Ž;ú8õrÝöSxâ?ƒ\ŸŸâ®¯NÝ>~cY—¯ŸÙëÔz4iØ|2ïý±©rowö|=:«ƒM+vTà¾Í^Mï§;z¶_ÊT-DzM§¿	ˆVèíëvô”žÂ³˜súul<7òã†ÝÑú§–ªôI½þ>ز­›¢»1Ó.çµÅãÛù°ËŸ³ǖ¦Ñý*ѲVØGšÉö’+ôU×»~ދ©>Ü·×ʈ¢ÔÙ«×3A>žÓØ]3èý]%·ùÕUxtï築^È×`å²Þ»)æu‡â´³F›ïöÔ퉟“¢éUÃCiôYW¦«òeÍÖtm¿M—ŸC—ÌÙ·˜¾•EPñÎ>¢)4Y)K¹çÇ#.»å:o[UkÏÕNû|j•ÆíÞíZ’¿Xü”ü¾ý·ù/ÙíèòÜ[£=Ú^~‡#›Û\ŠýØsðçéªÃ›¢Ü7{o®[žÄÊ¢KÕà(Þzâ+V/×Îì£T¿Ý;êáÏò¦t"—N„K·èðk¥4?·ôLÝøFÖz½‡ËÑßúŸ7¿‰òÈÓ¡ÕN=F0^l)9yôٿѝő٧ê=$]§“\YçÛ9Å[lê³ ž«ÿÇã®ï“Åa”Ã*»ÿÌ4\~<ò«³|·|^‰{Ž£~Ë×áòùùUVNÝ?ÅÍ	®±ºrò>ߗêӑ|Íß?öZ`Wæ³=*e”¤—ô5éÇÊúý†ó՗Ëë×fê#.o/ì÷`凾£ñmoM<~Üï»_mOúe´ÿ…’ŸNÿåöôÕ.GQU¾Ëå2naF	/u±µÏöûîÒÉ®sÆâíŸ#.„²§¿@wÝ£v;­–ÏWô·êÓjÑÀé>Sû¿ŸŸ¿íú,#.çëË£nçëç©özýµuø££ÑûJ|ݘH¨Ä¿A#.åÏ7ìþogÕþ_ÝáýÍ'é°t_m£k¦›0ôF’ÓÍ¢KßÔíµ}âE‘¹9"7ÓÐ?71îø¥EYø§D;"ökÓ—Ô%a–ž¥ö|úþ@×ɛ÷UÑÞuuëú½|×ò?Çß÷z>­Ý¾’îóÕæÞAùäpî<+¦ê÷O´—/ÄȮηݣnÍ[>¿·Ôµ!÷t·ÄoÇgu¿ËGQåîñø¬îý}\çgÃóøþ‚â)ÜzN®“öM÷7cc¯oÕk=gðú<8·Ùøÿ5\mËé¯|›6Ù«¢ÿ…gÄVkò·öáՆëôt+Ïm;¾ywi‹ó›ì<ýâ©*/hµHvGÚk_»˜à›ë^v±»;ø@ÄyˆùSöʨGë«lQ©¶+?´d; BèúèYä›Ó°aݚ±¤7"?«x9šo»»«¸Þ`–ÏŸ‘åýÕ®mûþûdŸñÇ)m«î÷×t¿Eœ®±*ßç^aml¾txÿוÖ5hòvYñՖ¬}=Xý;…Nnv×ì²þ:Ï<3VÇ?Ïmی}՚õ¬”0&Žgv.†jãu-†«â‚/Ão]ºõX\­Ý$°ÒՃš•Üºzê!«µ¬­nOUMïغ³å²–[Vx%ñí«ŸáΪªzõìïEüøg_89_µ8›l¯Ï4§(‡;yþ؟;sEÚ[¢!Rßìæ—53F&«f’NEÓ£²{I=ÿ—–¿=…z7{Y_/f¿¿Ý~úªð{C}{BuJÂmV‘ø+£Xh·™<Uðßð”G6]Ý<pŒiY-zéJøÃC/‚¬ï9\1ØÍnµpõz“1”󾂁Gír1nȉÌOøïX՟ÀÄzâž;Euêý;l>*<îÓq®_†-c,#:Žë%›$…Âý†wpû½õÚ-ú85×è1/È·ÛîÙÏ}”2۞yҎgìñƒ—Éoóð)²Òþ_À“fs”B¶§`ž¾ô	u­m£I¹íW-’,œ« ›-ÕY‚â|¿³}¢Ó-NûMùPìî²ë:[â‘Ù㲩쿾êí®Öç~xœÞÎqsU¢=\¥õj|PTe›9¦íY”ýý¶`WŸá+úð…øòÕggž½›·óüß5,S9×V¹»Ù(§†/!xó8ÿ?—Ý:nôþ3«åÏÍ¿Út7¼Ð#Îl_§Ûº8Aë\%áû÷ÆJÙ§ý}ùýYö_χ·m\§òÞËÑHnª7“Ûð¬Ë¹t~™{³¿Ç×¸Øg/ foS뙰èô$”ùó?¶¢Á5	óä`¼Žþ>¬y±%ûJL†²Üî¯ê¯ØŠÙv{,}k¨Ž|þ«1ÿ?¨$›`L/ï/óa]µ˜™s#u·!Ož*ìD„<>ýb1£PašX#ia4(­*IŽ&€­Øá‘cPb„aDQSR&Ò0a¶rôÖÚÛZÜNK£ƒ2-`ƒŠÒl4É4b,¸ÈȶYEÚ,Y+tˆÔºéåFºIJ‚“xͅ„ü±ýxÄ⚙Q3EFßdD?¥S‹&Ó¬_oYÜÞ³!¹á%þ9gu‡íS$ÏÓ4Ö]'ßüö†µ—Qâq8Òn]ÔEQ›™E"YC ˜äÄSÇCô_$t/=個Øp¼X`8hÒ$H‰GJ²¯n…¡šldcf*ŠÕ‘#.45šv§‘FÊéÚ( ÿ$8ÉǦ¿	“|8Ýôao…YiÕ¿ä+ütøS¥yO †éٕ¸é©ªyìصë‘u*Óð®ß&ûu›}µg³b_»×æ»?9é\Oâôé‘û²³—iÛSroÕ«¿>ÚU_Íjw=þ•Ú—Ž}¶ãï¥#/°{¦²E‰ ¿©½m&¢HVóKÖ¥ê1kÆyû=ߧô=^ÍZý]HkÙ]	ý'_)0ڕÎ÷þ.0Ø`¶}º?/åǧ¯«*¹-·Ôßº.”šRyIâ+”\Ay“Ñ„ÿo<•~/GìÕZåãDR]ßÉôo’Þêšý¾ñÕl¿M^Þ¿å‡_U{7nþº¿n¿f’Yé"v9øÉeÁ›œó|®A©ÚVÿaàcM)IM9üø¹Aúîcé°ñÈË…&•¸Á,m+G#.੉.¦¥#h€ŒxÊ4F²·#)i³ÜBÃ#ÀlƒLH0ˆ¬r	”%‰Šbs«IµÎډ„.ñ¦Ûw5§™Y9#’#/)Z%¡`È¥M¦ÄˆÂ¥bi¦Œ¸°lxVZҝ}1òu%{Rop¦ù¦ÌðmÑV#.×#)°u12(8ÛC¡GtqF$Þw§£X>#.#/9QB!€Ò€0!¦¹˜D»m„Ài†J• ³G\Uñ¨ŽvJhƒ à)"”¨1£2#.e"æ¥#."N*Fžȇl’©bl8$XµÈÃX0a°i&PNÍÖÂÙ§¶ö`“4Á²ˆ‚Æ*«H!SÄH÷bÇÚ¢·f”HþÖ(ŒLÔUV¦œ®Â$Ô(¡Û†:Ã#)™h K+Rƒñ#/œ™Ü(qN;pG­‡Ãq6µ%MŽ3ME^­nÞ1aß€Œ°Ÿä})ðØÿs<ëoð\:$^}nöWøŽ×¹¶ì˷˧ýU}]Õ|Íä[ððd·öçç(~+¼wŸ¼í¿è?ø[uåvk”õ#.^q®õÖ¬RYÏ-2«ëזÝ«ò¯Û»»í‘Ãflݳ“G#/šÆõÛ¤¡ƒQüÛΝC~÷×.úJR’™8™·îcʯêµv›ž]ZêQnúT¨P·«]„JÎÖtèo‹f']Õ}ˆIϧ†q®Ýr’O®¯ÙmU#/Úü$å³<nå7ë—fÑÇSʦŠƒéRçŸM*™#¿/Œ‘Ę.z\ˆ¾KpÁÊÞÀä7ò÷1Œ|š–Yé#m»dŸ³#/»ô>Ghª^¡¥aùý/?Ìþ‹}¿Ò¯-Ï»OÑÔ0#.ûÔ´<R­œgÕ9ïòÝQvYz œÅs¿a뤋”³j‹'C.|j©¯ؒaœXÁÌ7·†E2pd5RD—s&xÉ:ÎõéT¼#EÙ$pâÄÁþŒ’ o¾täyg$„C¦¥æ%ó·Í.Rèà[]ÉÉɚ=®EÁØîÌyS3AoEpŠµü=Ýìå‡ÿ?M¯ÁÊN?l~$ 2Ë$èuk\£s1¯YÄÏf#.þ£Dç¯'³áÄÆ7ð‹mÛf°»WHl{#."9ëû&K'²˜9žÜz57QšÊ©{n~V»ùå4’ÞjÃV†½Á͵ž8ôlQ=¯RÚ×šÌVÖ>ºoÅöWºqÄx¢nr'ª^>6]õýn9½3¢ ™TÎì’üºÆ›j¢#.HG©bj%þ+bSöJrT&5‰TãûD „é‡Ãï«Ñîü=V|ýçãô=ï¦?TþšmN&a„˜HBhž˜«ž%#/ߊh&4Π•ì4õL1ˆ åXjgLclm…™õä›&f›‚ĺb5MXÀƒDI<ӕšc‘Åì VÜ+ÈÅrLÂĎI]ËA¤T`ÐÊ+Q`|‡¢E7NÙ´¡®\»ÑÁq ÕyU²Ý@QÛ½`³¶ðù‰¤Œ#/R:ÊÁ¢Ð࿑3H×-¢V‘˜bëL{Dm,âÜð6@«A_#)ÌÉcà´õ¡œjŠ¡íéÚv„ZÊÆ8`Ø8´Ô騞 Á±D´VŽJ,±#.6ʵ;R›FŽEÎ	#.S54ˆ©.m´Z1"”q² –c/©8LÎQ-#/ó×ÿI°çN.1yÞJÓK±TXÃ-dJV(ZÅJÃ#/	ðÈ<-[s#.>û%f»#.øºp6L©ÓŠŠÎ˜¡Ç!ќþûþË®3{{ƒÀò‘ç§ÍÆèüõìs#.V±½&t˜Ñ-*ì‡èu"!AöŽCƒ²$Oûºº,cH1î=ž¼:×äה»]Þ»¸4åª<a+¹/Ǝ=0ö`~ê/Ð~ÂlåwœAm	–Çó£0EHp¼àÖÓa–|*LMj³è^½Øh$¥±0,NŽ×R#ƒN@‘‘"G`Æ¡ªO”Mééθ¯÷v‹ƒIBœN¶5¦™¤Ê;t¦ÁÐÍJ”cÂâăºª4´h¡EŽ‹l$méÚ,]峫”mNÝ=~ãUP%Y6’DÑszh‹æÔDJoö~ŽÜ~ú—éèκþI}]tüÕ_Gu5ãöù<Myxz¿Ÿ’Þ]{CÌBn‡n¢ˆûiú˜mâËÇh$\´ R76:6;nωŽöٌ’ã²L(#/˜R0v*‘º·bRД"kT)c9TinQi7D#!…qUú‰ª&W*-c‘§KÀÅ[¯eK!™“¦®.gH²dą”9¨µL<LË“0,”Ê’Ø0ZÓ:Ò·Àצ•zZyCZU`ùoF?Á¤e\ÊߊÓæpï;,.ï1u6N½¹ß4iÈÈR†§'v\$Þ›"hàÃ|PëR+…ñI“9±i–Æ/Ž&\Š~º»4_Ä(tN lièÈ(¡v)7dŒC¨Bq”Þ¥é9~x­G{|µ^ÚiNº´W¨7Îd{!¿ÏåìÄvû^ùÌR“ýbfÜÞ5˜³U±±››•™‡›z̓°ÐpšêÕÎQÔ2³MØj#|ÁÅêòzMܪw±†oÊNň¹p›9SŽÏXí'`ëÞG³	–=&Áä¼Ó‹0œ‹Ô„¿L¤¿¡Í›3ömoWäƒ	©˜ƒ¤#.ÚBvnt\ƒ¤Ä´ÖY1ٚѾQ&ÑG#.B㍼åcë#c#.­MãjÆ܍˜©J÷0̬26ÈÑ!U*°!’ªDK(VÍ#.²f:e†¹«}nùÎþª¯#.w+_Ç¿9Î Æ#-4ÇA²ÀÛLՈmX¦SŠ¥R	—]dÓ×GƒÂuø"$ÀôR<œ•¦aÆå$iökˆÞ\؆¡ha)ہ[çLo†;Á#/ŒS,fœ—2ᦢ³|ßÀˆ•‡ím¼·Ã—fLˆÖ*£7G|¸g0ÂoF’bJC•â³"ÔyN¹Ÿb4#.2M›ªNlj½š!&hL$$z;u²…óãe`Û_/œï‹À]´„šíý8ók]M¡+Á¾_(ÖY‡âI,” 隆¢ß€òåPY;]°Ü0¸8ƒ‹-ùF¼!ßpG#qn‡˜ÍЛ³k=k¤C\#/'"#ª;kSFí|oœ]a<2Eh‡†Jî+¯'M™:ÞÛg|8,Ò¹ÀMÙ¬97Hyg:b¡Çü1™¬Û8MÁ×5K‚°Ô¢I Ô!ƒ¥Ð,^(± ¶C´­˜³nÄbú9%¸µr`žj¹¶|,(#.5d àêÎLÒb0	ð(‰°úqïÆ"•])¬± LNµ~6ðõZ\7¨tôMAŸÓe‚Æy~^öÛ^փ'¶ò¹Ç”ö™„u—æSãè±=í½æF[sä彝‹Qׁøl*Ñq|éb•6/á–ï¬Yãqô>ˆ&3²G"ñ9_è"»«÷‚%ô<C ‡¢vز&Ÿ´bfПbî ¹—’UÜGpVªûÛ©·˜›r©ðøež#/˜ÀeF1µUœ‡|“S¶,”…	Ò1P™I†Œ@ñúW‡˜Òה™‘Õ‘›¦¬¼eånú¬©#)èB`I{e2mCª5äë7t–9Ðæ™0™@ÓÄrC’™wÿyvËùCÅ÷™§ñÓ½^ôÜ>HOØôv-ðñ1}Nžpg3/Z¥äµVËSÎB2#¬9ŠMýí,OyJZq\äíªÁ¢YD­aâNm¾ûkÊsÕðŒã–ùбk<2C÷¬Ëèáâá@ÚZV‡ÃÅ<»‘:š=<4ó65ŽHk…)^áW±íæó#.uÁ•*qŽ²|b„LÑ(:?¬\‚õé0#.àÀՖ¨7R¦z6篫Bc5%&çûܸoº.2tÍØ»·Š+³J–(“ì}ÚµªT9ݪ…ã§m“¡(º^l˜},$AîÖ»èËa§-xk1™8Œ5BçÚD£°{ôrÉËcDsqR©k¦ˆ*krwd×ËBµR\Ú«¿‘ù¾çüí=ÿîí´ž=õCLŠGñ𶔉ײXâe0”Oï3¡U1êCÀtÆãÿ{óþŸ/2}ÞI4ڒ±Šö7§ÚKì%géW|KÈY›u6¶psik#)H9r)&a4:8#žEÝ<˜k)^vb¾×6J£ 7·îꪔ<þù#.sIÝ­i\¢W¿½EXwùˆZüô·&·Þò–˜²\#мީÏ\cJ–^©“”âËM³õ-#.~–DsÑÞʇɺIJ_³#)/š¦ªT¤æ	ãSy,äMc!UË@,ŠS(Ë>‘ü»‹(†*=KÎU—›Ê]u¢IÌwC°ú´:PÒÄÅ^j¿~8^]ýÚ¶—jl qÇ#.„#.çá"tmã”Ø#.CËà#/êÚYu#ºe‡™§YÎé8`®Mq¿Ÿ¦)–m^ŒŽç½ØØ|ïׄ"üÃWJ³¸“÷P¾Ñ2íp®Z=$šöàÉ´Ïa*°KŽ†;'=ÚD;¼™¾.çôˆi(7úvçx0N<ñ$ºtàä¹d•<±%¼|ð8»ûá«;Òy‰^=ϒÌ8û/sÖþ¼ÿ[uR$%åÉA]ºÌ&b©òƒ…ódrÙþäl¶žÒVØGä sR ‚ñª-#/[ck8gcò ²„ÒrŸ?T”î°zžßè:<Gޟ—ôwÈ<(…<‰µd—Õè;á?¡«¬yI×6„*R„´ˆQ¢}IÌ'ªcbü+Çk3·!РpåÁó’ÀœôT¬µ "¦Ò›+^Å)ÊW)‰<w¢Ó+u¸òi½ÿÃEGIJZÌù¾Ïë]›×IÚÏSÚ$‘øvv{¥ü#/dü5á÷gHÑzëºV!­DN¢Ì;xÄ&JÛÔ²ì¹óG©'Az„b>]VýÁèJIíÁ^è,àz<š¡ØHt’f‡••jÆëJ³7̓—݃TÆS=¦Mž;ô3ÉonÏÄ~7Ý{¬›9—sÎɦ	ä4šÎÓå³+Ùi¬“üÓÓ%!ˆâNFÛèÀ»Ã½Á[¢ð)H¡Ì‡ìv‰¾Ï ñ}°Â†³þiWҗ6!ݏ•-°º9ØULP!”r<ã¹,ž£õœÃ$ԂÓáXcý²§4‡¹’ei8ìè»h÷©RÂ\T~µpUÅތpºÂ͍ƒ&ˆ¨«0v QfºÞ›˜d†rL=æ)ºŒµ©síe2m˜t:pü7\.9ßËmðùk'C|Æ6* %ª»_-›bÿä#+ȪÓdû4[}ʲ³‘å¥wG˜²Õò‡ ø|‹t£ÍÛÔØZ•Æ<g)‹·Ñ”q-º¯sp11$Nâ=|¹¹õÖbCoºUëäp‡)ćtî ¥µ÷²B>G³žTúy·$±ŽQs‚ ™ð¬ÉššE¶vœÖM_zw!î2å"]	©gÌà°~ˆé¤$ÅðyJ¦ÚJ8Êà˜rPGë™f{d—Vì»''=OÓ·½±ä헖F”Z6&ǝRø3Yùþ£÷OqŽ#.ń®Žêæ&¢½^Q'î<ÿVx†±°&ýò?˜›jhŠNâ/†ðÕª»D‰Iúv½»džyšÌ†¸¬a¬ËÞÙך-vC¹áð‘à<؊ƒ1ìÓq/‰ùbôُŸmFîÖWn‹v¾³›æ9}ª{¯rµÈ\ØØ°ðáèªWšëã¦çÑZ®T2!Ljx!;÷N'ÅÊFÕ)¸¼îV…J¢+ãi»}[G¥#MVL;ñqòñžùKÛê0ÙhQcË	BâÝ®î×wÃv™ï8³/™'/%2G<¿6S6°Cý¬}hŠÚp®MäLOc‚tIÊ¡sˆ…ÍθÒ츆ÜÇ뀺pËìøI(K®õ8˜þOä¹ëíy>»â}uÝtáñd	¤Mžþ°\ã­ïQj)·N$S-Ød f¸LÀÁXƒs5‚ЛÙyAÄòÕ~D˜ã;T»ó>k›º5Ê:7QÜiŒ¤MYÕ/8–ˆ^ê¥o}¨ÞïäíŒå[]u>º3ƒ„mtW!Ní#.Ç~Â#.Sa“à›¬˜<ïm(äsc‘ÿ*|/+Í¡ÛÄþß¿_3^סuùkÍM!ÀóýÉ»¿kó²öî"˒‰tՌþåëÄi<S˜Ì÷Ç^Ïvù¯òDµkÅvE¶ßðM´Ô¡âF½òE˜Ã§ƒ‹¬{3¹K¶J6ØE¥•LØóíÛ\ê“0á3š2>Œ)Gµ˜ÍEˆuª?Ü4Šs®foY={˜ÏëŽÄ‰·FÖõ0“Mқ×WËSbšG¹3­Ÿ{¿³ÝMSTׄÛôÛ]ùÍÇʎlëàö”#.©G™$p?ÀåŒŸÌãÞ=8¯Neùëœá/|¢T±ØàóϜŽ±¤º"Yhá®ÈÑq¼zU»Ð÷ìç*J;©s$S‰üáçâ3¿64M¬fØÁhùÆþ#.¾f)½Ë7œä#O¶3uì֎Cpv6øâìÎäQ/6¥¡µq$H¥¤|&z*ʲ#,-‘.‘–X%fn)æނ#NÒTüHÙØ8œù§G®5ÖjDé'í.J}ùÛHukdüø»™¦øT?‘Ï|ç	ÁQy#/ëÆS2Ê[Áï·“#.—øK?éý©_Ð|uԉŽ|ŽH“–kQ—‡Ç“ÍKc¿¥‰xnªÇuÚÀ¾,À¨/ŠyâlÓ~8C Ø˜œ·HŒY¤äfœi_7òŠ5¦d·Ÿ‰Ç‹ù¹Ïnç^œ³®ÚS#/ǘŽÅ·v"#)ýÒˆ³ÙÆQñ-ùT^æl)¥¨MÍZ+«á†‹B2çRiS¤9â·MM¯É˜ö>{pK¦&ß1Çü´¢©Éü’€¢I:›Â«¿ö}5]±Ø³Róæ]„®éó~âɕ¡¸bJÙ+­‹¬õON){êtx<âœj—Ô+Ô4d7pó4Ük±hÂÖºáÇz…‘3ŠK«žì“)NXKúÊפÔXÑ"…×ÍÔeúˆ‰•9½<ù®3HÕvvèQ[ywPûéû{•BºÂùU}k¾9Š;â«6TS~ÒYŠa_‘Ç	»Š_ãýðÛ,Ó´–XXøsòñ$MU~WiÓ{V*aȦüÕ[y&²—(à®Vs1ÓmªñªßßÊ«ËZÌÞ¹øRÚ¨ñp,*-™tôè®%T¤§;ùÊE¾©gÝq‰MéÅþHÁkÄ\Gw2ºŸkö¢zù¿—IæØ­ø¢e'mÿt‹ç?«^m]><õØ´pŒ’àå–lŒvÔH´Æ1¨}žè{¹ãœ65XÖU'Ç,nÁ%}ik®2[VZSÊí†ôÓÀt[•ñsº=qDG¢ ًý^i™í·`j®Eøg"̈́ ŠJI÷#Î\ÀîÝ5%íúçõQ]ÌG]öçsTŽš?ªkÇ~—㊗9$’G¿γfKêQ­áù]~Ö`Ûyùðʃ|¬UêƐ¤DSG**wsGf©ñï¦{C:U-®pZQN1•Çê7çeB#/ŸM ¢ysgê7k¦©Ö$[ü—-æIKç8öƒÿÙìffyo/2ã‡Õq‘­«N‰T‘K"-„Þg<ñ.Õá92Â׍k¦äŸÛ§gÉZ»Ç2¢¿]=%àˆ/O«2—¾*û÷Çn?5]ò²È½Ð*K7nk­ÎÍÜ®±„¥Ê/èüa´AŽŽÎoÃϒ¶±ƒõ"•W–Ÿ¤ž¾mÓ:†[Š’Ó?_kÕõ¹XõìYEuÜô¼Õï'žUxœ6þ>¾xç¥öPµ—:çùoÒ¥g{ðù»‡Ž‰Îr=ûã“NÏáÚræL¶#}ÜÑÒ#.]®»ê·Á͖ssánB¡– ¾%á‰Usx¹ée„®&ìy»¢ªåîéå}K Æ¿Ö·­‹¦cªÞ¬öÅñ»^»¥VáËm‹.“)Üd-T¾T¸FßLañÓ|Öyn5xóùûN¹Éï]Üìäûÿ]1Å¿ÓÎ1'g 8<\λfÖ>Owgðkçç¿Íay`~:{ÅspÞHi#/3º7E¿ËÃóY4-jcʳïÖ÷íÄËI]ü5-¼:ó|ôùc‰}½£‰ëçá,>ì¼ùò¹‹óƒzžxš…D:hv]ËÉæ¥Éü áw?‚ZQ¸ŸW=:ôŒyj=}Î;ïÝõzÖ}÷R5þg¶HI­ž4·¯]W¯m’¢î³M欫¯9Ž½8Lé¥×J¢<±¬Ðþű2dsg“n[n1*é*ÝÈs§¦Ü¬wéÔ}¸èɤԯ’D_[“Aå·3ïç83=Üüã·§Ö³¸Îwt\¯[p£”¯…G*n§ó­¯U†z*	p ñ«h‰h––ŠÕjÆ5„j›Î-×˯#/±^ŠÔ¤<¯‹£(‹_t‡–…ç¯E£ŠäŸÐ\o9Á™d|öNº§ÏM…t«KŒ|'Œ#/k¥•ëvÛícõ‡·O tñž?Á·§¢äܪ"WÖg\¦ÕµÕ@pÅWµeœhÀZ¼b»´td<êeŽ‘O>#/}þgJÖµ³ØQß×[4G¯}Ãœ#/•$dËôžüt¡´Ùœà1HR’c¤"Õ.iގÅE¢¾dmºš**…$x>|r²ªø]ŒŒogàµ#.ÁôxõŽý#.o¿ß}w¯¢’®™yè¡ä–-Ê­O#Â5ôD-X|Ua-IêTÑÐiÂz­²"Ý*å<Û¥×U³–w]d¬Z']#.+,4çÂêêÉèúÏÌ>?wNþaNüùzFëÞ¤“Ë$®×ƸƒÊÇ_èÇmÎÙÓŒ{û1ÒC°ºg]=²OáFºÃ?Î|T­ßÉEwÌt£ƒ,ªúPªz—£	œXXÁ;»â,ºj1TRÜúêœëdÉÝ`HªæØõg¾3ŸX£ÿGÖwÉÞ*ä¶Ãª‡Æ\Ÿ½ÝÂ©é ’úô¢—y{I	lt/Ê7ßeãçøâõòÆ»È\¹¯ÎI–;­Ïr¾[žwäÎ÷)JVmÛw֐wMÙÍeÙ1Z퀻—ßÝÉÓY;ñÖ'ŒTücŒO§CÖ» ˎ|øû¿f£]ú3œÉ]Щr˜âÅÇÏÆ!›N•3íÃÅ^%8PáS#.ŽfIy×ipZ»Ä˜ëX‡cö”æýþ2Óï[ŒÆ¬èõdÖõij˂2ⳟ#.eÑ®<=|!ø×z}Çx³9;u®î_áuÛ³Ôû-Á×À}9m!eåãëšYCû½#.	[ŒD|Üß%+^ºÁTˆú¼ÇX¬K»S¦KúºlfSB‰áT¢ìó7HŽŽ<±Ê™ÝŒKÊí~H¬—lýº\ú~Q‚Î:EF…¼?Íþ#/1yGؒMûÙ&ÆÝÐD‡Lõ,SD\@s=¹ñèÜ©•‰l<ԛ)&¸q†å~ép<eù±ÛÝúü<5™õÞâ[[î*®Zºï¬å”¢ÚQÇ´±¸ÏO„¥XÖx-õŽ¬Šˆ§?ON;'渲É[&Åaƒë«tUe‡jþ3¸¬ö¡Ñšãnõz`—º§œƒ}ô«-5=˜U«‡T¿ns²Ôü÷êïíCÖ>Kõ&Ååמ(ý÷ßÞÜaŸ_OO|íVîâÝ\¯bËEWó¤]^ç6Úúòvx¾ù;Lp¹7‘qF…γ²Þ¢L@˜©ú¼_mtn(ùjv<–s,%¢çUtˆâR¯	vºf¨³ю•ÌûeÑã:KFìqåöB¦ÏÊ|Zö +zbw†4ò\ek»ÒCž*ë‚Uùz§Ï±é÷s-ºÏɳ×ëyjö+ËËÕ@âG¢È݃˜›”œg(ÊJ:œ3Pî䝠œ©Sõʼ;5]3~¯Óêû„óÎê|¶Ó+ìÙþPÓÝva"Htæ5ú¬f0—'æĐCOŽuÊÕj#/¤¼°Eo7ß+­®ˆîì)gIJøÁ§Ÿž]tÄTþÙ¹?WhÉ÷˜ü{—¾&s°ÉiÊÙʈãƒK¸Ô‰Í5ª{ËMvvߍWÑómÖÏ6W»~ÍP÷ôÅ/–kNªC'œB4h£µÅþzñ¶³{åˁâ‰'Éßîèùx¸+n7揺¸®i=YNùµx½S&#jæSΏ,nZõãU‡*µE2uv¥²odÝ[óÄWu|ñ=çé¯U2îº~®­—Þ¢R€çnÎ/}x†žø_ߖ’[{’z=G¯Âtuq|×nº¨ë½Ëœ;ü0åó\Ñ&sÍßSÍ¿éGN»‹õ~˜ã2I±ñÏYŠªÉÞ·í[%}ÔyÖûâjeT5è‹!­É½ˆü°¿9ü>Øú¤·¼ÀO®äúPøvô:Ïõ¼·¨TXÙV‰HBJ!Ú%a#.ÀÞRí™AZ޴ׄäzãø-’,MA2IÖüÏ4—7› ìlFÛ¢þgÄyf?@™ŸeðfŽ}s^.Ýə­ê»C-)ƒ\W$kl,ԆíÙoš[<+<,ž#/i»¥˜ï¦5˔gŸÎ¾%uFº¯ÝKyUäȜc{ïÇ="òW–E­œ´Ëy’o§¾¾þ¯~k<>=2täð{oŸÓšëÑ,FC™ ËŒß¦TÔi,CUÑ_#.—Ô«ñÖ|e¿N^¨½¯AŒ5¨k”¤ÉÅ4fÚòv‘m´f4:êîÒ±=Ö½u¬÷HŽÎO»½oÑ`“Åô¶:ébé×tQÇ2ðùOýNìV“tÖs:Mè÷>WR«ÓOeA¢Ø'Zí]ߍíu¯QŒ¢JQ».§´bD¤ƒÔ5Tª¬ù>ƒ~n`|æsK¥ÖåѬ¼¦|'F‹Ã¦¹NµkÜBí¾=Õ>©‘§NQj+Ã=$ C	K„úHY:(ðÕ=–ëÅ~'›Ç^3¾õJŸsÖè[Áñ$uéƒüѕéð}S6<òj8>…ƒ'…ùçȖUÑEŸDYƒµKuN¼“}0óov÷S‹„á›ØVú%Cr<jye©ŒlžìZ0¾h´$Õwõöíå—ëu(KÑs¡Ü:Üm‘Ž—Èœç"ñOzï#.1¦ ùh÷>Ô?¯ñĸ—³’™p’^"1Ë®³›zûˆ¤rãÉÔõ”ekàŸ]tfЛ–¼™«K Î&a:11ÙkµÂ5k¾%§BOŠ½r©íÙSѵ§™4úJ@ä§¤J:«EÏö@âê˜ð@±¾Ú}®ÑëѲՏïÄÚíOڑKÚ‰Njýk+ë}‘œi¦YÜKKrÓSٷľº2±a¯àœ±È¥NqqËÓ	K¦étãvzœº4'to´ÌÉÇ~µñV֏|¨ Ll|âDf%Ϋ~`÷nœŸëï_BùÏÝN#*‡Š9zŒ\$^žIƒÂßCŠ)…ԁô¹uò¬¬‰nÔ["â´Ìì‘ó=þyZivd$ørÇǜå7›¤­‡ž«ÍÒ0™š˜ó—#/‹“Ç-¢t–Ýô¢éôfƒæ l‰ÄÌ/6…@ÙõN‘Rï]!ÓBö½ÁÎxM’¨Î—´k¶þéÎïׯDþOé±¼&‡<¢Ëýœsƒ¹:›lR†ƒ#)¼¨ˆ±ÝïڍomŨ·tSõ"¢àunì)‘.™$ô^}ý4‰Oë-ÒUwÖRŠ€ì/VL]ßYÓ7tÃ?n¿nvÌP™ÇC²c%„SmøMÃ&¥š£kí¹ËÖåXÉ!)$ÏmœõgYwÃÓÐ.¼·êQÅùèQZYpíþîoìÄ>ã>Ìn.²ëzÓ5zqL‘’œñ£~ÜuV 6ü<±Î^ÏÚG.þ£¨BWTÏ;ð´•øµ“Ó­ƒº·]VaìwbˆÕÊ\}9PÖßYDž<¾åò—›ƒ0×fsHR/]üÿD²c؋ñŒ:ªúÑK|*ÐXlòš©8´U§L¾ڑNÛ¬ó»ÕÞٖµ±Ádî`'{ ¤°ß„xTz¼'yÎMÈEêSØ ’b”îp¤ƒÉȘâ°*zî§ðřö«ý<­¨“:y•ïüM¿:q²¶·Ü\Ҋf¹¥…â›W®ÑUƒ8Rw¾­¼ñ©®Û¶µ‘mØbJ½öHº§d.9¹¡IÇAkéÔõÄ扴“'=¾·ô?F×{û9Þk±4Á2ý1ø}WÝäó4ñ®{g5Ï·vÔÌ&úÞ´~ÓóeøEáÌÑ:Fõy>+‘~êËmµÛ‚#­1t<ڞß#.W*T!¦›ƒ12=Øþh¼ó‚[ÔU$ 5„ºùÈu/Ïý†'H’3!hˆH ?*žé#/&j‰F‘0À¬ë¬9Ù‚‰ˆ@Àâ5¸R Xþng3@§"i#/:fõíÉß©ÂOÚôØûΦ ub	 ÈR”TÃIQûåÊ÷ÔfÝü¢™5¥qñœ|üFñáÎh-¦Êê³*4¦¨•R…ƒð”&څœKªAûÄÙ³öÎg÷`šžœ¦_Üô´ýAˆ‹1f>ïŸ=&Tí©…Uþ'6’¸Æî5Aö?ª<é"¬àÒ*\®GøOMŽp\…æOõ³74_—¦®_hªîNêúErdù=D®Ô>½ÍÙ»¤¿«æøï·È­„Ö>T|ãåøC_'Ýåæ,Àù…†®v–—ûŽXe÷á#.ö¼yZë‰Y¥·'«Eභþ¶«sèßYÑÙT’laO9U>Y½µÏðîàÇ¿°ÏÀ1pq	´A6BESéx>†[ñîŒgc™H'êýþüUÒb›ï"mYšáŒnñžšÔ¤!;M#/? @ôfÈ<£Ž½uôàsmkƒÚóp@îñ–†Þs‘VŽ_Ký›ýÿ‘'”LRI┎ìFùW#.ÆþægÓª~¾5Ik¨qF׎›KSqñº¢ekOÊjª‡Š‘ê•øH+@<a9ٚ¢“ ­}šÛ¹‚¦4 h(‡=Ò‰9@üçȎ0þhù@òŽ–öBykíÒ=Ÿñç9Éú‚vòÅCÙæÅâtpxFqyšå!Œû¡ª7–¼D·Ñ‰YvÎnAu#)Õ@lbÍE°èjCø D¼zú:aäFÉ0ø@{î¼0Gù¤8Cùõúl¦µôªfd 9×ü“{#.¬T2ª³à÷ÙVçåFoªhxTÈïBÒé礰ÀDS{›|û®ÁíóðÛ¦ú›kµÁa#.J*#4«àWµ¤µŠOJ7ÀW^úâ£7eòÞä5ýºváðcÒ<¸Xhla'ï†’”#î”àvxYˆAu+‡ÄqþêÍÉAîU³…«§îÛÓ±´ÛLX#)°Â…/#/:Pp[å3õÕ1ß4l!¬¹;¯ïBöª@ÈúXy„ˆ8ú9º¢û*0,yÍå9¤Ù4AÏ6}à´wF:4Š&û–¯†u;ÿ‚šá×˜|lñwúHÃЩJB…£©^yÚ2Ì"J=£æÞ§–ˆlÜõ7Þ¦@g_`ےN¦M^ß½°ãK¶üGš‘Õ	cçÂ-7ÄS^ڙ£T21!±K”S°›ÜÃwO%2Ò•&†NY!5MPRù‡QØ9“Ú+¯L°è?…rЧ£}ò‘³h֒mɋFuÊ«KŽŽê÷VýÔ¯fŠøîpxÐÑ47ÁMíA7}#UTE‰*Äá)G7#/¯°õñwA1ºnl1t°LØÉŠ~~×ü¥åuJ¦®\¹»ñ„‰ñ±°›Îy×Ï#.¼ªÏâ†ètþ#/ѳ=¤Á$,PÚVA“žÆÐWÒ8ÈyÌDù#¹ëœïï‰3í-Вß!ð‚ïe~ˆtÒ1Ë“Gm÷­~þwÙð½»òIË?C¾Ï-#.Հ#/,4¤Y9&.ŽÎ8¾Å ‰ŒìÆó‘%0zòƞLzö6¢zúüÐ`»>“J'rJ‡€›ˆˆÇ—Ã<¹*Àõ}h4Ìy‹‡]\ù•͛­2eµ™Á«ÖùV	¥¬xÖ!µ8ó.:éÃ]•H4¸ÏêgU¦Ý'ž™]õ–y7Uv•:V`ó¾í5gQÏÓ¹0}9k•ÈM:·ÓÛN­#)Õs­¢WÂMl’“ur×¢·Ç}†)m»}n_9·A5¦ ‡C×uóiCt¶Š™l¨Ñ3©kõe˨̛iÈÅ®ü¹¹<â,ì¶XGqVÂLLĦí2—Š”z“gB߸ªøfm5Øe"’(Ó$êsîÆÃ*8Åwn¶Ý#)ƚ{þßMå7)ã]ݙ«KÜRµø¸ÙU}Ô'”L(ý×ÇnÖmhÎgŒô.tõp•W–Fdš*{3‡4ìƺ°Ê¢•VO*ô0§•ö¬”ãJ0êm 2Ùb2M#œ#.³Õݺ”»ÕCð>¬fA¥îÆm:8'¼åŸlLb¿Q;v;±ø»9O¹ŠûÙ}¢(Oª¬š9ÁÕåüÈ’`8C‚%·Ü6@øг-d©Xjµø;åVD§¦Í5ƒ¶uêãÃà³À×BäÓjcN¼*#.rÐD¥«	es1CÌ&bhؙ¶Í´o¦‡Äh¤–Çç×~\(v[y°ÎÕ®f`óςw•uÖLƒCµfŽRÆeÚ	`ûËBÖåˆË´–'g—Å­¯¨ÌÁ,]õ‰±¡WQp#/ëo%*7ð¦ÉÀ›#.ù(¡4ñ"ˆr1ÙËMx§‚È}ÊBLU…Æt™Xa'9œÀAP˜¥Õð¬¿]=xâC³M¡Ûs£0‘#)ÂÈ˟w€i”Þ^$chBœ¹kÃf¨ŸÀ”n›±ªÍ½Ze«Nô¹ÚÄNò0ʼ¸BP҂ªËP])ÒP·ݚ¥‰Áµ7‰€×ÚÄbùdIi5:¹‹Kƒ¼£¦!úkÒƱ™ƒdïv‚ýJ1A¨¬òFpMƒmsº|¹ø¢œ$3$9AÄ9$Ìyº¡ŠXݖÆÄæá›ãi¬køl<ÑÑ~S$p5½ÔýaŠäðušìtÄ#/¾k2›ùõ=Sa{ùÆ0͔7–Ýœã{ãßÀuCÞéכÔ?'€Ø’’ªNÃƨ¿8ðTr€åRlpžêa”†ŒÇî‡×LàºlÜÉ{{a·ÒzŠ‡ØŠ“A漕§rŸ¯3;ø®Ý©6#/¦]\Ñð1†mGeŸ“5µ ¿$ɲ‡žç¯¾,݇[ñ±½!Û*®¨9l”­¥f*¼5ëËÏÝi-Zí“h›³$6à¹N‘fú³ÀØt°-×Ü[Á¢§«š`‘R•ˆQE«ƒPðf§^4¹éüì°â R_q®~b¼%’°¿:õٍZ(øÙ±tœ´·‹Æ—ÕÒÖ³uãÌUn¢ÅÝ^‚1†× ‰ZÍ-A¿N55ôœ›»L4ì8Ùä°f]ô:J•zk‡kv8[o®Ž–—Ɖ.%B†÷“Ö#ªkT¢€¥°¦ÓŸ~¿Ӝ¤5g^[ó8›”“:‚lʸtØõrœd›wf9ÊÝëF#/ۚ¤„NHz.Õ$Šª45p*¾€öÝ#.’]òVÚÚÕùZŸbogø&t›^Ž²<ý|xÇ#.¯íƒSJôeE1JI.9‡®lp#.Ðiš,€2’C¿ìûûQ¤4;§F´|àÅuíVþ'y½8¬è1Oø{Š™"DχËۛo[ûp-`{:*‡÷ˆ:@ïe·£3ž‹-'—·›œ&ƒýH9&ëã}\¨!QR'Ñ|•?€wþ\Px!ýÇß÷µ9cS{½^Éã}4v¬:«¯š¹SoÒàÅÓ¢`ƒxíSvÞ®íÍЭøý÷Ț~«›a&ÀÕbv©còɈ|˜]ŒðÃCt̂OÌZìNµÔ–~=~é¬gf³BsùïcùWÈd…ý¾òÏk€îJ!îî¿1ÇeÌôáXˆLŒU!.î(×52ïgj¢–lÇAÓ¢*T†"jx#."V5ùY/CyË]ÆWÔÚÄĔÁL‚.è×#.PÜt!‡”÷Të5ʞ¤³v’ýÇáƒ"¦üè•7881`sóq¬¬éóòˆSﶲ£)É÷¦—Æz?=µÍ*¾Ho8 ¡æÄûþ‡Tšyh7i†¦Ðq’Ì9ÈgûÏ.E9½‰&ÓêvÉ²=*ö­„.Ä94Ëhßúª$Ä&b¼4|IŽLúq„=³f…ù¿x\“êòûc½>bG„³ô'Ô!œ6#)¡9º}H†‡Ši±#.ûöÙj#|@Ýö#)Å:ԉ¥‚YÑ#2¬BiþX9|l]ƒŠbaÁ‘!‚@òÛ䉱¥•ŸœÈÒ(–Xatª	P¬\•öî%ÁÆv؋Bº#.’qÙ‹ƒ±†;*Ÿ¢9.Ȃ ƒV$VO¼ó·Ê¯×÷fûº6üXâËÜìeÎìÚÐ}9CBO R‡€“\µ¼—¦ù×S/@Zs5ŸZKê{×Baa2y&!g£ ØêÁ¡ù¿¦·7'õ0ìùhðzGeãèq’ŸN°ÒyÝKRlrž-<+ÄLÎj¦WPÊ£Êcj“£*߂0(ÿ¶x¬êù,ŠêWôÜG[²¤6Ž‹¾HÀâèË9T3¡´6{\=9rÛ%ìvb²×ÞdÞS`Ä*E‹)‡3DÖ±ÕÕm#.Þý/#/Q&¢3+U£m+=3*Ö/V¼3˜»ÂoÈ;¤tð7aŽy‚‡¼ð«`l…uÎ#/8!s‡E;ƒM13¡ÑX.6N¬Ì‹#."åËޅÕCvNᚩ­e…'û.é~57¢tÃ/?Ž¤<èq#»[ܵö˜r±$[»±˜q`_`„ŒûÚyûN½O÷(_qÞóV{Ü'Fq›n¦ák>®)	FDûëÉ­0ƒ^Ú¹kÞê]84l±aQY`†I$ðµÀ;FûØ0–YEJF()ET,ìÛËY9Ì3Ÿk5wær·±è§_Làv¯>NÚW­n/d´Ä.ÛL…€©¨çŽ8æ“;DˆÜ	ÔìÓJ+Ug#¤«”h#.‘¢s˜‘=*B@´;ù)—‚xr€Bànƒ©è™ûØÌ8Á[iÂjŽÝÃmÇ	^‡œ1#.’:äP÷œ¾v–:ñÀúŠm5@æÅ"àނVal:•Æ ×U#.akšk´„3tè7ì‹h˜§sÂHZÉÓ3ï°tBRÚ·è&}ö: ÁBb€¨2a1Ö1ÐÁ”Tv *LÅí+s¾ùZ"Uû¬ÎxËðÍy݈&Ôw{8€›€JóÖx4·y8ÍæÀ>¢8r0P#/ž(KËTŠrŒ”bU(¤P‰] L$aŸKY›l˜ˆbHB­¯—Ö·1¶Èe°‰">úæ&$ÑáORw°¦`ºÕ…ïÒòA#.hæòaàóÓ· te*6åj„ik’37yyB:0XÔõw¦L÷³å±9«.©ùéÆ>ý9[Äü\ףӉØmâü½¹G¡-PÒPL@4«rzIqð/;ä¦)c¾\ÃHqÁ¤ß ™žGÏ¢AWÀÈ¥H,]CpÅ¢&6<Ê°a¦ I5†HÅ[öD Öƒ•YäÏSwÈçIB:ÏЂmÆ-0ýxt#©#)àM´`9#/å@…,BR™*rAÿ œ`N(ç¯_QÐVØ»¡–¹‹g€id2AªØ!I3¸ùÛ{Òbl=#AD¡Û±'žvµ×9¤‰£2§0å{à믅ü¦*ëň!ÙJ²…´#.0 צa!¨†·=˜`b‹«ÕƒîMÛ°Ûp•×xC¯C—U¿˜ÒU=aä%@ @ØfPÃÝïñÛᅭ£ÇÀ9*‡V<Î٘~²„ðO¿¨k³0¨ºqãZž‡ˆd€ÀR|„£"`ßΎ‡\yŠì½‡&àÄ31	™°Õ¦½Ó ³2·–3¹è^H¢`Ìf­ƒŒ– ts›f0j,çž/½X¯.äQ÷ÙS(Šœzó'wŒF˜P5æ]²õl®[²á=w“s¤ÛˆBZR‰c)R], ^³cÈ xøæ–mR…LÐã&mÂY¸–aËkXxø¥q#½œ:64χ-¬åhG›	êTÃ_J#)¡$øсÁ)=|wâ;¬Ð÷“‚z:|gšVüÎÎ0îã:q}WØÛtèoí–i5È@ÞKê-í±ò˜Hª]ä{bÃGÇå	HIuÃQ¸ãꔳ+Ñ£ó4Û#.ÍÏÓ¥´¦ÂŽÎž´ +ºCÒÑåèÛsч5v=ú [¦aRӏšˆÝ.ŸÀ©½0ß?ËÁ¿¤qÆÝ8öžæûCþNâ¡Oy¼‹‘ܶ¨è«×)†×åÌݍÀ$̋„6A§,C~#/ƒ±†\@”@ìÏBþ™·–8Ëö‹TçvÄi¥®áó>{xÑ]›FÊH·MôyÑ"¯:¸¢#Hᄐ甄&ؙ¢Ü\ž‡êÙÅÇÉÓ³œÓF%@$àþÔI¯…@R" N{ü1!Î¥Ê8A:9+b¨è"ν½¼sŽæ#)«1ˆ$«R…!f&¼¤2D´œ=¡%7y,­GµÚCäÉæMP³öÐçvÈ*óúa{¹·¾XìS§p™†>=Õ¸øfÃ<¤><)ڊ$I…P¢aÝ6ðq i¡&BÍ­Àp}|ô^½½øK)[S£y‰—ûþ¸÷UXÉòû¡,Öÿ>ž:ÎïŒwçŽQsâ"ÇZÖ.ó’|ôö¾ž¾…¶Ù;j¨¯q½‰Jé ¨m»¤I1{«ªÒp8à©ðBÁª¡(Ø%¶w+Ø«îõKf»(á&wÜ)šA6¾WgÐïÉ3´Ô¸§âHJÄì¿"zúÔcæ'Kðœõòå³ùmïmÄßå6LcÉ̞™ÊÍ>(Ցb1]JÒ¨7M!²m~Ñvpøo44qãCM‚6åKv„ñ/w±VÆ3Åü<~økj;à8¡™¯-‡Ÿolˆ† Ïr_Î`‡ŽþEâ«ÜSƒCt4>?‘P=IÈ_F•M!5HªÈÌvéZ&)lÊ«K©Ç]”!ç2›lE0íݟF0qäæ>µÓv}œmgnÚèûÛlxk#.*̌uðàaﱚƒïJJb--,àZÌøý‰°4ºm3³1ҁ¼&íØty5·O9´[Âײ°·i†À¼Ø©ðL̲:ñ%$Äf³“yø:Ø!C;5ñ‡*%§#.»ãvóÉ¦©ëbÐcp˜m-ç<s‘ àìb5&ÞªCÁ„ó"hɲsܝЖDƒwçb0û4ó†c0e!pÚn)°ClC!½×uß%¼ZE4±hô×U%˜“Jf]^øm_;/O™ä.»—L—¨Øš7ˆq•¢‘ìh”ÓX¸Ü7¶æ$üâG—«Ël:ŸRœ^ûgC¿úæÓEEèÃíÌօ­Ø`ÆcÞkÐ@ÜæªüûFMdaတܺpgGó/3`T ùúô*@ºú{QÉ¥…Û!øM.Qyƒ3ÙÏ=c¢Wç~’†Ê¦ÞYµ¨LlLnÂL,S†}Ö]¶û®c5Öö#)@Àáö·#)Å5}]4~;·àû ýr4Šlx§ ìH‡J#.¢ÄQ„cœŒœ˜‰ïŸíÓØ$—Ð&ý?)`ZQÅoÅ¢gŸÙ£O‹¡™˜*ÈÞìÜè*LćφkßÚªŸ‚¨wñáé\yž9Ї~H	ûtȺ#­Ë¼ôÇÝåå噙”¥ˆÔÍPÂñ_)äÅÐ>ÐdÏR6ïcßnÛ[ÒY½ÿzS±û“3ôb©–½ðuÑóí#)b†@ºÓA<8«w«@S#)bˆ±™˜5hö¿·ìÝ!×gÔ§*¤™€Wù? zGÈR’©Vûc´oÓèòêÂ,®°ÐÚÓh½}]’(lQòš_ö°èk2pÆÖIk¿ãL¬òÞ_w»‚¬kùý¼\éÚf’,¹óL4¯òŸœ_™>í8k"Ýr›0ͽö(f™½‡ò_Ÿùþ¿Ûùÿb_ÎSJˆÿHœ§B–P…#/ÈgœW;'dkœ¡}òü×WuÕE'ùål‡º—Æ|ÚMAýƒ3$Ã0ßíU	M¼þŽ(l6ùš×#)áí~þœ·úK`%N؝îp¥ÜuvèlOù…úÿ#/ÇaYNgbgÌèýï×Kìä¢ñ”Aü?'Ž±÷ù:Ă!3sõwsîðëãYÉMxÍ"ÁHc‡vngôIÙ&B?ïÆFA„1%ddP§FS?¿%6¥HWó{‹ãýwŒ~Ÿ´Ú$ǬLÍïjßñ»ßL¾8dÏӊ”³qØY¢WÙ0„2>wn¸µÞ6º£˜GäµNy[q]Ùe‹ZO»ZÊk1fž¶£~uÜÛÇØîõµÌd9ÒvèÇF»ú«—ú§Þ¬êz€åC°—“˜ð6ەåžò°qþ8y-²Œ3¦éÐùV-±×¿VÏ&AÙ30GÃÕbFÂ-wE’sùO…X¹Û‰ìu¾$†€!ãÓi¶º}Á»à㉠Ðrw–r™6L¦<ØvìǑbaë„­XbUkÅÑóøôû3'úŒÊëøqÀtäğ6„Ɗ`°VŒ@’aÆÛS#.ÃêŒOôêè疷ΝVòµ&§2(ùýŸ›ªË—i؆<[C½1ÅK‰?7óÐK¿ý?¶>0u\|1?¦xm”ÍÓHlöyŽï—í¿"؜¾Ì µš¡Ë]ìUÆ­¾È+¼ªWëñð/?	ºÿO?›÷VºN–‚mڐâÌÅcê„s®™ÛÜÝüýüǤØo#/ëñ†¬”½R444XLúD%k0ɝu¦Â1<0Äqk0ÏfsÁyÂÏÝøêx@;ïŽ8ÇìÿAJ#.qƒ+61	'SÒ^ßc3#)½oj½ÞÅúdNrt’L{XoÄ).Ý@v#.åê¦.ݯÔB*3î'T°µíó~¸“|RòyF‚Ø3½>f¬ý\ÚÃC5>vCk`‰xSǹáww'¨ï…JUW#.}=š:¸¾VKœ—Ÿ©Þzõž¬?·ø[]‰øÓ5ß³[úXJéÍÆ äøTsS½ô;º?^ÙK÷í)ñÊ*~ü-ôOΗð¢ë|§“~,œ>DZNA43Ì•Œ9ۊ÷ûüÌ?Ïk#.çÒû=|ìcgA\_T©7—’¨ê¤qÎz%Õe´¥W"C1ü*{*Vz«¤·üzÉg»Õ³‰Ýë=^¹úu+à粯aôéú¹·"x¸ŒÌ$ï®oG’ìÕ9FÉªëvv©#.¿{’gGî\×ÌÉ.rXÂ¿…ÿƒãçèÝYÐ^ç³WnRLS¯?'FO‘K!‚oÕ&>h:ïš`l?÷–¿xɆ- ˌÌ™¿?àåÞaI€šM;ӕ„ž#/ȑ"ˆHW}]uâu>¾[4êð׬+žÊ‚dȘ\Ã7Á®Û,1×®{M¤“°(››«ù–¸…á$ߣ³·Ñ7çÉ3¼_¯=2ìå_u`–™à˜}+5Á‚d§1ä¤CŒ6ªIQÞëªú«ß0æý÷?~L¼>¢+7¿·÷ÌÄô	À›”8¢4®uÝ)D¿Kòa^ÿ«ç‰œ	šl\!”#·‘=½™ÄÛ³¶-÷š”šO2¨èčçÜ]-Þ«®›¿ä‹#)¤oÎL1NêƒE•ÒéN	‚r‡”c{|bVãÓñsR‡Ç"½T¼`Õò0þљ~¼0¿}è¿f½›gŠ“©öün1/R™=þ\oáˆ¨@oqÓ¬Ï.L+õúÃ/_UƝø_4°”¥ØaN ‚á‚â1†wâ`ËK_Ëä¼çXzŸïúØÁúuóõÛÞÌ̤ãu\¼’7í7MÏB¾¯Õ~¼.÷#Ù§–@ÅÌ$Û¾(Ç$€y4§Óý7¸èÙ§e¼¸qãX1Jå™öf6&ºÍ™?3oÚ3KÓ®¬å£ŽZh&#/ãbý´üc³û¾Ÿ’_Ûüwɽ•3Ô	ŸÆïÍ*Ð(Ÿã©¾8¾*d­íŸÕøŸ<kÊËyžx×M5üHµSAö+X’Í‹Þ+Ìq›TRÑદ‡téIß%=’/úäÒêUQä°NΐµKþ4¸ËÜÿgI¾M¸ëN#[Š“²&æ?·y òg5/¯8Â<µŽHþÉéßõ§J³êƒ,å·gd!gß.(tǶço“?]nÉ߈hU¸‚åù†Âb§0öðވ’z=›­/\×¹1ƒKû_׏êÓzü²Ð–Ìíâ©R Ez?½ºB™H÷âå¿TtŸ(ŒZÂ_ëÓUaiåðAèS9“§£ÅùC¡¦>ò·sŸ_7í@Žâ‚:ôY#$<ÕÞg‰gÉÕR‡“/&¶ãüWŠØÉ÷”†‡ãP}Ê«iµ-R'M°Ú,”RŠîÿ2æ«ÓYû1->}TÂÜqKËÞÉQ{,ðˆ‡æ­‚%­ÂfÊ%LÁéûÝ&á±+9¤~[>…+ƒ_óO¿­¢Ɣ3VÚ4?Âÿr:Äryá‚z‹µí,þèfzuîºL‰zà}ۈ‡>í OdëñD-Üt¾.§½e—G<qÄpù:¾¼øéƒ%´hæ)VT“æ÷R8[¦3Ã#/rrÍøÄ& Œ®¸Žê=º;¢SºšéŠŠõ÷æè*Ý%ùÃÃ?~㼯Š#¸çgK®n}pW̬ù\Á䞺™ÎïW1‰EKyÝ%;ò‹Bë×Yív­N£A‡…OØ\Èêxƒñší¡„|‘Áœ•w-EDWN"P[@ÉtwbÏ_<yYie>;LHHB¹vøö€ñUì/)Ne`‡´·T›F™ÙÂҏÃnXVŸ[Æ#שà܇œÛDc<VýœM5)íñèΓ0Ý#)¼®xèz5Cê_·SËᙑ?[J£'N™Ó¡0ãZc»<ÛüÙ¬øwáÂRBŒþ0SQÿSåqW©¾­tº÷;µIÛëã+ÈÕOgêžöÎZyz‡˜üØ^-ûæs–ˤëë¿aøËOÄôð4^·Æd‘’nzN~ŒËçê§NLT÷‘Û]ýªq,»ÏDU"ÄÍ=~ˆÏG¼u’yÀ÷¶Ã¿Åñå‰G㋼YìF2î¦IqDçɧØÞó°'	ž£ß÷¦Ž>èJ=4uu¤8‘…rÅr®Í;[̟	ðç÷slÒÛÊѮڢƒžŸtø›ÿ$?™!çÎsNÌy3ÊÒú˜ö0N’·¬'ïæ^RX^‹¦Ùý8¨1Ñ?EÉûzÃdüpý#.T_OÒ·.L¦u—»Î®+	Ù4¹—R‡GÇ£ím3”Ÿ²?ܚGχ§Ã›@‘Òâ¥Çú®¿3Ó;öGvËüÑ#/@—Ö‡ð‹ ž}9Ö¶/S×zܞ…»éøYf9#qÍ«¹ÑiUJ	DÉht¶þX¸¸†<Ê—é“éT¾€|Îú\kÕݒñõÍÔ¶ß:̘‡Œ9àƒšôòkâ|ß+éno¥qº¦ùaüaòØljm4õ/d"U#TãBŠˆvM±=5™kœšh?µàGjÚƛ	ð$í6¨hÝ«(Rì5êšE»æô`‘Va~(iñàõ*“ m¨ïªPö¾)Óa]‘$QY3—}*mšN§E¼N˜Ð©®¿TB›á!èH(—m—:úü¿¸Æ·ž….ڟA¶Ò³5íg™O‹®1ç®aj•†QåµÜí›ý\ÃÔ`¾Wºû¸fùkp'¹ÜÜ¥	Ëçƒ]".‚Tý‹.¦$[#fÿBƒ,‡Ñ’Ó±3Ö¸kÝp–ÚNÁöò¬d¢Xwó’<õ«iÓ% †ãçù”láô㿂$žz9À»=G­ú?Z#.ò€½íÁàr¿·ß#/Ì	¶~w	#bµ]W—D¥´v¡óëTÇKµÇ6wi÷0jȂ³»ãl÷\/®¡ÇçñÅV4!F#.Åãz˜|û›øI·ã9§Ò"ÉTZ!Ÿœ˜ãP$Êt&®Î‹ü`½eu»lcØLg9xф4'C£ò@Z=QܐÓV»öQ¿Ê·fRvžÜyã\ÐR’¡\gә2¡xåq/1d§'؉Å\›ªÈ©è»Ç£‘Uf7Z–Rv£¼isJÒîÃ&#/ܗxWJÌ=õ­‡âïÏßgńêɆswwgwÖžQۏ‹YGÕ¡y2ŒJ1FfXœI)Lè1¤`MÇÚ¹‰}œåo«²®²p”d½Ûûº·Y’ã¤#/Ð,ÇJÒçø1@4qÌ#/#.Œ„r#.j£;àZUÔìCùd„^CÑ	)Ø/oðJçü³›§qW×ø×MÞ…ƏÁâkûúv	äZŒŽQ)÷øü¦xF³=ty]`¨‰É2Ä"JÛʬ2v‹Ã;Ž™LÑk^_7{ò„­Àƒ[30}Uh×ùOo—m›_ÕK~ôô.W!Ó#)²œ»è釕*{£»°È@Àoý‡ØH·óý?¨·qOI:üi:Î$?ˆãG”þ̓öÕ<XÓ³3TÏ PÇã“ GŽv Ì¿oŒ¼ŸBb;äԓÊr‚I†û‘Ѓû¤TE?s$	1æǜ<Áß)>®)*¤$̶°õ´A~TQ•[å™èz‰ÛQŠ¬žRòˆØH:Ë5¥C#)ÜSD¯Q[!KÉþϋgýæûu¡-7µrE>‡ù:µÒÜ5cêòДú;FóFa´+‹¯´®VЂ2±›Î¨Hv_8¢@@«ˑ‰!˚Ÿ¥ÖWXtõúÏÕ]á§OÆCwg³D¦áš¤“ ’?¨ny¡µËÎömlGæZ/ÖÁíöŸãsmôšªÈù¾XàýtùûæØÌ*Gí‘å+Òv·—Í)˜}_³EºNäՙõ#/—5j<ôhü‹)ëEÓ¶•vi­×í7Õ[‚*Ap™³ô±Ýó°ÏØE1¡ÂbbÈÓ+,bRF[ŸNmîæüÊÒI“©Ã#.#.ÿه濽—ÉÛðú˺xy0“8DØûÇñj~4óå_Í¥­)`d©9UYëÓþ'Éz¬¨5pû¥‚ØýõÉ(,Iˆ?z)óüô"KÈys”<ǁ̃øbW·‹ŸÂ˜ºÈ’hޏ©ô/kܹj“1ÑI’”“3}‡)Uî^u{ ºunë—d]¤³w­Ö1l­šfdjD—Q÷ė¼—÷~}·é¥;¥ñKNgnã#.Q¹Ø\_œ“‚—;‘'UM‡‡O@鎴6>¯:T’n´VQØ·#±°Š1òùßù|.ôá;•›îlúÅѾ–bPww᪐)]^枽8“HTzzg>†½u61T*#/œ§	ñ¨¬¥¥y¿ÄJ¥làHÆCÜ6¡ÓÌB Tˆ1X’Cˆ²°Œ,F(k?ãáÏ&Š~y*d<ËÆÌQëBk¼ú‘X†Ý¬1²·¯šØ†•oáPëÉ.ˆ•nâ{ÏÕ½®Á¤]Û¶•Ý¼²¾•s5¶p±‚›;›Èg,]Žã|¥~¿6E$#.3„ç‡xäáÉ>Ÿ·ôé‚Ãö£ðZë{n9°èƒ¤O’’†fì`3¬ßu˜K¬`Ӎ‡/͜‘Ñ>[xHÞhŒ½ ¡ÜüáÐZ‚þpYòW4ۆJËå³2ƒmtD„¤¹ö\“®áÊ3]:œÐõfEä#+M¿‡ò—eÝ>Œ{<·„Ãwn‰™È‘jOÚ[ǪÉØ|WGEüæó«€AØÛ£äéÛÏ͸ÆÂYJ’¦›©²vXk®–7\ûª	åVÊôwY«˵æ$%ØËÜîs̈́éü¸®¯ø3͔Íj€O]Hx2Ðòí«>뚺¥qª&ýVmŠú}ô¡9ÜeÆÓ½iéz™ˆB^¾Nu.¬/†8võGäüÎ^yJã	Q?õ{=!íß½7ʄv§OÕŸäýÒxȍ"¨&{zç:xè^«áα쟐ˆYρ¼ýN¹×ó]5ҌﭱeóûuXŸ¼DM´5Ҕüq"WV2>Eýé¨5hy&.3-ÃÉSzgê,G®ÌêèßI:¶ûWjRx'Ž<±Çó#ӀßsåUíÒE{óδ¬eQ•ýü&éÂoèÑÊìàé’ÛÅ2‚G×:‰ÉíLÞHã”çFi™û5ZÞZgÑ®r#.h9{|Op±Ðk³[éÒji²œWœ—¡0„uJÓ«MYí¤ù”ݍ÷D2`x+TÅcÅLü~û5íڔÛÆù\òêðGœ“=ÙÄÊ&ç*Ü÷k¸×+™bH¯*Jÿ™ØÑ[•qNd­ÁeB¾¯ÚæŒ3=}o\èaS잎Ìl¶ºƒVË+›jÚªáUÂ_ºQÏ%Sj•wjÅ®Ó,3öî?ÊNÜ|WÝ<vÌ*—}`ñYó·ˆÆ¯3o£Ñu	x‡‡Ã:tFª;Yü{0@†Qåž#wSºð÷,çô5T˜Œ>N’ß?DnÕç\n%éÜÛÎÔ´¦îɉ̙¯áFŒô¨0œUrO͆8ÒÉkQmņÈÅéNýºb+¾XY</Õ#.{óÜòF·ªÜTFX‰ŽU‰:ô#”m`x”psÕ¾õlzmºèãÂ4þÞ§tGt´ZϺp£aùºE烗MSN‚7]°3ÍúkK‚n=ä}fÍDJKºH르1xRm‚dˆڏÅ2Ñ_¿q¦*Hâ”Ô)ŒªS]‡7ò~¿™¤Ø‡¯ûOÔk˜½xöñ5@sW¤‡ñÜF–*ã¿zýö5öŽÄm§“„ºȤ,¨„•]–K›¥éz#/‹Ü’B@›N>úpóIĀíÚúÙ©I¼Óoc†PÏ#/KnÊÞ¸îq/t¾¿ÜV;Q³ÏŽmbƒqΒmŠN;$ÛQô`ø(s¹¹0F¡‰$ŽÝŠ¬)õÖz‹¶+µ3öHoõ§ŸÛ n3ø®5½£#ßӕÌÆUhΒÊÂŒ÷ÐQÀø½—ggäúʵȾû·æÁTÉ1Ü ôâc¤Õ[ÖþJð7l§º³χËêэjÖV—]ÔÏ=0b*k5Šœ¨EÇÉì÷XZÅÉ[Í”;7b+O/³4Ú5ÐäŽç†%F²¦óhgöàã+uL“Y"ä¤ê4’×ж­z“OEÍíUJºàv>uwÁã§9&Ö)Îà„åÎÄx;¤›Sº;Í8èk#)°ò_PyõÙi楗ipm®ã ݇×IJae¤ðÕÒl—ŒL- #/ܲ§}‘½"ôÖDɵm¤–åáÂï¦Xµ–š~7-·z{I³xw])¸b.y…M•0«.º·½öð+ê7Ç}T£WFa©öRî–VŸƒ¶+µê…¯>†óñ›mM;´ÒÄ­!»VîXíÏ;êžèÙq•¨s#.,iaœûÿ½:ðûUL~}ÐS]ûVi*Ês»žû)ÑÏt`ÖurÏG-jæ4ŠÙ\m}Tý¸_©j&ÕÚÉs—!3Ã"•âÐFD& K¯!Wøm`᥅«MxäYJNew­øQW²ªBØ?g;°Ú§äEÛtt<‡wÞé‹pgÑ>48\)6eªÂQ¡ý†ZMüW#.u6þ®ùJ½; %¸½©-ó5!õ&Lorv#/©&Éo2§X·—E¶éÝÄ:g›ÌÔF>lª¸<™[±˜ìcSuJŒR6ÉP¼Ç·8ËqGR¹@dN›òÏÃÄüòϞßßpçQÍ Í7¬Èws*¾ÈÏ¡†¥¾éÛ¨ä‰qø_‡Úcê}ùɈ­ÉìNTéo,»A€NF†ðŒê¥$ª7DxµQP“Ðv©Jà¼ÀbTò”ªN¢îû6 én¾ÐÛ[üU:ð·£Ê»âºœ/Òìïuäèl›Ç2‚Ío¾.ª»¥"EThV!!àp£Ð#«uôÜ»iÈÑÙª~­&—³H95á®6×Ó"~„AÕMÑײ±õ½BYá”KÛÂèÞ<Šägd¦òŠå“2YãÐRÌaÝ]…œ:Ï5ïN¡j¼â4&µ6¢+5å*zl:õL»LîÑ]÷H0¼åœ}kñÓ¶óÏNîÇÖýPØxÆ+#.ÕÎF[AƒåEpô¬@ûؘûŽ’qÈ/ÓË<^þ†“mŽ‡Ÿhh7†Üë:0QzzyÉ݆jNÙèuáˆì#)_ë»ñǖ(ýÖC®ŽÊwE¦û&9ôÁ\2‹œ¾›Ï҈=ç¿Ë«œ³C!ï ³‹]n´¥RcZŸ”å«ÛÜǕw^î.Æ_fiÕB;˜â¢Sþaœþû<ξ½Ã&‚öxqü›Å‘æå_Þ=+È«UD®'~z/¡½iG¡ÃS³t&(Mø—…µðÈÒº5啙—ãG#7ւäÉ£M›˜$ی(_áu-Æ»\Ê%ª¥ÉCêÚäpÖÀ‰$ T}G5ÈM~‚)€]օà~;L#_»Užý²SðßMë͎¾Eùáåٍ-Ó¤Ñn#/1Š¸MiÙµṲ̀]œŠçÝY#.ñq~X_v¡_»YçžRùg#.ëªú1Äe}µ>Y8oϚg†Üt9_—FT·TôrÒLúìc©1sH²õ~/áÒRP;»ŒÍž;dÇg%+¿.7I]üožÌ]=ì	þ—±„ |õkÙSÙ£AŒ˜V<4˞þ–1 ­ßŽÜ,ªÆ5qáNó}j“»nM´eª×¦[1é6=0MRfŽtYdŽ…*b€K—ôüÛ7~o“ä>?!ñýžc’:ØêÁ'ü$¬úDµÁ”ßü^šuãA÷#é ûnB½Ýrô³y:ì†É9ç~è~æówd’I•©“¬ÛJ0+ꢪ+=.)˜|}£Nž>Ž‹pqaq„¯[NpI~–ìÏ)H\6ótÀ¥>‹‡8•ê}šo*ןÅalô==ß¹û<Øxrý’¯?¡O›gñlmÜÖË"îx<‚ËFýFcêXãŠÓ‘0`VØ×\CHþ§kå¿ð}"ŝçTÿY‚a¼ÝR÷H`eÉØcc¸ÃÈpc)ÌÑç+¯ý¢]žïêÁá=ٗ¿òwýjð_±ƒô„#.ò³%A€a¯ª@éÃçôöñ$“ü¯÷Ûa4#)ìæ¯Æê ëbž¤;Rz…4Xiƒ˜w’,/ö¿Öjaä8ÿ™“þsšË~ü‰w$Ò7Ø@†ŌȡþȁÃä#/5€ÄÆ©©»`2%~®#)1`ÀÅLZ@2CÙÔñ#™Ï’W²Í©õž("`ÓiÚy0UîÅõ$÷{P³:’‡‹É"‡bv`–˜l þÏЊzGkæAô¾Ju{_ zÌ~/·âÁ~Gçy(#œ|‘:£”§ø “Öˆbf{ÎC`¹*h˜1ƒñÃÿX8*¼yg@ÓüÕπ*|ˆvÛ¤Dyüß»©ä›œÓÞؾ#¸/ópô‰üiÌöÅå³pöwë*OK=‘ñÑV®ÿ›ËOQ`Äf‰Vw§ÐÁ®…iú—Z4ÇA#.k&æa®s”HÂuxgï6aրá¸æü\P¤)Ÿ›#/f©©ÔÍ£A_Ûýgñû¨sý*?“<Lë›F¾ˆWÙ)m;ök//Ùw°ˆ^IQ†ˆ Çè?6áúͦ³0@aJ’’;YþŽýÅ;Žpwä^T('ýƒH(3¶@›ÈÁØÿ#)ÏgØ`'>î¯&¸Ê-b2'vkCƒ	¯`$?BÉHhiaÛ#/›ûâO#.C<ªh®*‡Ç[‰¿ûÏ Òóy⟇}aäMû±1áï©UZvTÂ"ød÷ Ä<@ð	Ô7ˆÛC®1ÜѼÆgžá-v±nñˆyÛ´ÃÉæ¯À莤¢j‚9#)sš0$ ÈcXv®þñì}&À½´qL#.ÁÒAõŸ[®"L>!ã±Á<ÀéĚæ9ØÅØc)ÃÚa¶I::ë#/Ó+z'iøAú’È€üþßfÙ°KùLËú¾^ͽÞÅ£'àdÚ«ÞdnË¢uñì2QæVì0RRH­ÔE¶Õþ¶†€oÈþÄ–…ˆKðâTš1óÿ,.Éd¬†Ò$ˆ{ZBͱó7ÃÊå‡Mx¤¥ýz®Yùμäk;¬t–vb)Š C©%îàÚeLøÊÏûúñôVY}¬¢Óáiue„²Â’u§†§.L̒C{WŸßäžsۉŠÆçéòû@ùٟCGÜA±ÒWñ7Ø°nµÞ|çJÏmY²4½"ƒ•*T•nӗ¡:®œì"C…ÈLiƒ§åß9 ¥‡ÅCÖÍøMìˆo#.!TQ#§OªãíwaÜI’¬mm0ôœ!ÜáïΤL—©ÂŸÖ¾ã·ÁâuÛ±™ˆv~ǵpN¸{qï0ŒÈœCšýljÛâ	ꛆã;ÌÏ÷[#.æµ2\œŽ‚uÿ¤“ª’ªzˆ§tœ¡O‚æ”ü®æ’N‰6õ–t•Eg7né$°ïšwË¿†ÍìÔa°¾s†Ã>ÌðM¦™w6NÅ»K2mYºöð3œâŋ;õÚxé×CTæQË@Õ'9Ï3RÆžMBÈ s£cyü)×Mæ<IIOÖ|§¹*!IGù̊ô÷@	Èýïø|üó>£Ë+'çQ'ÍþÆ\`–T0¥ÑGÜnãû´†P#.ÛÈr¦¾‚˜F÷Ýq›ã:¾‹bñ´"jî‰Ãò™Ù}ZV¢ÏÜ´€ é’X²H=ŸßÍçû‹¿+ŒújbæEdPjÐ„Mü,í#.ë¹;çšÈ–Ä?ÚxšS~3þlo‚RšoPä7ã~#/‹¢àvB¦*ÀKÚOè4áÜ#ÕÖÒ#ÚöF}þç‚ñ$8‡#aæÇäÎ?§ôÿ³Å¯Ð£»ØHI˜‘cBxúë;ûÜ<†àw¡è<ŽÈè‹C°m°6=¼Ò¨nŠ›Î`ý!SØ`9H§:w–X>34#.TßêqÛ&š3Â$å‘d	©˜S@"ùsSrñÎ'š.æãäx>^³>Áâ]%û‡ÑîÞ¢bœŠ+«õùîww„q#/ÚÒän`lÚ#.ú8¶Ö­¦oP=K2[˜à†övÁ,Å2hB¡{¦&Ì}Ò6Æ,¦àÿ#.!ØB‡?§¯÷œ¡•(û2ú8Ž@»@W"MܲK4æü95’i6‹uÍwÖs¼WZšOzDz=Å®öÓq»ÖWFVì|R°cÓ&›ââMqR­ñ2©˜eÔÐÏæÑrhm2ènØöfh™åj)YD"“¦+±à-8R|J7T\v–¹Ê˜´e¤ØBj1±k—6ÛPqñáö~$|ò6CH#)þè_Ïû@iӏDþÆvýl§»¸_§›3·ÕÕcw[PÏò·×Üý™úY¤˜ÜϨØ×HOß8†ðî¯ÖÈ*ª«t:HIrA6	I'g6ïîcÖÇw±ƒ'l²O0¨ý$|}†¢×–Ð^¢¢•5ìêCã»?…þ™??­.9±ýŠÙàÚäÚE¯Z+Wq”åWÑü÷y8ÆpSÍû\qÄ]àõuwtà#.K¼Ÿ)çÙô"&,Ç`æÌڈ(¸ÝE­ìÜN®¼²Ò¦.³%#5™{on¢Ã¤LÂ1øeXo›Ôz§ÐtLblÇ@~qœróCsP!Œ°p•ù•}þT̨D7Ü¿¹ÐÜÉ#‰JIC!˜¡ømÛá£Ãñ@}÷qÃÌÝíå™èd×|U“*ucùMÃÛv¸ÈB0ùrd_x|`!¬ÛÉ*\¸éôßqÿ&¦üç¨4%!Œû³ã¿¨Óõ“2ùNEG\›íË‘sÝ7)Ňà!I$萅“.˜Hœ´#)}BÎ#.Ih~cžºX}à×Ö7Þyô#.֘Ä>ŽŸA¤Ò$Ìq%‚¸KæIÜ°‚Cy‰öƒ¼#.¥Ùª…€Ù÷ÒTÐ6¾Ê#žáô?,C·päƒÌÿ†à?˜‘ü}]ºø{¿.‘á20ËÊÝǤ#.ÆKí${‡5">'<sŸÀûzrÌH»ÓVï¡ÛÌy²©éTkZ ìoH€iú°Dp•DØêø˜>có÷ê`éCtýMè'çKŋ³˜Ú[¸g A£Æ°"a‰Ieaý¾!AüooGÈë?Gr{ÓüLÎ5A¿gÔޚüâµê a®ý°GÌÁØ]w>3!´‡­õ"zIt{#)î}žÁ·¸®C|AÊ™™™º2Õ±óäœÍ›`Ï1šs¤®íØÀT› 4ñ\3¡'_¸Èè½-…잦͎Aˆh<©¢#/(b¨¡ä/NFŸBŸæ=²{_™ÀÁ«ß£Î‡±<Ï»õò;ï#)8†°gà>›Í1#$>/|}?!࡙?¤Dµí=ÔÞTµ~Pp÷¤„Èú=/é7ZUYßfi&;C€|2“o·g¥0Þ~G?’U§©˜¬â½goP~cš™Ä_­N´]™#/9üŠ¾ÁvTO€©C¿ú5Í[ðTäé#)†1Ó÷ý#)ß óq#)nanr𕥼gxòñõë}Ô.5õµ®™É99Š“g‰òƒtAmEè詃Ì#)põö`ÃXL›3TþéÒy†NÿÈ53ÞúìWùÔ}gj1ÑO×d”$Ô#.¤!™»ƒ]H¬j¨3;q«Èkø¿:MõŒã$ÕXF’%²ŽƒÂn@Š¤é#"ô¯NcÁþ‰Š˜kŸL–ÂÉÍq4©TN9ã#.QC +===òˆ	˜fz”AŒˆEQ1!ƒ®q’ãÊñ#&Ë9«#.gt„ æSÀ×¼9O„7íèe	`Näoá=ž7ÝÙû¼¤(Ï«¹ñ=,`ÂD¡‚Àý‡iˆŸVÕ.ÕVU”]ms]˜ÍИèný/°ên@q*ÍFàlÍ3v­*‡´#)õAéèdzö§yì`x£ÜN–9z|í›}ƒmùêîÖΫå!#)òb’“3&“¤¾k?²Oó0‹%Sþ[»ŒO÷î]X©Æ,‡ ÃÍ8Ù©LÑ‘åy/W˜i•OSˆPÏÌ8¬M0·—Ÿ^Íë¤Þ–ÕÏø7	²žƒÃž¼Ÿ¨"Ã3é噚â³MÞDÇ?Ü1wk`͉aÉ5ÝÉÀc´€ŸœÄuõ_ä:Î¥O=EM#.DPâÙ+„x§æcóî‹Á:¶OAèÅ<ÇåÍ[H̬B4XXAÉ·–FÉ:_í“{n«ÞQù› fâ4&s•†blÈ<†žù„’#ÈõȏÍV3ëCM¹­1úlÙÈ1äü#.f–1AØrÙšis¤™8ãæPDfvèÂAA1	4&õ£a¦ƒÖ,WZ´ƒ´|ÜF.=CúHcÐvqüqòÐlÀ/2Z8²éd+˜l±ƒ´	Jûäy¢D…GiBN!ÐA8žô?‚(À„<“ ±D´›æ¶'ž®öEbhjŽ<¦ÿ¯šT*cˆ`tK’v*â˜Bõ1¥<1ÊCðø视ò‰ùln\(*w‹ä#)á&|ÿˆo£Y§§ãÅÑ؅=¡à¢&îÌãp‰¢©K¡z4d>Û8×LŒ’EdÊfc/‡yWÃ×k;µrם¸Ú«˜ј“*fՀ6Œ2L¤i¤Øÿ#)æM¤:}]~ºåɖîß( #.H$Óê¬;Û°éô Pdç€ÔÄÈCõŠh~c·¥±EŒZ VÙì=Õ³käÁô:¯C­uÃ0¢šR†$#)ä±UO¶l#.$1ž½C–sÉ"ŒÕ3¹¤àm~ìF­\ôÇU6Õ¤fŇd þT\^!à=c”<];7£±þôa€Y¾Hn#/#.«Kk`¸Äjm#)E–¤Ü˜ð›#.´™ÍWXû奖ƒ{3i€¸Ø:¡[$؉î½þŸ\ÂyùÙ­@)‡\¯´Ö8FäòD¾Yµh­£5ŽTÕHBªÇìÿˆ‹Ú˜æql^„ÙÌã“uÃAöÎpÆÅÏõðiùú¾IV„aèt#.A¡#.¦a$#$’ŽZ6ÙEîÏyš“ïü Ðjo½F§O3‰!u†ne0(ÆKÂ*QA¡	ÚºŸn”P2'žwé#.Ɓ¨ë¦¡3¯‡÷CµŠ'!L‘¤C¦ù"$?0(ŸòÇo—ÏùIúvž&"/SÓ­äç™èL@d™òãàƒpe²=aQ¿‡äþ‰ä¯Ë»ûbëÇaÅ=®€ŒîO§P’g}ÑÕ4öÚI"\LÉ9¶Ä›}Œ›ÈRkÖ{ãðž6ûBU0Oä`oßñÏèÈhÈ(؛©ü?~vaÉþ#çÀ‡3:›y]âUÅށÈQŒ0ò;pëN@‡,ŠÍ8?àý<ß(o¨Ã?<=ííônkDa%Æ a#2JÅö²%jJÍÈiË¥P¿©ú¡-öÖðôAò=?xaì=cí=G#Gޏxž#/¸*bÂÒ'ˆ»ŒŽø°Ó´7v=œÃµ61æâžõ^Cû8­;òǏg‘Ú'hB¾GbžAú]ÍÀIGB¯¯óh6RMÄ«K#.ϰț1¤:î¬mLͤ5íÁ(VéHP,!0ÕrC$p<x:,õÙRx`çaËÖ¥	]x—·¬ñºÄQV3&†€îlxyùtºÃ¿â螥èÔï#/#/å$»òÐƍ#)“:dØöp>î[èP²ÉY#.ðrzÔÏjZ¤©ŠGZV…úp„ï£#/“b¼^¿‡;HÁ£ltwD³e¤Qøþxª¡w¶Aš!	t˜E¦5s~x™uNm£Y̝×úâ?½”l²}pîÚÂ"ûçM\Ï	CÊS™¦©v¦Îk%eQðöîËóö‡‘ÕèÑæ:/ û‘z‹½Ù2”A^-QÈ<#)úv“[]z”&áŸCÖ{_G{Ô«ð z€ßxóTÅ/׎¡åSýÍt¥W=‡	rÂÈ2ƒ°{Àì^_¿Ómwjâ>‚<U(ô‰ä¾k°â±Æ¢D¸³iÔC°Ãš$ãâ}]@rlÆmÈ#.ÀùšðÌ33@¤ªÉtÉaVd9OW—C§lóÜñ›A’hã°6 ùŽ¦-)lÓ0­c›¶1ÍVÞ¾äN1Úy'÷¬_r³ñ#.ÌîûM„Lz5”Š\»ðH/·cž ¼‘€>F=þ¡oêèåÂgò˜Ö3Q‹[À¥Ý¿Ô¨fz†ån,ÄWغ«?¶W ™òՁ#)ö~۟è”êò±³^Âzø&æV|ýSó©P÷ýúÜÁ¦¬‚g4_ìûò~Hï)Øç}&Í_¼U=rþk/8ê¥}åS¤ÇÄ-ÎÔ[³v@&)†Ž¨®Zô1קçS…®žkªÈ¿¨Àï­s’Œ@¸,cdÑ\T@Bә§nt~Í°ÇÖ#.L#¨3‡.èb~髙=ÎãÈcHh<Ï E1?$ùÒ=G‰l™Šþh‡‡d 3	Åõ‡˜#.€#)ô‡P÷/‚ô?‚8ø\E¡´:}W°õޔ#/x?—é¼²|z’áï&çdÓœÙ=5ì˜ØBC3þ~§¼	Ä¿åEo< }¾A€Q“!e™·%–Ðìç;&`N¤È‚{Ž h4†Eø}_#ÙôÖ¶ØE¾†¡¦&”6ö¦„([¼Õ·Ü½W—³zúAWi„;'p>j^†rä!AæóaœL7-íci«þTY8"éÏ97Û.ùÆWÕîàü=§üì§éÖS…sO„çˏMl p|0IÑ˕ îWbVš ¨!¢h=±{¨‹(òÅQñHæaÛ_oÖŽ”s	âfꓳU÷}p„2¶nÐX8‰² D~DÐ@ôûPôDGÈüó¼Z#)]äBek6Þè#)Ä/<k¼Œ9öô9Õìè=L”gÃnÑèz¡:ØFŒ×ý‹ï>ÿôwEæU،~4$ê”Fvb킬ÁMÓµ…dD÷ýîœÍ τ4Īðg4ÄoÀ¹[»þM«lOoŒþò#))0]5ß÷êC+P’f#.­£NfEPï˜m8É'V&âÊÿ%&bEa Ö)eÔ3jÓ©ÝÝßp&c»æ˜ãæ&'ÔПî®W[#ó©Áå[*¼jÈqœd6'9yÚ_γ1óç$¿Œ ғc¿ÃõµE<*@±0~Àß>¯4ƒò~Ä8€C'‡nóy÷вŒòE2=m½†h8”ÌmÙ÷[¡Ihy~Mƒ³ÔÃ#)‘_#Ž~Ïeô?§Zû[«ü}QٍÌT“¡÷ÏGÐ>“%ËÛ£INØáääÀ>è1ÙÅìÖ鳆AƒähÅìöš4ÃE"I¢†ò0Òí¾ žôQþœçþ8èè-‚bÍ“^ÜÅ®–	5`Öóßï÷ïp˜ÈG‹ï¥Ü5ð¶5Øò’1™>gÌRe¡0!”#)ÆqŠa°aèÖ·¾RNÛSjÐ{=„˜õÉjiû›ÞëæïRgþñ@Uʞº?‹#/¯¢±ÚiþǏïíŒC§ôJa0™!3£,¬&ab#.f´	Q@hњdÞ)_é$7†„ØÃsŠLQœ0àJjD^JĤƒh+1T„¨Äi´P€ŒdˆAF„–1RÄ¢“S4йô 5éÌ4ÿ±j„3å#.ŒÜ„+ÿ¤$-šßzxىº››%)A~ø~nñÛvšt…þ=ªJÐ1+~k˜;k­#.0ÅÞÝÀœ8•˜$0܄ˆ2I1â#.ïº(äZ9—&¦Tê4ŠÄrqUDÁÂM|ä«(;7Æ;/¦ãGÐ8~6åä%-šš@Ó8¡Ð"ëúpú›ó7íýØþÅÈQבæð¡’ç&{š§l%ÃL××ý<gI–]ÿ-éF¿w聭™ñtc;±°øЪ1QÁZoæ!(¦¢´%°4äÔìíì²vÉ[š›û?jBO\ʁÒLÎü9šÍT¡¨| ]!G"#)‰™*¤vPüŸVN÷«4e*v­±EȜåÚ¡¡‘ÄÎcÈÅGåÏ¥-+×óáBlEŠÐÊÒ©PÏߘùú“&¦ò~ï<ò“y÷o[‡D32øÊœ:žørT2DŸ°¾}tÃ¾d@.ARDsüûl,ZÌéñ›7·ÏThdy¤9Xüü¥aG¡ìDù¸ ±¹Z3iÛ¾¿ò¹Â„[nV„¥›Vi–ÄPbš4è²b¨ÐÚ0Çîv`â)Vr ‡`÷lv¿¼túWHüC$ÝÓ _Lt’eÃc¹‚% +Õ׏I##mï#.^N#C«ïŒZAL]‰Š§\TòrøéôÉË*fÐ|‚ ¤*é¤EêBÑ,¤7Bœ–â#é8ƒ”ÁM„oPüämÞ§BÆP‚l\`\·ŽúÐÖ<Ö¼ ê&iZr úKÙNOðP¿Ó4×ôîÿ4sޟùrߦ!ß\#åö”yd©ÚçÛ1ã8òE>fŽñ›¾™Æ|œ´Ã„d‚kûN[ׇ|Ù¶é͘.¡2Ú#fwGËæ·Ë\ôKضA\ócS䥒ÿO𖀝+h×ê†f“!›¦nÍñc«Õýjÿ_-î9>S6#RNç`€²Ÿ,6µÏÂY)jöK`œÃg,O„’(j+'BB£˜Í¬Ø&çmþ–Æ+áÍÖ¨[™0ítn“¸ïÚFZj?.&õgy±´2ˆi«Ûîª)ћ±¶«m#.h,×8™ª®±2+òL²~¨!9øЏ˛·úõ_	ÿŠ‹o8K›âæ«£Lé¥û#)Eç9±[‹1¹ãÐØòÖ¼ÊôÊÃIïíð>Y#Í80Ü_Īk᤾QoÕnîR¢>{>¨Ã¦‚¼È,Ì·)}‰¥ù–¼Œðy?ŠëÇ`¬xéH5ÕY2¿Ø鮈ï–I¢ÇÜíë‚t¨ÔgRæºÏmÖ]¶×ÏçË]Ö$ªiŽJÓ`¹¹e*¢éن’{_á/8k¶R´¶šÃeÙ Êo‚Ú/Go3£öç‹ZÊ5ÇLwƱçãœwÇI—2[i®»¸èâe¡¯;¯#.7£2õRÁ$ÆSݏ„æýÏ=N4köe´©fÝzÔ!A¢Ò­	?%5Æû"I¥M¼Â2“WcÓð§=5Z†Ñª¹lI«´Æ!6œJù6Äa1ÌÓäû£Vr%N/”yyS^YÞq/!0;dC»«æep™Õtˆ9s…R‡G1ëØÆÞyÈgX5<ÇûP™Áë9<­“‡¿8–¼§¯Aƒ$!LÓR¢¦hÐjRTԕ}÷KšO4ãӞÇ7Uî®\b¦½K	ˆ®}ŠÚ¡£ó¸Ï.“KfáÃB³Fáæµ×wù8X5L˜§9ú?YèùÀ¿§å³ç¿“J„4"óO—[Ȃ|%Ͻâ>ø¦ÐÝ5»ÕÓ;å\ô8Ù/¾î¼í¾ü$øÕfž¸ZEç7¿'m‘ŽÍ¦îÇm6ôoôÅiõ;ãSùtrš²îˆÇ#/	+ü½;Cë&³÷œsyºãÌ`X©UŒöcdÛó»Þo¨Õ~a“Ö„;”‰Y)Šz\!%!ˆ ì\¬ÍÀ1ó»S_Õð°1œÛ’Ç4Pø֖'&RÄ?c&6®ä2Éé\ÒùW^¾Õ×^šžiÁ8²ië`h1lHŒ(ñøQ}Ã?×Àÿù¯pûîûﺘxÇ 9H¸ŸÇºí¼Ø‘Ù¨°èåŠ<5»ôCúJ­Þ¦‡'ꁘ¯k³0j:^@K£öAÖR—²@ÍjÜ`ûÇ,b™éUOàïdÇf;ð†Ã$„YуöÎ|Յˆ-ÅúqéM¶ÒAãá!Þ_<YÞlÒ#{h9øL1<·è˜]¡l#©Q]+2FS'”È1XMÇM9Ä3¥ËyQ¼Ï´ùèì…‰ Ä dáɤÓFzО6,HÀ‚G#ß@S		°ì„‡6tfJ©”Öï§ù¨	®#.b´)u-BívŒœ™4ò* ‹È¹WPåÄ;H\qÕ9ÁÇÈù`ÃM#)ØÁˆb“3­	™ƒœ-	áíe|ó~ntþF˜X²d4·ìüÍæý3-½7iÀԚ8r9KE²jóŸ9ßoËÓ;DÇ7#/	îrtlÎÉK(gWÊôÄvvGá@ãF³ùoù/¦üE¼w0Ó_R¨ü&Kóõ9#«¦€ýÑ]xµ „ÊÁٟ—ùv±/í¼ÿ«Dƈu¡a˜Æå¿õ㌢jY˜m?W³Ÿì1²²´–`€ú¿“³ë&¦™ýýñ­©A„ÀQ“‚D3¯šï³|Ïßø›¾¯Õù˜i0߀AɊºüˆj_1úÝâ¶ýÁ&bdÛñ AÃ^à+Zæ/LhE¸ Vj? ^6‚±(èzù?ÞÃg}Îã´Ä+&F"qòlÏÓi!²#.ö¬ÑÇk¨!à؁=e†G]0`nÈh`ßn•f¼yQõHx@ oފ€¬"ó8ض¦|É[OpŠ0Ô#.áÔÅø^1˜ïÁœþÓCP#‹#.Ŷh˜oÜtõâƒÔ‡€£Ö*)dPVà—! lYÐäwÿ¸l†UUyÕ7$•SÄ8aÌ^§<®@Àq›íóö2,œÍ,^ÆgÚ}Zý°Ñí?HCBv`$K‘Z	Úñ<¨ééh¦‘³Ú÷y‡â°NҏGÛ»úŽÇËç€ßVñ…†föE¶¾Ã>QNê5S”fXñþ:?®€;¤i0F#u†²°ÃAR#)ÃÆ#.@ ZTÈrh ^É1ÙE3Ž¶q2#)àúÓ#.Á§­\ì.þis’.¸âØaµ`5‹«±ë6̆a%( ÐEñ]ín;6JMö£½¹¹†õ+×™;%Ç×ùçÓÃ\’}Ûü^Ú֍_/nooß:OݪÍušÎ_]l¼}·žMJï€3Øî¢hüçc€z¶rW¤æc¨8dÍҋþiu±¸j£#.õ£R’„FádA ò¤–f¿P’ÃJñùŒPIHÅU!#/êýeüVõ3YK¥úÌJ0`Ne†æa¡1>Ÿï¼Ÿ›ŠüíeTUY­j֏y܈³Šùa ‘ªú·2£3HtQ¦Åߣ¾XïßD;šGúçgag u˜T%“¬•&¥œ¡}¿ÊÜ;-®§@éP–6ÄLXtY	‡ªöb¶Õ‰/^)#/y4>¾æBƒ½68žFÒr‘#.5¨4’Îbq©%MȚaÿ~¸À“Y„žM²sx!QYÆ;wÖ&ǼL3{s#)"ÈI¢ˆ?q£ì8Ði¦#.?O¾I°÷ý{$qϯieìË()+šÛ«mðÏÀáq¶Ò+pAŸ1h=¼ð”„‡H`x£»—Mù˜g¦ðòè)ºŽÇþ¿Ñ‘&Is:ûX΍?$ÝD0I­üCšÄ#.!€Ì´É´±#^LÓV¥#.¯ÓÝî[“‰6õìx›<Ux#.*¡M#/±µ±qSndk1°@¤^[_#/Òwqùœ7Iۖ%¯vPv>¨î²(ãÆtiM:t¯:3—&7vÞ;V<$qÅàHñë\]G)ŠËgMÌΖDbšuLßæ6á"ÜÑ)²½Œ8غ±0u"qž’]Ë÷NàÝìÞC§b1BÓ5kmþÙæxÌľë°¨’„}	f£l‡`â“!´j#)­†có¨·ww}¥Zð,qmf5”b#/ƒšLÌÀµœÈ˜ìËE[ø×}¸ã9;ˆdÕCd‰Úª¨_.¼{¼9:né·Ó€^÷7œL4ÐijrW/OÔ6oÅćìÎê{®R	&TƹÍiÔÙPHÝ;åƃžpM/£¾zè·#Ûíägóöz"T$5ÜíÐéѓPÓ왧w+‡#½#/1hJ€£h}û£PໍÉs}”r"lh@Ⅱá™ÐÚùÈéÒæÔ!‡31¼´´0°Ô·èë¹s2P^šm#.ŒPÃ=¢ÃS‹F¤‘Ëz·¨sëÊIôƒ—&•‹¸JâÆ9MøïtbP^#/†¦$9^ѐâ1/غ‘ã»K2÷žDÌ2‘ØóCˆ	j)®Ðܙ} tŽ×Æž·„ü`¸wž¤S0…³ž‚ØçÁºŒm¹–¢ÂÜcÂ2Û:KEJìF ˆbhnÍꟼùû—S^ÓZ—v¼'/N8jöæ9‹Å9>ñòÛ}°ðò³m–„Å%Jž#.¼Üq•óεªiSnS-.·HžÙ	„KÄ=O³´d‹Ô›¡Ô§óJÒ%Œè<¢â‡YÕêr^£#"0ÚjŒ;8¯d{NæŒCŽðÏM†Z=F›¨ê4çltœ‡'Š&¢Ê#/˜’rù<mç+»Ëç‡>vòcv– #bfF‚|ý¹©˜%#..f\y™“.ffeÌÌX­ÌÆècÌËà—-Í>yµS=ÙÒ#/@â†nú2ç!1¬X !ßT ^o#/ºƒdÑWÀïLJ¨xS<äLr9–Éåì?²âãßåԅ½y×>{ä]:é,F`­ÑX¢¯Y8ò¥q—X~*ºu¶‘pºó¾"µlÝáÏDΰL.Ó:ÐØ­jebÆ*Ya2ALät[Ãçcã¥g»L¸»K3ù4A™’P;o#/U3DÇmƒ½¾Êë»2˜’qÝ(.Î™ ÅØ.b[Ïá·Vga؃¿†#.Òñµé!ÇFjFlf XÀÚ-ê´µâKÜ#/΅)·Ï¶ùUFÑT±¦XmR˜ð­N&ðÚwC.¨Œ¦³²‚Ä¿óÉ PN³¬åª**ò,gR¼„å±qª &€$U»ûe¼´FÁÒà×#.!c°èLK…9bcÃŒ1!˜=üþ.ô—‡À{7~¼òOïX4OoªqƒX9lÏ4|K3î'áø‚·«‰	6Çܛ	m£¦³è•#)¹Iáãzc¨iÒ™ºtvðQy/%ò]ؘFê4sÒÇ’Ë#/ež­„OtՎñD=¦å!48(ҔEtTêѺ+g‚›qØꂒuÒك)ZáFRg?Ûûæ¯AÄEPãXN=÷Xté"‚¦#/Ý`îU3´0hŸ/’ð&N§WLŠr—,d–œ#.q達CCiC'xCi1’ŽA‹ÂQ֊.Åøf®é,9PF†ô2c`ÒÆg·¸-b*þ}#/‹ ³,YHɹ#)ÆMÇtó’N¡Ø›Îí8ç5u˜)”Í2ØƜó>ß^ÌôƲÚb͉€gᓐ`Öð)Ï°Gx£#ôÐтc–Ú1@ˆ5ÆÚâƐÆ2dLQ+"©h@ÕÚ:Ül°–ÁrÉMVÎ2¢ƒx£—Ÿ$ØîƒÁXm™¹”¹M„R,Èftö¶𸦭©D½ÍM{,k>o@v+r¤‰S¶Irû²`dSp7iï1â;œj]ó;[€1›õ•ƗÐ;?XïԙfZgPd#)Û>F¡ì‡Â9KÐb'Àc÷À[o°wrœÄ`²™5‡y¯–‚á‰ò؏ơÏ(:_ШRŸ<Î8âFù’Édmº{U­¶ÛUV¿?qÙˀ¡e#Û:öùÓtg Êy3D]v”$¹íöbÛN'V”1‡ŽÕ¶Çxy-Ûڜ¯ˆxpi2.ÚÀ1=ÁPZo±oAAap¸%WEÐoŒ÷â4;SÉ3ÐÉ,Ðð’@öq5¯->|#/ôó*JvI¢BÙU÷k6ÜƌäÓ?qBhȰ䜹ŸÚí*#/éÏÒhé§o®´…Ò·3ÇëÔXº*ÑÔDÈ2ü‚@º´ÕlÉk£WeÛ#ù#÷ ´®Á	ðàÚï}+¥xöfÜðOùdŒ1©ZZ—ùÿ󱵉yu#)Å©L8WxچgvmÜ­	•™ôI¾ÿ"8||ÌÃ#/&&"2’ (ÓI&in!ãԞåQ23¯ý븠õن] +€ ¸…‚J€@ørž<ßÞ_,AÑäå(	L?½ä­—ú/ó!àÿ§þ›‚‚´²Ñä#.Y"0ýŠùßwÁEosnHc0D‘† ÿŽ->¯W¿ù'–)]_«àà{¬Çbäy1åæÆ|ˆiaI¥jM¼î„ÖRl¥·Ïn¼9ÒpKº¡¾pFjÄQ¦?Ùïk×5´[ŠD¸~—Rúì³·³—8;CœTjÀ]øí˜{4©üŸ Oí©6eÁÉ#)pQO.¬#)âxkç_,<4â¾—×£6öó’'¡Ú‡žóÙ+H{Åüþ,A;8C¨_#.Ñû¸žò"V“‹ô|ʘ œE@êSÐèù 'Õb‡A€ ‰#)ö󪀹þ-E#)ÉLˇærÒ¥$KZŠ_y°Ì[1Š‹Í^û¶ñZŠ"]»iò8ÃÉÀgc€ÈéD$ÁnÞFH䞅ãO&X†Ã–»sd˜ï3ªÆhÇ/?°Ä5	$¯x«Àð,ôcðàã—ÌñÃQÁׯœhúè<¨*®Ø#)´L¸{Y¸ÒÌe†7Ãò#)7¹€ŸÝÝòBösÛ²àö‚H@Ð$C	#)ü#.ìíDª¿ß”A‰A‰X•€Gx*À£Üpûƒ`Owz/Âùß;ê`K\ë…Dm¥©DP)ö€Â²¶×–ÄëP5†°¦‘„1#.ÂA؀ô#)¯NåʏôúzôèŽÝÚõ"ñ¼4¢Mõ×o•¹z‡©s’kšéwW+ÙÞy¯;®›ºãšJÍÝbå>3¯3"¬8#)Ä#.<J&-x‚8<Bjø÷ãß)3$l(&&¼´¡ô}#)ŽÆÂȧ×n†Âqÿ#.‹ êñcþTUˆ¨ô—Â!=-lÿ.C³ë\š(¦ÛNHx%#.2´§›‡hðÏb1-0?9%°Õ8b-]#UI¥¸ú”5èÕ¤éÙuƃYò‰ŽÀ82¶œíñ,‰#.ガ0n¸Gp‘(Tã”v lêfIš”ß?w¼å€x}|ûDםT=à`¯õ‰±÷XžAêývaä%òÙ-øš*êÒ)ÀÊG”£$6INM#)®¨"¤ƒmX±‰‹´qëÀÇbz,b`W½“E-©´¡Âó/+I˜eæ¦ö#Z?"MŸb¬8ý8%†sS&øŽš»Ù¬ü¿‰llc°õt¦> £k#1€íp‡€Þؒ‰[ë#.½%áۏ‡ñ8¿zôyø¥={ùzӋ,ô9	ñxÿYáôÈ·MD>ší&L0j¤Ì¯vJêÅÅ^‰Hyüì~?wäA¦2ŽßOw`wy-‡ èŸNX¾ÐÙ‰bhÈQùԏÞ鸣@†Øâ£ðTîïXãdó†³(§´NÒ¯w_s«#/DÂäÿ#KªÅ€×¹ùOÇK—i™¹.k#.43°¦xp¯ÉÝ´ýÐÐ̝Ó$8îÊD{`»Ø^„”ÉÕ@ù”J…m¶6Ú1KTdT¢MlU#.–µ™#/m’¦¶5“e*X#/#/‚e…XE$D~rWuø®ý~HÊðŽîˆ>ÐȐF‰iCÇ桂Žá$ÁêõrÑ­?#:h‹ÙF…¬a¤FœüoG†R£[Ø5¾Ï-–6ȕˀðõ¨uE±^¨$#.à$Ò257Лk©ƒ²bzf¦“ãe¨øXÀ0xÄpÆ8òž…Xjhi)î䛺\•_p±Æàzó~@IÅWåù»j¢«Ô!"¿(S©Xv3”ΛÒ0s¥Zƒ"ÚTºpb!døažŒGßôGÌaš0 !˜¯À€F†öâ c!7]ìf ´hÅf•™Uôg5’™#)ä¹$A’L´‘%*{!2JT5'Næ}öm¥㧁¹ÅC±öEa¨B€ˆ«ì^jx’·Stl"þ£‡lã7gg0P‘þdÚrÆ36¿?3B}ožÃ>¹_fˆH…?z¡÷ŸtBùi¿éÃg»Ñöý½Ýý÷\€çC+DœHfâ"$…÷yàۄÀÊL°¦Ö+d›ÀxNH§–Ø#/ÉøÞjHaŒ1(džÚ¢n{ù]_3Ìïïí³YÁ€o6”2ç)BþñEÓõr´€?‘þïõhüZÄóÀ Å;žx«mÝÕÙ2è†>”±¾ê‡ŸÒú~‡z›Õû±Që`ãu™(îgÛæ5	ƒ—-$‡´õú_¨Já pÂ.g46þfvŽÇk†ÂФ““f@zÊXŒj˜(¦N ɉ¼Ö^N]ó²ÿ_»ó£È`qæTœY²u€i"wi;K#.¨ß~ÂßÏJ™OòI#.ê#/ü¶×ÔvâÔœA™¿«a[HÐÝ.6¸›žk'Û3©àV#/|œ>/¼ôûds%¼8B`þ™ˆ„HŠoL'´ùÓ~%c›xLÑð晢Zà(i©˜³O9''M0jë`fþr)ÓÏæ%¤8¡á)HuÀû5Ö+Ô†ØS/¡âÕ†š 1¬QµYâý#.¹‹o<òk{DrAg/)H›B1¦\áëðÐRƒ? þ»®9ç3Ø…•bMÛ–¢˜sUkImD*QÃ7•Bf¨§nðYìHù´†Õb€_°ÄÉPø¾ÀöaøÕCÔýI¶¡õ ù°“oujB~>ßýå=raû#gø¿#.n®ÃQÉ­¥Li:þ?³·¸ôçî("Z<ƒ0ć"V†! ¥óü|wASˆ¡ÂX:q@¾íct´ç¥Lp¥NÐ	¥ÐÌK0„Z„ =õ‹í¢‹§*—ƒˆp7x™;âÔ™¿x?pÅU€ó-	ÆC‚²/i»#/ÚݝýÏðë” È8.•ú"dÛ۹없W®ëƒ¦+ä"PrËád÷xè“hÞÚ#/ÑÁúôiÆöÒøW’e7ÉèrKæÓɳ­¬´¾&B´#PrAãߞޙ$¬»•{ù\q2ŽqŽ<ŸÔ¡0ZØÓÊD±™d´&ø¦àÓ;*^ù5ÙÆJý:és°Öœž·Ô<_Fð7ÞBˆ„	6wSMWã~K­–5[ãv%oCŠŠ¾…›:g‘k#,´avé⬛Va„¢´A#.2Ü5M°¦W™Ï<G¶» 4jÐâû¶ÏîçZnëtÆuof~\L%ÃցXK9^ÊÞ¸®UpׯzžñÒ¬ÑXèHØ̋L‚ÎÑÞ#ÖübVÓaºSτW*àb<²|~8͇/Ïîû!¯˜åæcdj˜Ö‚wn®ó;v·«mLX˜Æ«¼’Œ­[†Øv®„#°ë;yíñ+2÷«#.ÑqsE՗&µ[~>ËÉmש¶;ºZºl˜By®\™·$Å#)çf÷™ó٢܀œ +¬òÙå’é­« =£°ð¹p<„‘áyllÛ³Ñqb~æ·¼—0ä\×>ñµÆ@£á¤àW¦ËPd$™¸úp׿+$àg6LËœ‚`×YGT8ÎýÏ?oÙðO3,;4ÈànÝ[«`=/s'äÒòKtF™(,Íí ^îØ!—~Ì݋õhkšSMZ.B²GT8HÙSfF±šð‚á¦øf$#/@íV:L17$ƒK8Læõ©ÝÖ5™Ž:&&$Ü;ûý+×Ë`°Îe}õâŒbj•JPVÛï÷}tõú+{6ß!ƒ" Áƒ0Á1ê’zÐ#.|sî{+„71CÂ0Qßy0É!“°³‘Ôɱ?×O ¡ÆÍcš#.œ×fhÁh;m.#.ë´ÛWI]U5ô‚çîR2SR´ê™¹ ÷ª¬à¿HÆ½†ÓŸn|uG—I.ü#K·ÎMƒ1ï^ý96gußJõQÞÐ7Õøû%`³–tÙy4Èd3r6 K_n׳3†-aÙ·ü¢QzCt‡£¨`V×0ZI AÌide)65t×dî·Rd¢,ÀC‘3<¡²pÌ0MNNHVBj0Ö-!]¾ƒcqwLv4¼=­®ƒZKT—sàxj¢¤€#/VQt'dzvœ ©¢(•&B@À$Ør¥ùÖ ù$ÒÐÅ$ó†×cËÃð:ñèÅðŽ¯›»Ës6ç88ۈ‚	’€Y=•ÒÊeeWÕ]v>ÇtNfzõšSSĚÁõ‡œíA(õÇgì7=x}û†“ƒÍ^Rˆÿ’ )E/p“•Ã‹@“†¨nï' ]_ªÐ­*ÝÙaÍh¤c‚‰ÍCF!nŒ»~Õb³~úI_8©RnSB ½3|	³[Fq'ÅM´-:*Ó^wœ­Þħ‚߹槛†$2*YÇ*å»rÝnÕÚÝÖò)yqw&6”1ÅYUИˆèÓõœìŒçB”™„×¼‡aó ;æ#)¾!SÚw!IC’€a"P>m“ìƒyå]BÑ.A#/„Ä#ó£põ¬Ë»*ú¯Àíñ}xª°¶ìú)ûª¯^ǟ5+Ù¥yè„? ÆϪb	Wžyf—åyšð%yd€”¤L“6£k#)mdÃ0nž`ø„HÌ}»5㬵ÛxãQû½úWHT	û³o	Fqµ# ÂH1R;#.š4Óuƒ0HL7±ý¶°­R¤Ù|g(z|zy··&<©™˜pØF»UÎýg8¨Ì·èM½z㘥Ž’µE2h¡P=G–‰G$hϘ’1÷‡5I®Pç²]ŒoÇ× Z™Oˆß#/š¥%þ̨I'9¸¦!_S”Ø{{‚ró­û%q	Ý#/d)#/>Rœñ8qÏW&›|@ùó9w.p.NÓÙ÷r#G‰ŠF qۊƒ7	Û¾1÷‡èþ3Ϫ{ÇCõpÀC^*ª“Ú8ÌV£#)Vy¶ÒR¸h)6—pÙEëƒ4–Âji4+ºl‡	]ÂRbÞ!ù è:#/,ü;Ë5rnP@ۚ(¢NIB<¤	d‰ÅåÎ÷e+ögƋ3ØÜtÛíÇF{S†‘)|ê¦B‹à<ÆqT ž$.¡¬˜K!L!#)È$”#/ãl1‘Ù|ÌY‰žL;µ›Q²VUFYH#/7àqSWIК¡en6\$Ľ¡¤›)*Æ«x¥†“hcemðÖL#/xÎ-CÛ]Ÿ»µééÛ^/ É>¨>Ü÷4_áqè6ف“ˆ'ׂÁv8óô€Q‹||ÙQÓΡiva-á}§F”ÚÌNJC&`ÄFËÌû˜,gÒzËd˜@Ø©T1…1z»PžIó0vzŒjáétQEvH[aÄäpÔ=ë4,ñF(Ž&¸g"Vè<'6h	KAHíãÇÕ¹†ë¦ÍÇ6ÚVd6p³Àâ9¬Žê˜@Š‡vP¨ d?Ndë´Þ„Ne²s( U¢ˆIKÒøúÏìHJˆJ Žjy€O2™ôø~žn¹Óba_¤‰<Ó¨lÛÉÛDÌÌ’êàWhÚÜåŒîT§Õpg&¨)F""(´]ºÝ¨‘bÕ`–&(~4`pS#.ÃÓQÆç‡ëÍ‚(,Ä{¤Ð3 Pžx9ÉÞÁý×F\"°qcƒÄJqS\†%Å`ÈDÀhT#/=bQ©2ÈޖyESdq³[ùnР1‘:’yÝ狞¯Di%ì	#)ÂÇ$úðMž‚â,TfrOÛÎ`Ú0öïp‰Ò u½¦ÿZßt4ĸ0†‘º©@øÝuçV|Öéј†'|W¸ØA %<çwv“˜ˆƒ¤œÃcvZÃ9…"§˜½š„[&úRžÍ„Ýc½}#ŸÅ·ÀÜ`³§·MØç-'„Dm¢€24ž2àB0æË,¢C¸')ûƒOCå‚x°=BL‚š2	•îcúüŒAðhõO–VNmÙ#/v`Òlu¨jG,ºdԌ$ՌèS¡.†6ˆÎýbÆhÔ0eV"ŠPû³ÍÑi7Õ@j{¦ÒåRAq“väˆiÇj¯DBQ„é™­#/°¬ædd#.²Ö“be¥h¥(`‚Fi‚ié”=‡iê†`*ê€Ê*¢XôUٍ´Ð°<RQ֒ó‘>ºç’q&èÿÉõ#Ão‰Ý¬O´oäÝ]Œ(‘ÜL½dÈ¥7GðJsqÇêm›!¸¿­<©Ph+wb;Æc+gWtF„¶ïtpï®ÿk?ÌÌcœ‚qÖ#.bÃ~u#)Ã	`'@d ¦É	1†‘	ï$g±Êá#.Ž4¢ 6ÍlXW—Œ &ƒC¡d†©·7@C³…»gDƒPP›A±#΄‰Bè˜-#.`µ°‚’©";FÛ/BkLFØ̓DZcð8ÓThˆfáÈ4Ú5ìJš!8Á½®X®HPpLn;`—ëéî%Í?•§_©;wÆqŒëÌÄt\b#Tfè!UÅF`Ő¥1xäÔJ®›‰F»CSj$þ`¤4Âé¡ï698œÇ¤³Ÿ:-藇p†ÀKz{ːÁÄ(“‡Ç\›l³·ªa²ȸž:tô҃˜s,1‚ßÌ·|0Qó&–=r!¨®'°Ðœ‰NRwèM¹çÊTê;°vë:s	#.…ÅUV˜É@c‰Ðã\Úºê‡Joãƈ܇;¡ªNaF“n]ZLb]7(áh”MŠ LÛcLYö2‡#.þÈm²–ÈãǾŒ<±V}%ØO“‹vÁÝ¿d敻a¦6‚	4øRÌ.Š¸‚t›˜ªl!ñ8”g8œÇp&À8¿ÚæÈ)hÜëøs6ÃÞ&šÑ]¤ „³2L2XgBà#.Ïyd[qÌ【	H“@À|CHðZ#)ÄÏÑ셐Z÷¨ªt6a¥„.Ë8¨ò6fŽ{ffY²Žo&àÀ®„M½Üö#)ù;t]ä\‘XA@hÇ=²dßB0«5 ,RIR˜l#¹¸ î/_‡HØ8àA%`=t$7‰X40uRÎÆÕ#)îšú“9)ºœ¶ç_Àžh)  Ȏšž%õ€WݼßÃOäm5G»fI¼–džò÷Ss$ÿ)ì’™.‹ï†#.Œ´V¥sڍáX¼ÕøºŸãìˆ'r@F ›«Ñ ±Â÷`zTÕS4–-_SxÚñ&Q5¢¶Â¥¤r#)JU"EZT¤¬%Ô¢êJAÂ01zÏ¿„ówªŒ„ð°Cç_™á‹RM«CØ$²·D°otóYÛ¡#)ß(@©¶|¼a¥NãÚûWܾ}&Ûo¯%!F ”ÉA¦Ô†d™’ÌRm‹I•“%m(dŠ6šT!%4F`IQ’³$kæíÓ%i4lҔ«,Ó(É‘F€Z˜šM¾=Ú ±³B‘$RI,¢6£$ Ôh›%%‚)LQ˜¡1VR¶Be”4Œ–FÍFbC(4$l,E™ËÄì@×n”t:î”h‰zÁ٘!5ÑTÁ2O…‘ÚN»™Ž}¾ÚèV#ü#/ãÑÞ²ŠF¡J&K¯ÄåYÁ‡RC2ºÖ³‡2¦€ºO¯	ªY‹4czKë'sLI1eF{Cv!&ÒÎ:câMÒÝÌÃa9˜G#)n¢¢lËEŒÂÑËm’/¶œ¨kG*$k¹ñ–ñôÒÍ£IpS?Gêû9çúƒ*&;%M m°Ç*1½#)l#)àxt_ˆÁ_=SR±””Š–Ò[4ÒXѦZ&ja$LLÔÞëÚú;áôîøžÜLÆ(Òq8—ù-ñî5ê58·ZèzøiiðÑeàL>ݹÌ1‚&’¤>{|0¶¢ž—¦-'QÀÒÚ¼Ã	†LgàŽ#.k¸êØǛ¥°f_rÑÍp↱©Ü¢j{gÈ4kûŸŽ,ÀG‚7îÁ{•="@Šhû˜¶~K:‰|9:„þ#/˜uQaB‰“‘I!¿‘1èí?çùiÉ[þG^ËmŠqzbóÇcÐÉà©r×®Ì'iGw”ņ›¡_T‹Ç{šÕ[ѹu°èÓ&lé‘6½Ì3Á„–׿8]¿ÍhôGU´â<“6ìÿ^&>‚·¹ö 5śûiï¦ýyå½zK‘¬ÇCÉn¶­ÃxÞàæš#.OÚôk2`º’a&P/º`çî|n×g§KŒùè»âCÈÃá3‡ÏNa¹j{Ív\y%Ù òI7"m™^Fû”e5uvvZ•OµÈï;¡Ç¥·zÍ-žª`—@žèsÜ÷ÖN#ÂHK½O)“AŒÌ£½N‹«M²ènÏ×2R™7;¯v@\Ãs›lêLf—­e3Eš,~{òs„pÔ3i.v0ч[‡E`Ç[õo2#.›`÷õ0Ã@Üà²ä“û=Cš„+¬!©LZô,g`öÀÍÖzp<Ø“õ¹ìÒãy_à3bh¨#.£°ª§~wiSj­ý…/ÓÇÒþM3r«a%&P¤ù4ƒlòƎœFN›¶ãݹ¢-ƒ¿ÛÉ#)Á'0Šc†gi¢75ܛiÞ3ŒälzŸ#ähjt'n:¢¾¾#.)'C¤…lå_ß#.Üø\@oíôìBLÝîñ#/Ý¥(„:O ÑÁšp’BV6ž¨íhŽŠ¥„0ƒ|Ö¤û2;ÅãXMñbÞhlɓUGÑ{Vz¹Æk#RÜ㮡º^_ªf—\ÍÌ5/2åÐóL£BҖÅ.K¯ËR½)LfŒ{fñì¦,ëÓ3Dt” ….’þI‰zM!ž8y/ˆ[‘µ,33ËL,¾™Üêâÿ8Põ‰ru/œ™‹…ÍNXΣ92ˆL3H™à])éjã<¢Q¹¤ÿćhž Öյ˿32b]eؘé/5Q…ƒ‹Æ3dîv]Sa€–R˜fsWLfKڝV÷-Ö*MÿlàˍÂ9THóˆ3o	ˆGa•¡ó¡‰áÙÍ'˼óÜ7nø2ÐŜ²ê;Uq¢6§$jæ8ÝîuB	‹‡<Íâ1WLÈWÞÔj2a2&¢ÔÉÝAÁ׆ç3©“¶qYÎÈ>.¯7ÍÚ?»ÕÛ²ÞÙ'81Gœ^—|ʾ™ƒ}\ÚÔRd+ìäÖn×ÎÛÿ“ˆ!öìy†(¢)Úï ÚÐp¥>$‰çf‹l¤Åü»Æ!bޟôN§8|ñ½Á¥·8:gW!ä/>7S¸’xíÇ)Þns/x.ï>¦4t€I&ÚaÔ:ÞĈ„q²fç¡7Œb§#LüŽT8»ð,ñWSyvƒ:¹ÔÁÒû›pBÎ{ó'%¶:„<n	GtÆïvèç£w^ÜL;¾۝“·àwÙÁnÛA›¼²{(ÆPïU)ѺlââӁŠ*tcŠiã3j¥ˆ	Š\4Í(,dÄB,¡åijíqW¢ð¸ÅCîoq$,p…±3È5µª¶92D½žpCgV,7Å*h(ô4ˆÕYi»â«Pzjkϒ˜ˆænJ 3»š˜Hhîõ¹«uÉ5„uŠÒbôáˆl¦!¬6‰#-¦|Gnû3S•®Ñ[¨ùO,º¼jT4ã}ø}ìH\hfngW—~ˆOšMÌæ¦/®èL›¢ç‰Þé°È’™.ˆãvVͼàʕ˾莈uÐG4ç&ÍSËL1s‘º›²Î,ˁs,)Ëf6‚hHè;WPАìO#/÷Ww-RVˆ†ðéÓ{ÞlÖ ÇÙxPVI†‡Œ証pçHÜX¹•¢@G™°D&ÞÞy[Àªg‹v́¦QAÃå¥Áa±·J^º4q—¬å--Qi¦›Œg*XÙ8#„9S"†A‹LòíP¹á#/°H§)úé¹yÂè*Ðí©§$$™?÷[²™•lU”«!¶û»Ö›çs7k?Nò\8|pbٜ9úŒ—	xñ«¨ÆBŽÂ3z~Ìb,¡Ó»¹s…?À“O˒7‰¤„*ãn,rÏ®ím]j³£#.Ë ’L(ù×9àûfzdM®xƒx(á˜àùÉGh,¬Ta¨µa©aáazÀ»á^.²ëL¦7é„ÒI8ÉBn×á»GŒƒèB±Ø£îɨøöP>=/•Û¾O>`únîæ°Ê[«;rö]n„}Éá#âõ(Núví3x·HÄa¢ÑyÍÕ HK*2cV1™’ïÃh(¡ß|c a›ÑuŽL‹ƒ%¾dŒnE¦pÅÇ;©ºefXÛo‡Ì4êd ddºæQö2½Üvï%ÜÇMh®Íٓ4Ìu¹kDÈJ6ÉFPw1fûomYah£3Z¶Ú™“¶’²Ãƒ™ú‹¨¡n-§@+DñnulÅ[ÒøñÚ6BlÛêð4Nñiiäa¤·‚€D¦ˆ`Í=á…ÔçqÔRvÈٗ#ڋ|ÕɔBΖ±£¨›ÌJÖîÛkÆD”6BnË[LQ$Ô¹8Xšj0]#.I€‘ªðLΨÔ˜Â-1FL"&ޜ—Dª%	ªtM8‹…bqLϑgö1y®!ѶæÕ­·ÎyÉ0&A·Òm :‚\„±¨BŒ	ÝŒ:tfù6¸#.Ô@”Áç`#wVHwξìoßÒk€è#/<q€#$WN ÅÞ "&$.úž#.5ÂrÑ{bˆðàÙF”GX@põÂNbA­Æ:†ç‹:®â'<‰	C29ñ›w	7“‘R¾ˆ{ö3%w&‘b¶(ûXŒ#a®‚×¦A¦Á²NÎeM2:°§“#/Þ°áÐiÈÀX$È1anC…BÒaÏX…Ú¬lQ$#.M5†#.´3UHl™BÞII#.’¬lڍ’MUÑÂJ`±MÕ<4¬‰›¢™lÝ-“Y‹¢Ù³&P7fqG'ŠjȦª8ììÔ†eÐÔttŸ:˜è?X0¤F¨]eKÁà]™D7F!{µI©UNØ:äuuGç´p0ØàØÞÍ&Ó¡2jlkÖqÀt2‘&‡\!áò·¡k8ÐðÜB†ÃŠåò_ÅuŽ‹HsCº‹i]wCÃ™Ř c˜†ÐdÒi´;¬Fh˜iBàÙº5ŽlÚiÕ¶yx¦dC2rHâhHHQq/´ÄÉfž^q(äCøèµêP…Ml$H&ÓspÐYª®9(yê…âì>‰Î,	‡È„/(wÇ¿b‚ü_NÁ:ƒK`d¯)h"6$4Dý#¶Cºž]û†»NœÅžÆŽ˜xð\¶ ˜£¢;y[â÷dyˆËiŽó”JvŠ!¶;	½Ú…Èqç[¨Ìt&#.ŽdCQŠ#2„:Št61H€p¤à{¨¨7:r䴆á‚÷"äMeŽïrb74!#.Qõ`½,]¡)TD:‡9GL¬†më¬Ò½©{^A¾_a‰ÚNKuĝFX`‘§IˁˆD+¦RÙSŒã5:Qž½g ‹Á(Ž&f9®zM4Î3#.‰¼ã	¨ÄœMFáÎ(L’Z™¶Áa:¡ÇI3³dpjЮéVBØ»´w±8Ùcƒ PÜ#. æ©¥¶26Ƒ)$sËãß¾Ûã<É6	/²Û\]ŽC€ÄÏÖÒÜ)Xа:ÓÁ$ƒgTXix`©D©õ°˜‚Y€Á´(œà½…©¨¡Šoõ¦"Œˆ¦ÃÏûžõ?°>ß?#)èžTm}WUÍ3Z22#/6ÊْŽÀü¯ônRô ƒè0Aá(w 'ŠaÌÙ%3?‰#.ÈÂ	”–³DÀQÃg€.êì›i¨Eх®¥ý±{¾aðê\Q¦FO>8ÌÆ–‰µ:85Îâr9ÕçûŽO+–ú¾…ÏG®èv‡{©´"ʁžª1nð¸lrN.™“8´­S˜v!Ôôvv]Ð6ŠäÓMŠ {‹Ø¿iˆ`eV$<5èB(ts1u›Y™‚¼¤ÊÒò0ßj`Í°¿t4%AÆbJg¸ý]x¨Šì8á`×ñl›C0”D ‘;°Dˆ¨èæ ðøB`î"ù‰#.Ö^¡6ÇO¥7Ð÷qÄR¢ÕTcׯQ jÓn“„}>žÀ8)ÁbýÓðØMŸJ¯à½?_@å$û$Ô:5DdI64•×vg7D±mÒýŠóSñ)³	Ê\ƒõªJ¡â=¥÷ê]?Ì{®ˆ	بuˆviü[‚#ބŽðO‡yÜñ$îîÅ윅ñ6Ú_´‘þDð'\Ì	È>I¥a&ùIPÀÒa›ìj7© «ÝCý?O¡ðî«„ðÔÔ$­ga†Óy®€™!Ñ+«'9ZÒÄRVÏ –P¼| ƒ7¢½fÌMvd6cê‚.ؔFŒz# TïÐÚäî›:3ž',Aâl»)º©>>‚?ˆo¾)4·ÈÅÈè8ùµ“Š/0û³éûá£-u&µ£‡3à}˜}ðyÉ%)ˆ+lm’¥+FµTRÓ3Q&6ÑfWÆ÷ÛP"Ž{蝟YLJCóúQיO·«Íä¼@ÒN†!<¸’C÷r8yÉ›ã¦~W{`¤ãS%	©T4iK}dæІ 3R›Ôaìž=37ƒS¬ñÔZúݤÿbï¼Àƒ;¥‰=ˆÑ„(ACôJA)W‰¹‹ªKêUHÜ֍ú»9i« ØV,2¬ëª‹ ˆ¬Ðë°ºpÒ*o*lBBr“P%¬¤Rj¥·Ÿ´€Ž#/Dø›íøœ	F(Ð’=7ÁÌ̃”T2HŸeüvÈi«ˆ9—;óŒ`{‹Ž“ ›áª·Ÿ4Ö5©ØM¿óXM”ÄCV„rú»u#Lä؋#)²k*•°ÙÕÏ,—$¬R•äŒkÅy7ÔW¯«ã@4Å·,ÙÛJI‡™u1ŒMÈA¶ÈÈBû¯aÄ7ÈÇÄ8Ú㌢ñɑÚV•ÚS„¡¨4îӖ߇¿èûÕït8{=æ;l‘ Äê\ы:1€È¾õȆ1b[1(¿œiZ€ÁVÒ«ôÒr qQA†QyÃh?FIîÜ¿c7F¥_N·“œ5T–+¨IùX±¡­0Ÿ”K•Ã‚7ÒŒ35oÖˆC Êlýœ£nߊ6éÓJš[ŒimSÏClc]º-Œ˜í¬n°62´6&$ih{k–qÂƍZƱb‹ò"gúôÔt3¤3ÎU3“¦„ãӑ#¾‹Šï<f6]s0RÊ´<l<5†æT€wu8~‘€Véú4Ó‚«ùÄÕ#Qùöɝ£cÝeÜsmÃ؂᭚‘R䙠d;^C§:>#.E„"bèsµ#)””Ðœ™Î™È’Ø.BPsONšIDn.Ÿ¶ʑ°8Ëwb‚˜Ê íJ0ÙÐ΀™xô#.ÍÕäǐ‘¸B¦‡#);ƒ`¦Âœà@c‚ò^FÀ!·4È‘@¢’Œhì‰0l÷Ç`oÓs‘‚Ãüè°¤ýL‹Ü(Iá×Ú^W—Õª_äúõšú:Û}‹œÅv¨=GúÏ3ª„?\°¨¸}Ç0ᢙýþŒà#/ܾ^yCgû	9GD#)H°”œþšŒkúî˜Äƒ4CÍo™"€Á3ÌÌÈ̃Ôuý˜£B¥"%@£ï	“슘‰ö¹ú›‡xIb‡·û§#/óúàZ¢§5ßê?»Æ1Hûsq»‡N++ŸñÜL”µÙñÙhâ×A±UbW*œ9g]8c‡}I•º™’Ój)%…«§|V)³ØL>S‚Üj¦MØ’F#.äÀ<4À`CÅLeN^ӊ†A”¤AÆp1IMW0·î7ƔөiD)ˆzºŠ¯Ž·zè9²LSN±yâG#/ª6©#.0åØìÍaÅ!#FdËÔCD^e±j´‘§Q󦎾0½”¬œ9ÇG#.#.‹ýn@„t~npË$OÝ<PmN8°šÅ)ón4ÛÛ†nµ®d©X‡3ª—™Óî"1&JðÇX$©ìAxJ3’™·Ó)N%Ž÷d®'΋„“"¸™I]5ى@wM¶Žƒ$z8ÉÁâ¸.#.–g°ßìL«ñZï#)ÐГ2ó³m	ÞÙÏU£ŠEÆXrfD8$Ž§Í8üÍÖ¥”„aâÔnb£…ƒ2¤Ø€´†Š7[•µÖj”)bÚøW<Hà#/ÄHÒ7ËO{¶k^™5ý-Mè=·j„r(cHËklŽ]µ–w%Û®3YÎשιÓ;‡tÇ]ÆUå«¥A]jí%`Ü®ë·wi^wJ¢d,Kg#.RP™#’¸,ITºsóòओ»¸c©Öâ½­v«xØÊlÓ)2&ÍlVjh¦k©jéZY¨I"e‘ZH÷û»®ð߄HÓ5£RÕ¤¨ÖÊH#/‰ð6ùüÎ0U¶Ä¸'$S#/çMΩ®ìÒhtˆ'yá©”b#.„P(1Å­'l3iAºN'tI.¡Û]gÎÈvdnÎÁ‚$6#.¾‡Ù¤ìÞI<€#.Ï.EPÕÅ_8¯ÅÂ#/C	¤Î^C‚íöæadÎfˆaY4}¶uI©6(ª+ÇM±Ç4ÅS´›hØU#.„ÙíJâc°ã{Â@ÀmLLLÃ[`;²¨—#.I´JFæäo#’K˜`¦EÓª;0¾/SqìÀqŠ’*fs mMX‚ç0pßʤ#/)¤RÚ[ªi¤XoR£¢ó]&	o5Xï‘zóD!¡7DÇ'4Yn=‡ÜÊíµ­êÅә…Æ9fWhXòtÐÃ?>Oâ»?D¿øM+Öð4x{#.`„0èĢ֜0x†ÿ>å§v"ï*U†4${Ú@ĉ†á‡3ýAC¯âÚç|ðP»Tt02®#/Èdrp†²@fÉÉ/ñ"a‡G©£©&èm<¤íüÓ¦ït±ÆÀ·]]Þ´?@?Ê£*„€ÈJ§³åøžséú<Ý(ÚTìŸÃ†{¶ÜyAOÝm¿*8{¿°ÔþŒË4,¨*€ùä‘G§dSXڌi,Ê%Ië®Û’7msDe(JšªbBÍB‘,¤ŒJe’ÊdÓQ”FÖIL°–¬³jI‘Sj(­²Ö¥‹Îâˆ$Ì$¢èŽ	ˆm‰T¢1”•jbU4ڊةdÅM·á­n«â䊣I6Ak“Xµ«d‚U5V÷+ªm¤š†¬yç^Y¤Ë-,£&ÍXV²¢{FU¢–šÔ8P뫧Ñ¥TÄ+•v³T›ÁušËI‰­¡*ÜMqˆM!*×÷·mO¦n"i.S]þ~. >±ë†ª@6XTy¼ÞV}vj-¬ÄgJâ×rŽI¦ìÔÅ%’a6ïMÑn#fÈ~¿gOÃî‹Z	f+媻8a€ÛÆÐo#.4D#­ðá(#Àª#.¬&(IH¢i¥•¦Z#ҒÛ7Íhô{1OL#Â#)ŠŠ)7…G{c#N@tÌL!„(”™#)P*aۀ.€:ƒÎ œ°Ò×v£Á	æ¤&@5ilÔJm%%«Õ§M!Á*	+$À±#)ȦŠT–ÅLQ-²¶&В”ÂPÚR”„©3e3,B˜Û%T‰´jMIRm`ÒkJiEš	d¦¦)F¢KRÍ#e¬VFJ"i#.²ÌJX*+eÊT’P”e’“)3’fM³j–Æ*‰šÐd*’jSVm•­K2jY	 ˜‚Ý%\¤P)€IH’Tê€S!#)‚QÞCK P™¡¡`•§œ#)dŠB I™­“™:6CþI_TÄ7›§ª[oOga½ñ~#.qr¨7]tøØÀ#.õ3ŒÒmbßk6mQœÌ€:"y;:wpIJ½áìg“#){b¤¢?ǻ۷­nGç'¿±T=ÿ§ùGmшH¨=Û¨h% Å~Ï/³kÃg^8bÀ€ÍµšËîÈÏUЩ¿n@î!֎ŽY!¾Û›‹4õ6ëíýÕuÎ4Hѱ‹‰/1P™%¡¥FÄÒÎaÃlÁ;À”5Àï*¦ª{@<[ÀÜ6!­õ F—ðf`ë-l‹pF¨à½0OÕU43èG@õ(Š¸Ü2¯.¥ð‡xü“±.3Aû¾AïMÖ	êŠÄ©(wvڏ+y“4Ô¾•t~#.˜‰ÝSÖ#Èòé§ÞSÃ#.äê‡0z¤¥{]kÈ@™!b{NÑ6r6ßÕQ­»Ü]ÃbŠòÄûªt4I}XpV_|!F{8›÷Îkò@°I¦ø,Bv²Ã96>ÓéÒfwõCÀƒ£œI2ÞGËc#.²¨áFEٝ"‡o´Ý8\¶Þé±8˜ºŸ­BK#)è2@<!~›¥ñ{½[{vÒà"¢¤]À#)#.Qj‹mŒ¶ù*§o­­Uß<¬C@1Ô	€f¡À³31Æwæô25×)xóÍ ’ ¾ËÊÛ¿À÷¡¡ei9ÙéØ èâR8%¦ÔÍw|ÇN}üJ¿9Q‚ñ‡?.»¯ïff`;kÉ6<Íˋyþ+ÔR}Žç¯=퉐Øô†ýŸ0äbQYћۢ!µ.Ô	d˜ßÄþÀq伃3ÄQ´Eø‰ú×µíØ×¥ˆ²‘^NÝы\‡µ×¥úvåËÚC߁Ø&CeT`1­1²m„$,›2%PÉ&š(èØ`˜ÇˬAZ‰"•(«Fã#.‰•ÊÑ#.Hä#[骗#Zp`á$„f¯Z@ÍT6‚&iV*ÍN¹#.Gm5¸«E°M n#/?;ŒÂq$Փ#/p1qÍaàÒ·ë 2ˆÁ® ÇKðíMl€؄”ˆ'¥œ–„ý>°†³›=°:õª#.ÆJ-p*U0Íʱäq‘Á#)3o#._ÛqdÖ=‹&#ØLšÉGs!¨9$ØFë2;ÔƈÍËl=ÖÒìLi„jM‘]q⇗_#)ýޒKC)Â>ÖÕûüåÀd{Žñ€nx$và,3A®X°)kóúaé­ê£’b9hýù§Qo„}àïzîß”¯qWÞ5¬W|ßQƒÇóu¼]0иXˆ‹Zꆖ8«‚¬]š#/#.kßಶð·eÎ}‡Í…·ß‹ÙkÓpÚ°B8míS›Žæç2‹"p껚óV#¿ò*LïÚì PáhjCi½ý”h0‹|¤Úÿ=1êX!“*I‡T“«’MΔþى8¦~/zź•©ƒ#Q© Ðœ!2Xèªbmt‚&U¨8°`ÆYG¡àf X•¦Þª*‘*ÐÌ"^Ó0án.gCƒÝÏO|ê’á‚.ŒGV,•Â¡T2`ƒÈäW:áG˜@Qudiº¼âóâ.Zѓ¦ë9+*ZÓ«zŽÃœE,7DÕ+^u“\)¥hv©¹G¡ªƒ:ÊS–KCR,‘¼ju#.NŒ;¾öíõ&}HÑ,,°¬‡†deØpL–ª:o,ÕRÔɚYÄTÅ^Õéüýû ùŸ§éÚ©4¢=óÇ̋ÆÉà†‰LìҀÄ©4›=‡°Œi*&”qëEƒuɳŒTf(ƒD÷gŽ¼ì6ðÓ­R=ù­j,a§a;è«:MGJ$†gÃ/MEÙw^«Š×TÝDtríÉèeʾì1îƒ0¶Úgüñ¶Fìí]*‡wp‡Íèﯲ¦¯†SÂzq©<f‰R`ÓqI<_yË:N2µfê—¼Âǖç@å±àuÁHØÒD)’‘×C‚/rí4?.„ÌHØÀñ„Lì®`/ÍãÎäÎb΍|xðŸ½Aîí¡8½àKÜu˜ˆ=ûK‰"ã#'oQ£!¤ZJ¦™¯9YyÞ÷uǺӑ„ÓÝ]ÈÀÃ9¢®³…ƒœR#.æLÊÖhŠ)½Ìu/«¾3ƯeÍáÙp“¨*’Èn¾“6²"aª/ÂíÕçi܌EÇ®]®L¦étÔn¹Ÿiy—®õ¼ÝÍ#.#)ävÄJ7*R(˜ÓLí!î™Z4›ì‹aÑGßóM$”ͧÑyžhû@ô¯#×ǖÑáZÃsÚüS×њߏZù òß+х\zó×¹ìÁšÜÚç@'Ò`	fÀAæ[:3}±JxantŪbš­ajb›u‰£ùþïBoõò{á†>¼¿¦ZAÝM‘Ñ@òJÖ‡ÕÊ£[“l`/f²§-N>!1 s/(&s	`1Y#)ëØÃÊ)#.„(¡Öd•²Läq7ðèÎÖâÀIIPKç‰b!²L8©…¾dƒI¡oˆfa‰Û‰“jʪְÙC`ÂqA+yf(ZiŒÍãM«01%ÈpßT¡#/ˆ©HÑ] Àbi°:ôøN¦–Ðú…ë4§ŸRôî{î#ïü}|NåPd]þï©	¹¾õøÎ}YÜ©q¦¸Àætî;º‘{½yx“^w%éÀҘÓ>Ü8°f豅Èæä†È?ä±,#.1Nó×¥àAčƒ{Ó¾*´¡¨	ڃUk”XÙ¶XR›®.m;®”Ú5{æKkšßSšÄbĕ^K\µ{R­Ûï ÑAƒ!_Ýþ\Ô#)ÀŠmDÚâh7Ù0­à;äÞ=Ù”YûÖo#)Óã›G¨%¡®±ˆl(d‰5×]"É>ÈhB¢§¯DŒV¾z×#)#qZ\B,!Òn'™©>‰fĜÁŸ°üÍÃBdàDC¤ù±° —=†‡¬íè´Rm ùÁ¼#)(#/#.yQH‚°m‡×Ûv~º¥"ïS2k.G9@žã¨G§qçñÄþ>¬±•¤$“0À)	©#/H„¡#/BÅAÅPëûÞ<í»Lt ¦8FâjÑ.èxa‹åâü{ˆ^­$,ˆÇŒ‡ ã¥C_‡Ý·iö_Qvá’	áÌT¡üƒŽ$¡æÑ£Xøý½]À÷IÀömú:½7Tô#.Ór]Fè(_/¼é¶Ê¦¢®HaýRg$7W¯6Ÿ–½VŽ}LJ8¢4û¨ Wª#/JU?R<€ô÷Þ\ *-H¼¥ÕYsSï¨çcÏjözkÎR?tœM_<"V3îM½3bŽº›II݅¤~è¶Íôm…À†ðnãa Þë|ÃÐLï»ÞV"6·íæö#´pvv/€¨°³J	>FÞ©èr·`Tg[1n¬˜T’k¾î°þ¹iÐAŠ“yÄ¬/`«4;;ŽÐ!µ|‘¶rfo:ìÃò›®À>¯­uë%@b#)¤ȑ@†„ÁÂZR0pG%(R8‹Ú2#)m!@Pá&BñU[øO¯^!ŒkZÑ®úRÁÀŒÂrÇxM‚ ‚ˆ‡‘Ù-cP,#/ÍLj=pu7{1a»ã/Ñx1ݱü§‹v'Åù¸s|áx=çAðÓÀÎå»Ì{#/<ÙT@¯‰â„<ÆÓ{àÑ#.“"ª¤ßöÀyó#Að?›ˆ,‰ïe¬i²Ò|út`ŽE”C2{Û#¤èløœ’Ðì9hãèC-c¾µçZüX~<ÇԓåÆÛÑÜ|“ö,旅‚éà#/D›#)ÍîMòã¤0íÄ#.f›I¨`ÜÝúN®W£õ}G×÷ty/^ݱ	Pf.¿“ZMåÈßs2ÉÊ@2£a->Œ2ŠEM+þc?’uŸÈÓ)’d€sÊ0æ#.—«|¤+„™ü¦)–¬Ö=5‚íM™ËNJ¹á!iºB™¾Õ—DY”) n…$ŒèD`ôÐءÊÍ&:‘J£´d<–’õã«yÍܽž¤xˆ¬ÌD2R…¹a„`2„)M¤4Ò[xÒÎ cM-RšÄé4C'©Jr“„®ˆ†ª@Ë«tÿm—µI p˜Xš.CQÈë­0e² #:<cy27ÑædU4V,k–©½PÖDîQÈŠ¼^yÛ=yyàŒ]9ȶ9j#.“nîÛsjå2½zïOFOR€¸bcJ#.aŽfä3™¦pÕ§X•hÞDºƒ"íþ}fe„Œ820³8¸p¢—/2д™Mqk:7Z‰é8 8PÒ½°&¼,£ÔTڈR@RAlL»_<âÉ#.½GÀV=	æšD?xêÄ÷Ÿ†hê¯~œ— ö'24~…öÑ휢oÏ5JÉ3HS1+E:°TÍf&ŒdÄÇvM_ð¦Á¸Ÿ×>;gYöžG¾=Ü^*ö#)£àsÀ¾°ðëœêÁÂÕ@›¨/åRúOâ D°ÊGa2ZY‰6‹5¼Ú¢¼êädÚŒ+¹[n¥EX“Š1,p!!\WR¢tB@Ò04°zŸÁ7c£	ªQh¤.üŽgëž…“ì`#b¡‰•ùÓnc_fúEƒ³’/jz:‹ê¨bÀåk0‡	•\3¦#.A"šÃŒRvl9#)…A¶ë“	›®9éùþ|uZ/:Òùρõþ^¹í=Æð‡?'œCö!¢âŠb‚ëG«®­â®k—½äYXª-y‰’BäÃJí!Û"h#{¡5“cZ͙m‹¥cbº®À(S†ûFø8ËHˆG]¼ÝÑ¢“#/I¥6©6ړ#.ÊG‡,v¢ãP	BÖ"–¸å„,åøÁƒ©¨œ‹[FÓF#m‡Œ«Hß³A¬H²e!OŒð@ƒr”Ý`w!Ý-n@;À´è‡¥C©J×.%AeŸv–4€œP%s0àþ•Di ÁŒ©vƒúC\ö8scl:–;VLCì_J@â°Ú7w»G¬fÓU=§10™æÕ'wGeE°)kù{ºiðґ}ä I±Gìó'Kj»”t1.(:­õãAU ¸6õâ{€û˜½í$F„ìÝ3dÞ‰û;˜s›teõÒoLPÄÓ؇P°Cà GÑ4ú÷š²ÀÅÌ)(ٚ™Š”›Z6Êif5i¥e<•ÒZJ²’ ¢ˆJŒÓ邀öútéЏ’L·YøNU P`«	}šŸ?rï>}oWyB7GèGñò°ýç­c6âb_m”ùôa†ÉÊ¡1š¤.™¡­yÆìýò¯	—žxÑu¾R€¸š²k¶úeG¦Õ{’ ŒŠ º,TIA:µ‹¢H‡xҙÅË¡*†Îƒš¨\k›´5è#.2<É{>ˆ	ëø>¯P# ¤ªäðˆîïËêéÛÃÚ}_=|û c}ÝÉ¥_oYùôN‘BÍ9°‹!&̆Iþß׉$žÎFûnuü,ò6än˾»ÀÓê©ÐSù³‚ˆÐ´ŽÍ%’"‹èÆ×H?DI¯ïœV‡£TéfÍ@NܕîkÃE™TüYˆШe4a³ęˆ¡ƒw´”{€’~[pþÍ;¯q»ñÚL‘i¤#.&ÃaÕ¡àëVhi¹où¢T⪈Tgeö}“Ia±Ä8™©¤öi…“¾ñ“òÄt¿Š-dµëô1Ø3ÚÞYXÚ_+Jz5®ÉݼßgÐ"؋HÂPµ&’DÈÚ¥ZZ“j5£#šüä݉4%EK?D­sm™•M”–ÑQE0$"¦šFh•%FQ©¦´›Q6+FÚÍhikH¦k-6k[ˆ¥J˜™ýϜ@éŒIþ§ð&“÷€uÚáÍ"Nh›€<#)˜”ü¥L„(E1Œ•G%hÉB„#/@¤S q”Á¨€°$ô˜õùû8‹Ø¢‘Ò¤) ¦$ö!;#/ùâ“÷˜^¿¸ØÆìïÒ¿_^PRñ0,"5ñqƒÙ†>ß¿bàå_NÔÄ<лuõ:	]ŒxüUWËt¯üï#¼!è#)È”~P“Èçا֝é§ú’N?%ýà™™a`ºC$2\€ýPìB†ÚÅ6PïÅ~ ½¡¶ž;H’iñ{¦Ü/íâYÖv]8í¥P±Üò½QEzu€tª—'…ÉS™&®	¾å¹2{çË\&¿¤”#.¤…Àñ?1öù‡Ä“Ґ9ð7}Àøž„Âs¸ë<lÌtXm©ÄÀt„êxy¬è$æ Q­`pBÃAØ#/»vŸ/9,k9ƒˆÄB ;é#.ÐÕ?›”yâÂ˗jf"´Á¢AÆbPþúÏÍ„!pI4Èú<6Ãs&;X>/”¹5+«ÉeTé]û8ûbŸ¶O·¡ ñÑú¶£a4÷#ƒ€¢uщ,Õ´e‘$C\[Çñ度I#)£$ßýmxqϿҏ‘ͧ’ØNS×9òk…]:ÿYÉ®Så!¼ž»^Ìummj)[h"#Š””žüÔc!’Ϗ¥÷lq~\÷ž~[®AÁ((ðŒ¤Š&bA&I˜Ö!éQÙdVƒÛ8*scCAšÐßþ‘:àiw†9xa¬Ñ£B,PQJíTlò‘Z¶’2&š*eˆ„4MîÇo,­fRQuâyäÑ(S[íi	]›=Òd0PÒ,iì01CL#f÷p“Ÿa¾ñ-îß5‹u#)%ۏjÄ`ƐÔâˆOåµBçAÃü»·	Z$4C¨›ˆƒE€‘¾`Ñ$ƒãțrù<:IúWJ&ðè€q™5ns#zjƁ¬àJ)ʜœeËGC[l›ŒäÚXͲN AIHÍ4fCÀ°Ò”‘•EìÓµæ%åÖz»ÑÂÌBò€Ó&:M,’²‚À¡byÜ#.;˜#)ãë<{ôz‘=‰å ‡Õ··@jëõúN#/BSØ{föÙQKG´,Gs2C;ïn'Š~úqöqóv#)aÌä2îiô	à€¯,<ûzq␝º›ÐY	å#)÷Hi¦lôÒ¢Ò"Zõ*]×&j’£z\8g4O=Ûɵöp|v¨CÕÝ PU,BÁÞà€QKHùôšÎ”Ð¦Äe”*”ÝÐ!ÞЈ8¥¬±˜ô˜’D…z|/·DÅs[hø‚f#/-‹vÊ#/ƒi°évɕc9Ҧ­¤âqs‚8[„)%	ºaú4´Íë­óØ÷žŽ£]nKîïñ¼ð÷+T#gÖ&t Ä5ÍÇT5Œ¡ÇvC@ÄÒÍ1Ž@ŠpLƕخ®\§îfq€ô«ûiº¯ÙÀâ¨q&÷O{Xߧ²¦½.—è•°óÀq$˜#ŽýÀá¶H¶ëÈl–Ñ”€Ô:ˆÁѵÉUS#/nª–PŠß{϶}¦ñ#6ßÝBCe<¶¯À1È&æDØV"ëV#.ðÑBxk”£EãIà3OG,¨9àè$A½%,J~üéÕF$il]×Y-%eêq|7½˜Û‡%BÓ£T=)„NnÛ]6>^šäæïÅ\°ç®UÉT³¦c¹•çü_Á>Æß_¯@È£]HP,.œ;#.ƒd\ÌE’#/ٜ0’"U¥JÑ‘àŠQQ!ƒ=Mh#/cðò0=œñ§èHèyŠ=]¿]èüà"ø½}cîÍÐÉFÞF0˜‘‹?¶]~;œ·SÁÃŀMÎÏ7ÅØ?É($@)ÔwG¢G³ºÎ‘îí¢p–QÐì²Ý„æI¨— n~,šûõ†oC©Î$á>wpö*üOGVhŸÔBVœ1—T@Ж8X#/–#.Ejm¸‘m؄b!ª"ã#%\#@•**¢Œe€Q3ñýÞóÞytmžîú&/½¡i	*!vGx¬$rHÐ	ŸœfÙ>zX4BL.ù‚p<æjr€a-)™©Qƒ»Žó‹á­k9\u•ŒwXKnà 1¶¤iŒ3/2ÞK%«ËÊM|G¥W§¦œÝ¬2M°$‰:ãQ‰Mˆc‘ÐR&7¢mp™bGdÆR³ç$¦äU›‰eàjèΙ4òîêô†òÈmÖՂ"j4ÛfN,Ä/íÚ´ÐØa¤ãs`Ö¤UD,³Í1wèÈptŽê¤qž]j{àOñ÷	é:Ó”ÜXŒâvƒ¯//¹~Ÿ/Ú÷ŽþXi‰/òþm“¯å>wm?µðªœŒ‚Nf]8fŠ£ü\ÇS‰¢t>Aê#)/×ê" 6¢%}¡€D¬È@PLÈ¥#)A(zàöÂ>¤Jë­§v®X×5ºh×+šEú¼!ééý<ÈÏÈNöYé‚ä£.ùn¦qòw³0ÈæßÏŽÕ`™²Ögߞ'¨ÃãÏìåËÁUàb{äÎ7L=ýA1÷#)ˆHl$,L8ŒoN Ÿ>—›|œÏè°ëœr†Ûmdâ¤Hº¹{'ÕU’m¿Mm¾Q­IəœÈÎ8Hù©A(0çQmZIáÐ8Y6ë M6Èm¤.€z	#)D,‘¡óTÝ!ÂyÃÎÓOI͸BôçûÀåË1BÇEFúáÇKS$è¼L~i‰Ör߃ÌÑLïΛø<陚Ú)–‰S5ˆô•'Õ4£é³û‹ÒüqMÛñ#gz8ðCO½ØdòŒaä|40‚;ýù×£ì"'¡|½øY£C%[¾“¸àWKç#)Õ0rÄHg-n͐°ù¤ÄßÍ6`yzP߆E›9¡¢Óœp£ÚDc݌6BdQê7	lž´æ͍#Œ&\Î_{“áJÌjjíÇgÔgԚŒ1ëCI,",ª›Õ¬è·4#.+Fä?*sšNødÕ¢=éO&+#)”É>I!{P[_óóïÃ>#/†èm¤3Ù¾N2²Xèñ‹,Ô{-hÄñZ:ó)JZSh*:áuvD84'w7÷#cñûºg¯‡D#)ȅ~K°º­N¾ßÐT·÷¾Ô7ée¡uRÄH*rµ…Ô–$@qÛ#/”$#.ºÛ 'b„r`Š‘*ý¬!$Äa¼‰Yä“é$q6O‘ÒÆCP™¤ÔÕ¶33‡£ôI¤·¤ã‚wyÇd‰Iñfh&nÁ™Žß0˜<s»=]Ò`¬iIIµß&=åFäây§x“R¤,YD #ÆR™k9é]"sÉ3‡B0ñ(ӊcËU±ëóhõè¾°JÇ>‚'¾ƒøw‘½ç—Ëñu¥*6ßV˜ò!ØýgvQb„(XYlJTÑVúÒjCs0Èf—T’‚#)"˜„¶e[u[7JRñrMuXâ@LÒªBÐ#/ž;nöc¶óÐ;àxýsCOÑ©RaĦb6#š–ÀHçãQ@¶MR@Ϧ¹¡&¢FHºýöæ¤öŸl­­Ï˜Àc4Ì$¯g˜¹ËâlM¢3<­Û£R5u%"0“I2Ëþ¾JÑ'r¬ÌG}ðMÍdÚSjÃAæp}V¬èbE,…” Æ4ù#.Uq0Ùó`ÝLpC4îl³ŒE}ã”/­š’ށÓÓ¹öbœ8+ö§î>»·çž“¯§µdÃƒ®:JìI¥ÓØÍsgKvZƝ|ïåòAi&`.fa³~“}¥v¾çŒ]?xŸ¿Œ}%sI“ m…ŠwSž"ÆÙYü×®ÒàрÞN–&Øcø&,±$õ±ÇÊ[.ÖFD½ñwýŸî~ïðþü-kè˜±Z&|â5¢Q„dtÉ@ÓC$µýŸ•âä˜b1#.D2V¶Ä˂¤ƒ?i«{@#.Yûö›Ü5¬¥+Éo`Pg™SPÕ‰ƒÎ5ÐS睙V¼J¢;©*®ç"`®úZOÑÙݝÜqAú,|i¦9:Ñ3­œ†D¦5õŒV@€îæOâÔ÷µi&	`+záÒý§HÝÈy3d(ghc"Ӟ5ÂQ€ý†awLÐÍùT€©æðÈÀ:ìCB«‡BÃFØ*Ì,°óûÏî‹5Ë29#Ž!Ì6#Œ<Âðª¡*ê[}½æqŠX_vG\°o“ïÎu¥YÒ·ÏV›jf zˏ”%È­„2gª‡Ñ‘ÿraB;)™ç=¾YˏbB\Z{ŠœóÛ0t/ÊHÍnŸIˆèPÑÇð}”͉a äÓæ³ÉÄNþ,m1ð #/Hi#.‰neÐ]Œ=ômü-|i÷q=\ge…ou·I¡”¤jì}ÓÄ÷Ò[Iæ÷õ?]ÞåÞ_ÈAæ€*"7}/ƒØ; ­üÜöu; ­b’wCJ©UJV„ZP#/™DˆE5dˆ©’¢s.Ïn<Mx¯×Û]Š#/¡dÛùd™…•TIÌã0 ‚nk˜Ç^ÆYZ!šD6Ó¬q #.ƒ÷#)5#/$y•¾Ð#.‡ó»õ›ˆñüøuuޅk¯ã,º):ñ#)ÊƖ'@TjºeHVÖ9©‚ãyào#.ÐíҐOé­pwOÓÌ>¡ã kt<§ù¿ƒø_˜Y9#)a‡QñWëíæèsŁ%’t#/a•pP”xYæQ7Œ¼Cøš	³#/ý…Õ#.¿Մ6Ì.3ƒ™Ÿîw?‰ÑÇÃl9è#‡–TW’Áßë#)Ìó1U•HLÈcŠ~%"”„üSWmÚQoÛµí¶˜%	¿ê4Ú—d>'_¬#)³ÐÞk{JÐÃ>l´…¡É#/±Ièܑ¸Pú7ôùÛõSÏÃ"¤4ˆå‰’:È<Ò¿…©5ÒœÃ9h„ÄIǍüFÈb­ ˆAPIÔé8Ö?wê°&Ä6Dº±Dï²ÈvTŒ,©€kÅ>Þß^höíËÒW—yšIþfWÀÌÅÃ(Ìc—”*˜"båÒß]îÓ£5!²µ^üÍy<M¶Þ¢Ï´ã<³ øùðÓ§ig1?oó»t49UG«q#.=C<“çôúµšö÷⤠û>êì‚.¡$Hôi0v)¶÷#/Ïìçöw³7³È¥iáeûØ¢|—6:‰öž9‡uųÙÙ±¬©»kiû“D8Βbi6VЛa„m4’Û¬»#/Dµ`SŠZ4ö¶¶ÞÙ âm#.bqYj€uèŒ66hYˆ…3«¶HÛL™´ReE×uŸÈöbX…£)—cíÄÖ¨¨j&(fÚÃgrÇM.o¾‚ÙÈ Â Ó“lŽAêÚ7&¡D2hªŒ]âf8‘†9¾	ˆ}0ÉHFՌl^Û°K'šÂ[#)€/÷ù#/h&)@£a/QŠA˜t‹Îº»¬´ÊY4·weдKÊÛia&’’‚«VGV8@h3$‚ªÞTpm:ÅÐ#.M”ÊbŒÔ–šÓKe$£&6’¨P“f[6)j10ÕJZ^ÝÇ©ÚÊu$€F4›CPŠ1±F#.P¬-•FÒd+S·ÁÞß|`©¡ ­ê‰t·%0d­c9ÅÌ´Öå00tm§G³W#.!4 A ²*RáeN@’25º©8áSoCL»P£#.4lQÖ-Æ+¶13q]f	4ðˆg¾‹}´PÜSpZk‹C&æ,¥˜TDQí*T(³S$ÁrÀ1jKÛk…#.½wJÍ<ÎêmÃD­Áè0ә¬"¢­¤Û²ë,nŠ<¦0·„4HîAFøî1B¶¸h„È	M#.™#/ÐÂTBÑšjX²)X1.ª…ŠF)¥cÚ]X4Ñi-+HI•Ôæ61¡€X¢Lhc3µŒ3~̹²¡…Á1/	J%•„´]PȅáÈÆÕªšM*Ð1»˜g²—î{°Ó?C¯ov†M„U…=Dx#.7&ÈxE×ðdÍl%iá™DÉìʼnc1ž>3g„A[ÊáUa—#.CÃbðe¦¼sÏ#/½&ƒ4e¬DkCÒ7ᤌbÐh´ø1†Ø„—¨Þ¼Þä44ZJßÝpŲL'£$â~î¡ÌмoZàÉ!‹ÄÜNÍÁ‰AœÕJð¨‘K|jB„]Ä0” -1)šÂJd¡›!Š£#/V%ËhO0EFƒX¶šAªÄ¨ÆVo¦‚šÞ·¢H›Éî‹BtÎQY¸ÒÌV!c…‡gè(ëL‹.سZo/GÊ$¿|XAb	R_ّ0@»C]H mkPN£nŒ ËQiHÆǯ }°¶á.Â<L4&–V ֍@’é” 0eц‚#.d"bQ62&D"¢ J˜¸F‘H«t6¾ê§«·»Ïñ=ÔtE3å~ÚbŽ®ñ~XCçúæëë`=îk«÷°òj{¤6~24pš#Lx}¿rÞôk9ÏëõnzÒ7!Ê?d˜LJ‘ï=†ZŒBÈw~Ý»JªhŸJ…éAjÉ¥‡z¸wv\EJqB§’«0ÉG~Ð׉|‹&ß5™¨¤¢—pxx	KSMTðdNf™½{ì|ïFÕl×ò.Ì%Ä(#q$ˆOqyÓ¨á½(®ùéÁ¾XMŽÑÈD:,Dn1#¥—¬òÙ¥DÒÂ‹1ÁÂCŒrÀãMFÉæzeÊ嗫ô—¶žúñð:‘lç°º(êQG|Ü"¾êÜ6¸Á¿ârÊ~ÜùÃ[ÃÌò*¹=Q‰Í};3ƒ¯dtç’!^\08|1š”’Ľ}ªì‚d÷¢3âÍǙ•6ÛAŠ"žu)¾åU|d6BFbÒ{a¬}3ckÜŽ¶†@âFù€nú†ÕöӆV‚=/p;ÆN\Ô€~?—¦qø¶ãëS¼~ tÚRŠR•¨€!è?5#/Dvc	и›)¤Ò‡á7J}Ø#/º@퓂<­º³šh2·#.6TM”$‘­:´†¬$ˆYuP&M’®ñɱj6òêK]µN܌ÍÝxª+«šYf¤Y±³f¹Â§uZ+ITª‘„Ër\%B#DƒˆaÒä¥"º,0]ƒPy–bjdûø‹CMÑ锍CÌvªS\Ï~ë„9ÉÊ£Ê@ F7U#*ˆHÀ€Ö•/BÄlÁˆ@‡téc¡€g†¤-6&êh™(……M`!ñ»9ìqŽ-I`!8ðB”¡À™Z/õ»T2‹lVLÊl[Ñ´ÍFÑQTVٖŠ#.FŠŠfd¬ÊI0¶hѵ&’¡FÛM@£ÀÊ0:ß··B‡âàpOGÜÒ5ÐMŠ’$R¢jˆ€t¿CÍt½kÛÆ[ù›SG;ÉβM#ø’U]³¥fÓJ€‚@¶IDˆ&¯M– –Jö@úeW(‘MJaß9ˆ•šyuÒj»©6»v’¦0¢”Ä@æ8“$ªÁ´ª)	RŠKŽÿ®Žñî:÷Ñø7 ŒÃŸ³*÷&*‡âÃÂ#)u$Õ†!Êk׺)¥b€°kDë.€ýiÌòïë/„* ì3×¾<ÐÈX8›>“Ócm*p’ѓ鼧êMg³¥{wö`£òá¦_-#¢z	 üqEŠÞ}ö¥ÑēϬ'-c­³1bÆ1ÈVðûìªH61ªðc˜Ù	PB™€Õ«2¨=²º$šaƒd§ò²õâ¹:[¸|¦¬ öç.“0XfKyÇ/SÛ뜍1°èrJÑÐ2‚]؈nú[¦~·˜å¥™±™Õ(àc™twaI¼eƒ0•ŸC±v9³W\(6á¦ó~‰«‰ë²‘ˆ,‰N—Djº§;Îf‰²u,¢/jh%¢°Ã!V=º/6PdK%´ËºÙâðMþpí¸¨*Ä~Déèàݘ£Ç‰òØóLë"wʆnɄɼô瀟¼Æáþ±ãCúV?@äŠÄñì#.&#/y2o:;66¸÷%è¨þKìAù$¾ðäÚÂE~1#/(P#.D/ 4¨|	4AACýÄ%ˆã#)œabwÌ#/#/#)É4_Æ'¨a§_áÏ_Lт(}ˆ^:ÜÀ}#)ù'…}3€ÎL\û†Hb2Kˆ‚I‡ÚhÉ8J#.#)@;A¢6ÁihѨqÌì¥ Ó¾æۉûC€n*Hl¨æÈ#ÛU 0DÑM*®•¡ç³@šÌr(å*Ø¢’UÙT“½ÆîB€“’aÛP¯vV=è¥cUá@b#.36”ËFjÒQ² ­¸‡«XDÌ1Ȓ‰@aERDEŠj`—¸“O"¤$„5ŽƒôâQ`#)&pQ 91xÒ>¡ûï·1	I4ÄÉí£~»öðòq%'€®f÷D?yÛ#{	§FMBRs¬íbžšø‘“ƒ,IǎçÞr#.	£ÅãÇøy™€z;ÀÁ#)C¸•T}Fˆ¢b‰d#.m&bi á­¸ÕO•¹¸ÝŒÇvM3âÙò•´þ¨܊ץ§É¡¬@¿s30wˆMv#.?rÄÈÊt˜ü]ÈsÞ;àǂ$ù§lýr4¢gÕüü–ÒՕœž<ÕkðFïFúgj\cüÜs[,¬é$ŒES‡6s|¨š\{7äÙ4¾M¿4ÄÍJWD™„,¢yS? õHäZê½Õ#¾>Ú}04~ ðX#TRÔÒ[r@ ¶ŽÈaä`±‰PhfBzµ¥‰]G·ñ*&óú‚±31ST?=ºVõÁ{6‰Dy¢±Ðïð›·\<‰?¾æ„m–ŽÙ£#)‰)%j_˜ö?hǗPó&óRfXádHà„„Š!šý»¼W$ÈOYäœ@WñrnÇhÕ&e€ŽTÊXÂ8Dß Ó¥Ó÷¾¾Æ©¥£ƒf³ZÒM’¹RFç›Ì¨Sšºó˜jeF%±”ÕxÖëKX´PIcFÁot֓hîuy¼£¥Ö×6îíÒ+¥âÞ6¯r‰$Ñ£Ë!fJŒ+@¸(”jך­5;­¨Ö¢³MN×T™*ƍ§w.»­Ý•×[¢ÊÎë¶æ®¡Î͊ÄVB.$¡݈ vºAMWëO®`ê7N aôõý]¢üt‡	ù†½A@èã‡ó{ð(zÔÿ7­#.J1æ—Ù#/·Ø€˜¡ëe#.‘Üñüê>ñ<SÓÄGßû'­<‡iAÐՈ;"žØ)Xïôù¾¼õf9äkê߬ëï‰L±ï[7‘½€ê«“/cŒ‡Éáƒ&ö֝g¤=Α²%R‘™ª¥@¢	™#©#.Œ”õB&\k5†ÇüfƓü²ŠLr¼ÿo~DܨbQˆ„¨„§„ð$úÏdm/Ôª<r1¥ªX2À0Ì	$(YÌ%pˆôiÑQQ¾ƒ7×fcšÄã´¿"@N1ÂP-b›UH&1-ªù½ûuËW›[A¾€àí‚P14ÀRj6Ð`E*„!!#.Ît–ä@²R4 ØÈ'ƒ÷óç#.Bˆ}‰VÔ/ùü__¸¨AÂC9(ZPÉ(,Éõ?Ùþ×såYP#.»ÃpœÈ¶hT€a’Td‘ZU a#´#.¥xtüfÉ£ÎBˆœÄâƒÈVV d#/X ‰‰Z@BT#/|}ǵ€ø 'hJ¡Ù~ͽ墔¤ÒU0SJe*ÙH…‰P @FÆÁÚ*/úaߍլo¿<.ptCë„,xÀš N"Ô?o•3#ˆvèH(XPíðÙ+X.m¦û:|rÃàÜñ¯ê)”à„¿”#m¶û¯Ý¯#.¡Hä¼Pz°ã?›;‡0pl€Ž&’`_×ͺ†ò#)›+!!ÝU(k|‡ÖÀÚD4¶kLၴ38›ù£^Œ-dGø!a¸0¬‘…q’k0TLF	˜P‚$P¹6àë4á"ӎF*‘§R€ÁˆŒu¤›Jš7Ac³$*FÔ±âÄX@i lv¸#.#/¶Ä6Ôxªå‹Å\£oVjÕÊÚ#/ÆÑ¥ö¥clÎs'G>OmÍ]]«ܪÁÀ8QlÂM Ì60[K¯#Þ–I#.kùEQ£MQhÓÕL	¬˜µ4d™¤`ähÛc&EZ@"iP•Àà “&$Ð ¦-06Œì«°c°Àhv9ç–.	3ˆcäòfÖ°û•ðo(¡ä‡:TA'"Zh FƒHÄvtÈm££ËŒu¢õÌT(vtHlG BI ¢¿ëTüA!Æ>øPÈZH9	†ÂZ21I9²P+ˆ\ÑÌŽ÷Å'œ8‡¼ñwÜ#)SâSþˆ›^<“öUîìX£i$-€|`´•“T¡ÇÊSèÍ&™Ë\ŽH䐌c#@6†6Ùd²a ¢ŠúU1†ÁFL¸W¬alŠ•ÿ«…Ó€i”f”U§Éý'gýR|m¡óÈ:%Z-äÞ¶ç#®Ç·ü÷0Ú²Rõ6¡µ6n×RɌm#/Æ9Øa1éÇ#¦ÐõŸ—)HՒ“¼& ä¤wôªPƒØxv"	Ø©H¿²A#)£#)û0p„™Q%¯#)õ†4öM¥õ	æhüâ¡î_ÎÄ-_ï¯kHc·¼8×/8öÿ›§éoCâ‘‚"JHŠJ¤ Šˆ/¹BŸÄBŒ]ŽŽùíBÀò°ÌÌXó‡Nh\=Û¸^ÞÒmO‰ÜdÍ樮~%44j@†ž¹Cìû’¦Æ¯¿7àÁјӍ (z^5!Ž‡]Ié‹TÝd”©W¾	„1҉–A©>©ÌfüùÕrøÖ¬Í8Ì~©qn»5ÈO¢5²-œaFyÐ+#ID€	¥ ã……Œ2¦‹€ÌÍy†ÜM£ˆÏàÃDZ€ò$ĚH1&áІf¥é0pÃySèB¹Û¿)0îêù[ïKbücÚùÍÁ¹Êª£ÚG9Ë(¸Å‚^ȨwšråÔ1üöDÄ#.øˆÖ{VÊgBÝY<ƒo,?¿Éa­#)ÐØ0	“ƒ,#.­iÔYfҚ–4Ôʚ-¸áØQMëhÆÒÓi5”†}RI"'c¾l¶d~ÌÉ:4Nx”Ü8֗ç€Øć«NYÍt¸õ#/l½‡`±qRºNå‚ûCÏG=™¾nÌZd}hŽÛvGA÷Ž8×)k{#/rš§‰©«Ó¯1åÜï)ß²Ú¤ŠLýÓ*öö!þ¾s§°±’,	°véÆ̜ILÌ]Ú$p@e!#/™,!ȯ²¹ñæ<=½Ïd.ý5ëÐåÛ©EŽ:o2ˆa8!èTû•U ÀV¦#@TSÄ#.ü}¶iÐp™LqÝøÂNä>™Å^ËeíSŠàx¥Ž‘™ô7ºõßKÀN¼m(LÍ¥‚D4Ä’b‘ˆÝ.q¨—Q¼ÒÜåÌla«@l±ÙÃÛ“$gh’¥3\_3ˆ=©„i=IÔÿwED„¦$ÃP B™+ˆ>éÓ[5ãœcñæ1ƒÓÜ`“±FØGiiŒ¬C`›¢a$ #.±Ôõ÷ìMÚlA@Ú2=qY;W“kôkáŒéôg¨MQQÃCZO]iUVQUF-¯_·:Àf©‚¨³êÛùj܍Uä©6°îÕȘFÖ㹚”#.RÚ΃žÊa¸†ðs%~#.{–ðÖÌÝvï¥9:,²H›ÿZ<æžÚRǯNþ-IÏv.Èá}Œ$íU-Œñ8Ôáιi±p¡A-¥Ðޏ)¸ºÛÈ ×=M°Â"#)a	#/E'x\¢3°è™aÃN.§|FëŠwëfõ]¤2Ç)¶]ÓnIñËґ0î´ ë£ímúe՗°'VIß~:÷%T†IÄçž8Ä0<Ã]K`~ælNJ&̵92¬PCOF`ø7ã‘!Uw=]+`N^¾8g—N^&.™KÇé;ªª‘˜©,ºØ`>ûC+ñ×Í÷øü+šÖëԜ^Ÿ¡va’JÔ#T¨{ÔþdŸ¼ÛöÇäÿŒ;>D¨)@ÐÄ)D_Y¬ÙPêú{¶;i¿9úñø?Ühë`ōh½ñdɍaÜzçê;˜Ú¤ÀÍüÏ6£´ŽAʦ™ˆ#)ÌTwšô·+¶½Ç´¬#o%ò›øq;ýáÊEwÒ:>ÙéÉW4ۚëa¬Î˜9lyxM?m¹%]XÑOzuÄɒO#)Or)#.6þêûtJë˙Ÿ_CïI#.w9Óq“3#.›ÐÍoeo’ÝpðoÞnZMzý<»ˆr4+ mz°}z2þÕ¯fàE#)•"$õÈ°†##Ð<	’ ‚ükÁ۞é6;Xá^„3˜qô;ÒЬLzyPäÈAôDaV_Ñþ?±NŒøwTý0…›Ô#)¦Ù#/‚Jvp“Cº^]^ààƒŠ†e#)rUЧ4î¶$@É͛nhúÌ	Ú]€…™ÞI'÷t¾éøAAS3÷¨&x–ÌV|tÿK˜HÈå©èT¨àljBº;!X… zÒ4_˜G©™N±ì@/#)…œK¢Ô#ÜWÎ#.ÿ›¢*Ô¯aU(41üÍ4L6mˆúÜ#)Ökí[æ¹5íK‰º—IZLõ©ù}>#.@5Öí$Ýók;“úßág#/R„œCºeùʤU¦Ün(ùBG2#髬Jc¹>Ïæ8ö¡Êj™:q0O<ïC4TV·¨»¥	È¿#.…Låšø‡ô4x$‘;9-öP?YËQ´û7Ë­ã4½:%)E,ÔÙÜQ®¿V	bÃP9µbÁO}BF1~Yts.†|íaHŽÈ":qôùpâÂԂRUHҍúˆ@ˆ)h• ’BZPP€È÷rápå:T u4>zý}ÁO⇓¨‹!=˜ü>ܦšøz-UT³3#-$iIB)°ÃR†J4ɈÙfŒÌ™bY¢PJe@IUT‡ä^ޗP~)*@iHH;u&c±6zœêë¤öDԉµÍ\»qµµ‡8msÄQLà­#.s®Z#)ŒcM{AΠ„;úž,¿¸¾ÄÃ+*qÝícŒ³“ß©€Yyxƒ²ñZ ¸‚Šc3å9ÌöNÚMžŒâŠìM#/ébÕeЭТibêCØ„0Â9ÀPƒö#.E#.¤g(¢F$f£òæ|É!܈wõ!®¬8Ibp˜áEkñ"ƒËÚ©Õ¤¼÷$³¯uüÓáMP¥ڍ<úþa˜×ð“†cÞ¢Ö pY¼"çÆÑ"|ÿŠv×JÉÖ¡ˆ²ØÒH)?zˆŒ†«mdÜ`†™ àF“``+b¨hˆe&q }vÊçQjå¥uÛd­ÙÉsp`Äf梡H,MÍHAŠ0Yj˜D†¦#)´€‚ÂF’;á@ðš¡uDB;™J›Dܝ~LÚ*…”&CRäDˆàãCPb”äqŸLÒ&–üŦ=#á0F´0AX¹iA=ÕÃ4ÇèUkrBÕáTÈ3‡VSüÒ¿o¸ÖHGÝÖ±ó0ꮊ”Ñ£¸x1øD¯Ü^#/Ž€ß,6ŨÛE(òŒPN’‡È,Ž2XBµœMM#.&´g@ÉJ#.H›0‡\݀ÅÙ)ºé×OVÝË(íÕxÀxٔ9£ƒ¢–SFiIœhƒ!ÜHV€ˆ†è&6°p‘P#,E$Pæ(°ÌՍ±±†&áci,Œ¤sYTL®V,ydÊÌ‘¼$Fn",TŠÊ4ä@©­ÖÕGJ±²€më-(ÍHc£qD Ñ)4(AÕ_#.½<æÖÓ¹³oZÖo(žjï7»I˜î4Bbm“l¦ðS.–]-OÀåMž¦ÈbRdum[º”•Q6G>rVðh+U8æXd#Ã!KÀ­F@zÄo mMaŒÉjNí¾ZTÔÉ{$z!°š\…Fm†d¬éT 5uCt†µ22WF4<Ý¡’£TÆ6uÖ¬ÄÜQÒE½³DÀ`27nÑ¢ç.‡r0$yªŽJ±¤’¸l#/F³pa‘£IƨÀÐÈEè\҃[3Db¸àp0Þ¶8\m àÑÆA_/Î@'ž¤×BõÄKHoRÚ­,ʤ»œ²#.T,`eՐË][¤ðßxD¥Ú@È2Y2LLÂÊcH‚`ìƒmV$û¶&B}ÅÄ#ot7ÀǓFµg #nVÆÉFÖ:^²12š"D¥…+ÕWãÛJH}Xk#.#‹Ã›XÓLÛé¼#/ª‘­†l‹¯JcÍLˆX0Ü#`¸g– Y›0©4Å2Æã[¤è`FfÙçcÓÏ0æÓEÎQ—€Öšý€ód«Àǃ|@ˆ,­=¹3#.J4²lFâ*¤'œ¢<ðáׇš^àè5FiEµmºX„â8×¹¶&0<]4n#-‚8ÅRE!gRf‘%£ß¤#)Ò\@cB(m*!¨ ŠEKƒ#)“€Ã„j´»@Ðw9'çìí,©þÉ4þ¼È2mfB¡ ÛL‡òºHšƒ¨B0Æ«59Ìèa( ªT(¤¨BZ!&Z&!NŠ?ÈTè©P?Z|ý`(yy/”àôOwîíלZTzˆ ߁‚ùƒa‡Ã@’è7öœo3F	»É»;].Zz!Ç*	€`2fX…¡RaL9‚§i+± ê9›hùD?3Á˜{øì.<>š:—k*h#bS¿f?yܑƒ•·"sŒQ.!‰Ê Bí*Š,“7NÙζ ­ÕÕÒÚÞێ¥Y˜è!B<\ÃPy3‰°`ï§u´‘'®¼òQ±l_#.ñPrçk9Xþ…,òÎÊ$¸njnËpÈÁlÞtB8€ÚS®aǛþU ·KßÖé`oy˜¶5Â\ˆæõÃ]O¥Ä´¿Céåc†ØA†#.%¹³#KõòZýÍuhÃL_BÐ(ç=±„&EüBúvÌø ñ:ž2¯ì’þÔïÜF	¬Öu/0èaÁx(a£ïf#.&¢Ö¿SÈ$çxe”ô¯SÏ`=WiõÀ3Zí}ߌSî=#ü}¾îÌKñüRÄVìø0Þ¶Õ=ì0ÙcÐÕ²a†ƒeÄ泀#/¦ÀõkåeÅEàŽ1'=£Ád×	d!±(ÌŐT4mkí=„31#‰Ï|ö»Š~"TH„XWꇑ#.]‚– ʨò+¦z&ªÿݹ²?ËÌôÙË«lO#)ÉW±ƒpäpùjêT >¯ê	畒VëÏ)o³ÄíccžT8î²TJUÒSÝ°p5TGƒ>é7K•ï?GWY«rÒª"pbÃM°A@-#/a³ )¥Q6xá›ØoÎ' &¸Î›þí)€RRBslš#/©ýUA”ª¥íÀÞM4k2W†³SX`ãT¤[FAK½’:Ü€w‘vhƒkQIµxæ®Z,oʼmX£¢V@á#/jRê@)	#.k!‚ÿ‘a(º„F- ´¨3‘cDa#.Àèz4TÂØvÕ"}#)ُsñN_X{ζ‘Æ#)i¤ #)xÈ)í%u#)âªæØÖÿFãÀ•6q&NÏ.¿NgíâžÓž=Ïm‚¿’CÅç)‚܉Çö(Î@éÌ¥þILÑø”ó¢§d´F7W¯4õdB8É	™Bá–SŒ…	åë6ö`.­„!Ø	¤8Ñ7%(¥U#/q¢T2!ªBDè&©4éXPÄܝ'y¼=rB¢xŒQ"ãìȁ®B‰Ì<؟öÕCàªÂOò?nÆÀûÈMÅ>Aî3᣹Í#.‹Þ#/ <ˆ' |¸·x¢^£©3oJ¯àx7ÞaBxâøŸ_PqeõÈû¤DááP<ë5Rf¥Ee6RŠ"4¨ÄM#.™´Ë)”€•ŠÐʵm¡	IZV@  hX6†#ÜM“RØ ˜³ VÈLb#er$ØO³#/\˜ÔPËâ(Óc¤d……a¬#¸!+DI1ctˆ“!E#)l5Dbjij#/34ÚEÆ0m;Æ죃„„¥d*@¸BŠÌ¨0‹žÐƒ#.¸#/©`’ãþ:ÙwòÀt #Èc0‚Š"îa#  PÌ5­g¨íCW\*>\±¾|>š“Ó—»Ìëðõùg#.>ŽQ–UwõŠ³Jí›û²e\ëi¾ä„á,(ƒ¬¸µf“c—>ÇG—(ÛB’`3eMeK(Aí:Hëuí1ƃC5ŽxRbHÈ-Ì*1¡²ÕµÇÁ:“̇¡S¼)˜ˆÒEff­¢§BF@LÌí‡ÙÜG$iA¡i\•p•3cBSº;·cÿÍ	3ý)„éOý¹ÁcZ×jkŒÚÅ (ÌÕ"‡»÷kŽffå#)“m#.Ú#.@çÑ}`–w>Îç]ÜÊ?§/(í¬Á$;7H™3㊾®'q#²Öúšÿ/ž¾¹¼Õ6öé=tDÏ^†qPzig;A¾gDívŸH#Š‹ìîo§[£ó°t	§\èJâ-¥˜A#.4.Äë|o²<\!5×9§ìv¢½…4òE‚ïÁö&Ç#/ÈÁއ'šÌ|Y§*;¡ÐC'R¢iÖ a)(ýa㈏_Gh˹Úä;¢—uÝ3áøD bஔ߱šŽ™ì‰¦#/†°ÏœœôÁ“;8Æ-›58ýß;®Ffnӄ%OÛTdÍ{xœè•Ì§ž’Žå?‰…àsŽ®Z¶†4@âqº;hDôdS Mµ®‹ü^æ滸8¼O›s£„Jt7;Q k©Ò¼ß#.v±Œ1ðZ-΃oáªóåîKèܵ˜ôô¹:‚§ç!/˖wß¹÷ï ý1)ÖaÙóçˆ×^	RçåórÀhG ÃËËó„·ÂÑFDÁ$}$R@;ŒÚf#)ÚfKtìÁÕ03ÊÏ[‰'×ÛüuíLR=‘ÒŠ·•äz»m	'óÙ:X£È]f#/rA(éP±þ.áãÐ îþ2kŸn›š><…;¶0”œøôbJv&»|ïЅaY~š’¥Β\[2ΌJ_Œ­‚~n‘íeSŽ‹LðŒÇr1=ÕIÝÇX¸‡(wö£‰¾Tu1›ð¢¢²T—Þæ˜ç—^Ý&ºwMôHFQŒ‰d™ÂéTOX–;w‡Õœ°vրñ—`äØÉÆjn<]õ/qÎs%Á뗸}ý0Œêé™Á0’c”FÐßIÕI²‚´“¬¿ŽœØª×BMŒ`¯›À‡N<¢‡Nt•€ƒ­NÎfp¶9òæ”HunÎJCÂ%¼¤êj혶pÃwéÇl)8g4‰a‹´cEar#¥CIšº/4ê¶À×GtøhµŽ§’‘7k8'Z3€-ŽtyM#.«wƒpw#žG/®Úö­ {I¾¼¥<’ó$¬Wrõ½©ÑóÄÍyq«ëӋŒ‚ñhuz&–…sg­>ÞbÁPã@×Ü[[ü‡Ï‚'Y"äøˆ÷ßuFÇ<Aàäç©7ë}ðiï´»w<½¶sEwv÷¦é»ýW¾$Á'‰~ŽîHñðíD{*¼v—]þ(‹wî±è3t—Oß#*“NÇ웁¹ØTç#ú68ŠmŸL£<£ÉäZMâJ‡Ôõ¥Wb-÷Ã)3Èt:ôm_-¸ºLxMu¯ö¸V$Aí—f‚ºOëyHñ~Gz#/Ý3éõªe,¶å³\ï°7Û#.¤C	ˆq¢#.&/"’¢Ž+]$r(FÝr1¹“jÖÚho#£2)ï:f°˜üÏ#©Éq¾£·°³Ð	äFªcŠ"hjXÁåGbDœq‰S"Jç}1`Ç£¹ŽC“¥×Ìì’ÝîCڟÎAMDQ ÛEÙ#/…òI@‰€äöë4ÑØá%Ï;:1œ!û/88Ï,Aé¼³TO_:0K#.äøà)~—±‰)°â%nÊàp¸¢]Ȉæ”WF•Ã¿šC4Ÿ™¨——Uâ.MÀÂÀ¾ÝC»@›:Á‰D&>>BÖ$‚ì]ƒŽ£CDB¡Û°ÑßÂtrÅÀ{rÛXÕºòa’K.Œ £Œ7–9z²ûk»ÜJ[i 嶄ɔ°v²X^­Mb¸˜žŒxŠ¹lhD D"#)¡#)gŠ¡cªhѓí•ûiUsfvÍaa´p#/š‰,ØMûëÑïñ7#)А8p€Gd€@0{lÛ{éËYZ$€¾;>¨3C ç6Né¸Ó³èGòC֘oI˜'|҆\"z§š¨A֟>}ŅÓޖwìíõ«D´Á©Sl䤐JLœr\p÷CýävMd¸–~¶×8[lÌ<o18¨ò8r]ùÇRy®è„»q™ê(=Áøw1”'ïlê%â3®'—Éaí“8ñÄUè;'ðç?+†Â3Þh&*K4L·KóPtcbr<·{s©ÇÓA×£ôÂXæt˜r>ÎUôŒö¦¥âƒÉK/™cºã‡æ»Ç<Ú.“Ä%t»S\Á;§z;Æa‘Q©󜤖û.–5o`#/$ŒÑ#)tïÒRö៌!úpSÄæšZyvvÛ½Eò5݂¬×IÆæ4èímÞÙ9â2#.F•5Þë¡	ÑïžOº‘¼é»Wæ+#–4bô…-²K6¶Mºv]VU`†¬)–:l½Ö“=iØÃj¥×Lõl™;¦Ö¾8[,³ƒDBæ¡°VçÞpê8¿ÝƒX1³Ê¸£ËOÕPùëÚ%݃Rz­ÑI« ‚P”{¤B՚z³=bÑv¸Î6e*ÞNŸ.Eú¨[cœXE’fða¶##. ú$_Q[0Œ^ç†Oq›yh÷¾Œñ^2äj“S¯™‹Y4Áý8úi=%š¹œdZÑV|q?gNB:ZQ”J0IIcCƒ8N#/©ÇpÂ@™H=W©êi²´V0úívÊÝ{<=zS:†d´•`VòºR$eœ¹;1F@¼,'Ê·›‡Å`zxèPIĝº‡ÀÁ'¤•n¦	Q,¹’KÔçÕ|`i9¤õN6C¿smØÛÖçD6ʼnž®MЋ¸Â礸hk²ŽWۉŒÃŽCºš)*©2AЁ»¯Äæi#.Ž 0S´_2f	Ôw3	,0JÙ6€È7˜hf†Û#.¥3΄„Îqì%•º+ŠåvpÃY0\Ì<g§Ñ‡Jo'”Dy™â ü0Τ›å|QJLzŒyf˜ägjj£¿ó•†èæBœ†Ÿšqæ1¥ÕO‰zÂŒ{4Ã4_;|‰Ìmû'f”­i ’50qíÖmÄLÐÁQÞ2®®À5^¶-Ž„5°¸m[–Ç_â>y(a• ­Hd&èDm0Ì3Vƒ0x5PÍ#.Œ4Í4¥¢ÒU’1„iÇN(”Èb«oMh Ö5ÍÄæ×6Ç¥Ö  4´‚Ó@>É”fi©«mé®ƨڵÕЏƒ@¨Ei>‚*†¨Š*ˆä38Àz3§7™ŒWÈÇ:mÌà‹cgTâÝ2Æí¶_ä×Zbƒu#/F;e­C¨!Ȭ´Œ0¤ª¡ð”)(E\d7ÙÌÂÍÏ~úv”ÊFp­Ù÷Ã5öÝcÇ-’.R‡SHaüaØ,ëÇ^TbÁòÜ4܄ˆü2aÃþïå0lál‘YÑ2Ÿ|Ъä;æ4T(ÉIc#.pòµÓcP)Kύ¯&‚R#/8H©#"W´Ê F—ˍ=Hîx±í¢0 ÂdCʄðÅvè=HWM¤Úlh$0¥j)ÍĈ‚U9¹FÓc±àìš&®]uªcŠWc;Öú̕ζ֣½ÂÅ´ŒHJˆ8ir(#.ªXb3%–¡ÈØef=íêõ¤âdâ˜ÆÁ²ÄfÈW+	qåàÞ,æF#x¸ê9|¬Á³•Í¡¬ˆË82,u®5†µ:›Ä›‘77»L‘¶ÔM¢”­X›jBÂYw-Ö®g^¤Œ`*uvf¡tíV'T†©aH#	N›‚ia#)j#.A„¶1´Åę#f¤#.0ÔÒíRFT‚ƒ.¯&œ¬×®%‹:5qÀÃ%Ù­ä!2êT›1¶ˆ@Óæ+\ФIÛR¶Â7vÕÛmá¦o´¨#.)Á6RC‚c´È;Ô¬#/ ³#)‡„È´8•%S)TTT*ÃÞ Òiƒc#/Æ6óQe‘¹yâéhá…܈ÆFÆ.JœCi50um#.7I€Â-Æ9¦î³	hrÙH4ÃŽÀv3k?é90²üÃq9:a؆¶@êf,yµ#.ªQ¬Ë«pŒ&3•ÜAœ.âà7f]é©H®qt2ÐJQ•KŽ#F6È5aB…hmFL•D“#.¡‡$Íä匢©©‚¹ç ÖnÜÒ«Ë28t"'®G¥×F‡[ Õ¥iGʪ©µ@¡È̂’¨±Äel«Ë›Š€èøL!X‰îE®öªãœ)ÓiUß¼\œR[ŽZr<bÖæ¶ëCá­½,-™J`pÍ:›_ÛF÷aZ³ JF™ÙÜ$éÝ)µše7L¤Ñ’1ÌÄÅÆ]¯OÒ"Ò ¨`¨×]Z·Ñ”#œ8ÆÂPùdۇØ]ˆŽ"I¢K;jŒ8n<½$f>¿7×ÌEUA‚0Y#/½’åÝm~a·¼þàÉR†¨'~ðê"S#µ1ÄÈ2ªñ󹇀/žöX{qÀ?A¼'zö*ž5;k.¦b¥1–ÝŸ¡È ÂEUTd£$=Þ¢§¹%}Ɓ¯Á9—¦?k§ekeӜfq3 ý â@6ýÞ ä‹êA"pm!–7Ÿ'³Üõì!賦 qnqa²Þ7ê[x³	‘~‰B°v±¡ÿ¢‡³|³‰Áb"²¦«ƒÇ[o	ڐc’FÄUª˜o­q+³FŒ*.¥ª•"ª5žA™æÄ[;^Þ3¯I–{؞äDøÛå4èpçR„œ†B‚ Žk2ÙMT?9ûq$ý‡~þ‚jny×_2xôSÚ?zd†¤÷ª‡ˆ#/ì¾e'i2¥‰Q e„Bˆ¹ N¥@Ô#)>)ϳù-Óà‹âJ~ùìô{,9ßԘÌT¬¨Ù¤«h­(Ô!‹B’ÊL*ÔÊ0Ê(C-,0DìIRbÊL˜ÒÍQJSI¨Y¥JE1PRŒ´‰RQ±¦ÉJh³iR•HÙ2ÄÂÌURL•LH#Mó›ziŸ69­k/ƒ7j2pïж²ÑîX35Šulr˱Qüxó9'g¬EáԏÄ˜û®Åu'±-ð4‡–Ø­R<rO´„×1qèJb˄õ÷š| ÃˆqMvÁ”2ÃmvÜ/ßF2—Ý栀H(Éð,9œJ{Níü!ٖ`Bh\žÞpêX¨™$ênC†1ì>~ïM¤|øxÄÝ<†‘…B¹-`Êü?›zÁ˜¾}M։·c/8„+ýQ |ĈÒҔ	%wÂ	¬k#/L#VL·Ö¡ !b™´Å4†€cí#.Ðø˜Ä˜Ù¤#/&"#'ÒUƒû)py7ô.‘5"#pY1ü#.èsïË£E`ÔL啜ÅFQі‹èìr“#fÆðqÒ,b…Þn‰—d˜e0ƒlY¶´.Ò¤x#.#AU>Ûó8.šõÝB–}=ì×¾WªM#.OnäÀcXù¤K_d0hÆ'­ª±ÔÕe§{ÒR¡cH1¤BŽ$ˆ&FE`ŠE’wqíèkNÇʲ‚Âqaý&X·ŒC”mN(›	†Ñ·LÆÖP©f˜™¢Ê4å¡8»;jÿb.V@{zŒðeԍ·öCh7¥¶DÙ¢bv‡½:D	ÈÛ3)öIQ 4î;&–ScuRâàkà$IUSº"‡××Ýé¯;ÕC— Ü'Ã)¾¯A }ð|͜#ö:¿cÙÀ<=¨oZÌéã¯KV}ìÇT;¸nL$Oß9w4¦Èƒ©øü!回/ܾœ(Ç=0ÜØ9ÔpÙçÊO~U†6Y7õbºÓ…½WPs€ÞVÃ+G¨¼@#`ŒY)¾½8¯Þñ•^mÆêQJŠ-Õ¼1’ë?/Ç]t5*¨¥vÕÐˌ¤é>âàpӘ³1“tGyÆËoäb=„/POžZ8>‡é§†3ñË>‡7%t¯l±Œz™T\çŒ0\èb+”0 g‚óÏj¾»Ã‡-D¿_Ô|´äóv}Pô¦~	ø“¿[‡/Ýb¹H4öäÑã<Ń,=#.ʛç—Ä}wÙ浞~Õ{3ÜÅÌÌz)HI®âÎ„^o*ÈoØUbžX5{. ÷cÂý\JÜÌ#/†±ùw„žd³CO}XïˆÐo…Ø#Cý?9ˆnì;£¦·(Q¼0õ0Òc±0ÌÅ4ž=ÚÐ	€¢(Ȋo?Hõ/mR‹F_C/Ÿì˜6&š\’#)|N)²÷^5òü(ç	">™¯‹1läé#)°ü•ÓE†(cD$‰+ÖLG$`Ÿ³E#.BÛ<ðŎèçõž¢IÛø!h%UO½=µŠ.+[ÂgL¯†ü.U¨¢]’B”Ž7A’®T&„‡X6£Zs¤Q£n¯Ø¼``Û\[üòk·UŽŠ‘*©Ò¦\)T,ÙqQÏÄâ±ÄÎÐ'N‡W‰¶-41Ä!¸	6 NŠØ2	¡"DÎlÓSúKJÁhaþR$•EFš9ȸd\r˜õÓÇz6^ÈßtٖBBžÉ܊4ˆBôBiA‡G>Ö(įHnç40­H˜gf]¥¯Œ@Àg^¶pA»f!Œ!˜¦RĹZq·Ó¡D·nD>ÕNÜH§FaÎ#)Þú]X:ìÐÕe*<L:ÏaúÝ‚h#.„Œ%˜ß0ˆr£NÒ=<Z9«[ˆØ’݉W™Ä`‰ÐÓuÇy±Ól¢b+"E¹FÜ72`ÓÆŒÄȒ#/L›j#)ÙÆÖk5X6’ëi´’lå:N+ûޞO®v²ß[é\.	JÃAM*ÅPö¥ŒÝÑA•IZœjÝõçv*ü *ºo#)±y-ŠFár,nÙvì<»ÁŠ·ÅüõÀtä¦b^ÓÔ7yÒHpÜ §lT!íY€íÕ9͌ա6.(ÑG$˜‚y¡é°“JA¬Ù#.ÃI@ôd`å#)o3ZV-›™•!*åS©qÒW>uEaÓáÔǞML{»#.)ª\ãz=7»2¸íƒÇ'[Ë2™‚eÚö˜c³KŒ@:6†­ÛÃ#)ècMB2¤!Ç=Ñ]v`ÁŒë‡¶"aic&TPõÌè3i´¨CR#);9sštB‘¦Hae°b|ErEbÓ±¬eš´è•;.‘Q0éJ£$À§‡3©2÷k¦†LŸA’zîÎ#.iålӔ÷͔C6Í	é݄X܏¦M× ûA[Š¤Ù˜gD؊6@%j%+xVÏ(réUTîåF5Mn¸HÚ?_<cל•t!£2•u"eñ×1µ“»áV^„é’º6ä)g(XW¹#.FìN¹kµ·\¥»îL`Þj”"ì=枮uV”»‰*ˆ6úaSKxQŒÃtIn“›CŽÎE?b1&iӘSÕˉÅjMë`Wˉxé°²+’ f@!ҙõ™8…ÔðF¸…¢,;,YBú8ò¾Mt×'plØèr6:Í!4,ÓàTÊ73&ÁÐ9§kégȗkYfhPâë:Ç(:@÷GlÕ§“§<醶m.#‚T²ÈOøôX¶þòIK+ßqr- ÃÑ?$Cãf‘„#ÈgM6€v„Õb)(B¢#)ÚÉÃSŸÓÆ-Î(0…€tÌf$áÜtCÉ•)xy·AèðãÅ^åÐgƒ¡.PîQʆ¥‰#.4¨˜,VO3±ßò\ï:Óð1 ¸ë\‡Zg;‰Ø0SC3&†3TQUÚBI²ŠŽpÛ ä¼±j%͔€\…u;0e"ÏÎÿ›ði‹lå3dPå5‰¥â~>F¯ìoò<4#)”„€ÂL&N¬Õ|îf,E“¸C9©ˆjÔÃ2”–´¦>Fd“º¾Ñ­²ü¡·îk‡´õÌØßhæ+,"áÙ¬¸AÁbw°Šj>yn%š[Þ”5ÃÊ"0{OCïIõçTbåh"l\D¥!ÈÌN~K´f5½Å¡¶­éêވ“sEbÑv»û-Z	á9hß,§È°ï|('‹†–«ñFì˜Ü0ã2bB„Â0È"ÉJMNKÕÐ߁©Ã›X^ÌY7¥Cf÷G4Š}“@ËDSX:ªÜlÛÄí0á×\µÌæ_ÊȦtê`,Hõç}0j̚ԖÁ¿"µÖ$»š6q˜ëÛMÜ¹}­#.%А$Õ9Ô.poJœ…G+§ŽÈb‰’´#.qV›=  ¼J1Xo ³JŽ©·Á0_£Õäb·N&ñl}i©F­"PÈy¡‹ªhEçAFH"¸ªPÝԚžlÙúAÅâ‡^ˆ-µé&€Èm#.(.9u =Ó+ÀØáԎ¯ †! ØЗsnúñ%Haš‰173º1bETcQªdÉ/U!¹È2#.˜š)ª^¥—RË]ZO·Cb*H„lˆik¯°3Là%ڌ£B€´¢hëC°ø±4uƒ“Ô‰#Æ‘#.Á9u””•T‰USSx`0G©1 MŽ€c'™²õ¸fIœR‡Ð–v$f錼ƒlR¥õSgc¨Ç‚9gc¡’ë:*˜×ÜÄÐi€64é:)AÒ"1´|½}¯ËovŒ­âHÌ1Š‚o_—Z4ëãðôWÈtwû{ÉT#ÒÀ$Š芦@+âJþŽßíøõkuP÷žƒÆÚ}’y‡µ—ÞøØkŒ_¿âLĹ_‡Lmt¶±þEà1rJo)¬§ÀéglR¤lž#.»ÊêinÅxÅØÙ&ªÄÎSÐÄÛ4R¢=míñ•¥L3¡m[CÕø¼½£§—fÖY?#XAûˆù£ŽÊ¡ó$_N'ٚ͆4%	Ù'1îg:è\?WŽæ˜;£…Ð]Jc²¬©@›ñ,˜TœÐiZ ü¬²ýÑ¥÷èáaݐ‡—=¸#.(´,’Ùh®…‰æŸ¹­+©Ü1àT§-ZʄZ÷Áa5˜Š_nK–!¥’^êUq,¼¡ï0š}a;]ælˆ©»¬3fcI×=âÿËö/Ùí@ЫóÇyøÆ	âwoÙI¥v¸ß‹<à}RB þöB‰9™™î>Úèáp`îP”9G˜hÐĖ8(1²Bd†}§p)±š¤ç&ðÆcF,–E%RÀ!”nbhb)ˆÁ5š¢4æ5.Ö­1F!!K(ÆG…#)J d:	´à†,c"ºÜ#)°äÐì`a“6>#.hÚK(4‡]mʨ¾+Š6Û#.ˉÁw78ÝR×$¥a#Üûnæ;·òà©ÄáeàɃÐ6Ä31˜Áw‘òiŒýÆj"W8,è•Tp	‰R‘Ks1ŒµU º!Êeyð¬Ø΅N.~I8šLó÷‰Ú`Ÿ0]Bxž:FBuÞÙæ‹ÌõhëŽs ‹)ºDßl™ˆóë˜{‰i*¸ic8x„ yg8‰·¢t×N²'<í˜!ßÝeëû´i³;å4Èdoo=NúñŽ'Ќv^]nؼáæz=3Õ^lÁA5áÓc؆ñȔ”ÐHO0›s¥1õj‡Ú²`Ӗ¥LM¶Ò¿„lúãx,œŠ¨eõœž;O-ÏCpÑ­î„|<g¯åÑø4ú˜%A fÕe#/YU)o·Md­I´EÂ!)àB‘"jßqÀ#.@’ma!úá¡\š#)ÒB›3¼’Eë|¤±T]êœBþÚ0ÌÍàÀ0_h¯£q­ùô¹ÜëáùMòm‹K2hˆÕ&CŽ¸û|Þ'ÅN*#.ôÆîóñþH @hA)é÷ïö™ÛëðÀáñð5_Þ/Zû¼éµóîî?fœ‹Ü”&HJæHL­x!Pšm¤B+.e#/(ÅG|Ó8賺UvÞ6®h«¤¸!AšDc	cL&0… ÁXE€w7Á&š9#.Êg#.ô!¨#)`ÀìÒ(C jސþæ`c¯I´éÉ´¶ÆҊƒ£ùä|?i÷Ðé¨:”TÄ8@aªA+XWF‡Ê–$@–6m€ƒ§°{y´1îÚfà`‘ƒD11#)êã˾ð˾­Æ}Ž}E<ø!Ðbƒ!V"á€&†#.3ºÂ°Ð^¹<xt‡d37ÇmBÀ(x	îr2„úÿ.þÝZ¹a#.Ñhäôºòt“ד™¼…g‘„Í7ߒ°…l·Í­c]sŒ’¿"Öv‹7Uƒ0ZT`:Fõ׿–³OÃ*‚€²¹ÄZSµŒ†äŠÄÛOGjŸ2EC¡ÎD´Ñ"m8iÇ	#/PμÑùŽ,û„À¸é,üµð‡ë>=SîìBd~¤x·\"H™HmL¦™hÌT%ª¶c5¨hÙµ#_u±­n–éI«\gMÓ6»Sk¦Ä-\ƒ.ó2€Y¾ÆfÁ™€Äæҗó˜rÖ¹˜ék˜‘çí2HÏaM»@#)¯¨alð×6ó½Òd`1—#.ÞAãÀ˜èxÌrf;ÃÇÆ`̨fAA0µúîlö﯏Ÿ}Ý/Ôà&i}#.pÖõYtŠ®HcÉêr½±€²m?1ù–(„Ž–äq´·¨bwAMŠÂÍXé΢(4W‹!߈—--™¦‘x•£¯ê–ÐJ‚h˜KwήxCà‡.[ÎI6`ïRÒS,Ý7ˆèXÖq–¹‡¹‘¦›t‰ØN&6äï¢\7ΛéRE„Yƒnå’ɹ¹d£ý~3åÎèxzà0ûU@ƒ#)GÍ\pAlj ;LÖÑÆ¥Fà7œeÑfšCRè·ÛUüÝÉIPË%!OÄ¥AÊgÐÍÛPlö{¼ÐDD½Ç¹kԖz<@ÄHkò(&È#)?Â#/°Ãi«=^u”»„rÓ³&*k¬Ö§3X ©Š¬bÌ{HP  „qTRzýçï›C‹•±ãÐ;0ÌûÌu0BN8é˜Hr°ö	¼„j#/}þ^äzzà“°ŽLLBxzxl=õÅ8V)RŸÕ\‡auùPà֐Ä@f–R…ï=ã˜1hw÷ú7ß×¢,­L!œó³)ˆ²}¿©ŸÈqê.„ïäÇú#ø’ÕÑìâÑ·#)íÛºj}dè=àh"Chk?L(¸‹5ô?··ŠûÌýfd{AÛ>ø:‰<‡¤b'h¿ÝèÐlè$ô÷ô@X4u²ÆBBF$V5ÈŠŠ#/·T‚‡;¦\U!wRÐüF]Ðkmie1(¡jX¤#.“@<ѓhڊdcs]š¥ILbSøÀc!õƒ@€f'þ"¾ïG‰z9s$†@!»LÄ9‚mä ÜÁw!2Ï f§Œ	áäG”YZEŠ£eOyìA#.ý$´J•&D±RjÉf˜BÉzþ¢–ŽÐŸ•¢‚ b•Ɠm4­_^·ñ·y@£´Ÿ–T9~ŠÅÏGÌnC°FÀü	t!Ù#)öcTÄÅòŠx›	†a–È›5k6Æ*¥¦HHª¢ÛZҁ	,ƒéÀP!/j˜ÿ–C^"	Óýyýe÷?Ãùɵ´\™›˜Ŷ¶-óFìõ\zë+€7éêٍHCð·’©ì(	#Ôöäx^#)§6ü¤úÁwŶf´H9Iɧï}#/õу>˜[bF0“øíAĝMϸ¹Brd…\òzµÓŒçX|8h0qà(}f…8ždù®€Ä/Í1LNI¢®¼LXRM\.MU*Ѳ›5¦©Åœp ¤Øè미·E7þÂU扂°:‡C#.Ë1ÃÛRG׃8ö”ᬼ×Þã$lŠ™-”[¢›ˆ'ÊoÚcÁØÕBÙ՛–”‰ÏÕB>¿Ý/HE‚ùì\¹m/5àÃK#/37âŸ,ú>—H°ÅËfÍî-	À7‰Óùÿr¨`{ÀæG·+#/j†d¡©M5%ƬdÚ£Yö;[ó-^&Š°V€¥O¦Þ ¬ñnç8bÊpdàÎü1Âåßíé¢4ª¾ ‡®ü±ºõõF§³g¤¢hJ(h­£T›R•Œ&Úl£Y¤’9Ë]}ý‡.3߅U9oÕðý[¿l¡©Qûþþ~íù|H¥ß€»@¦ô#/:ïPæɪ=õ2ÉôW×ÈnË`¢¬G#.*ó!LÚÓ`4 ÛCGgPFFkþ=#/í"´<¢ #)ܵ¨Æ‘2Q9ëCE,Ñ*:ûNXn@tïT/HMH욝‚Gxç¾I¨s#œ@ŠÄ¹3·ücAZM±¦Ûc‚„Û1#)®|ø·#.õ¹¬rԐÂ#/((+‰SoœÝ5êDÑ¥5©;¡Á–‚˜#.&։2Js3 ÚM:˜hqšfeWs­¦•*”7®ís™)`! ¥¤u˜¦“ fÚ#.ê¯=uzyåØݗ4¤R‰X!$˜Y„Â\sË Êd9ʐÎµm!ÄïC¸Ó{†Á>¡^Ÿá:øÿF껆˜<=|“O¢8c–	:à¸Âµ'†B"ŽÁSníòvðŽçvìßä8ùƒ9Âh ;XGŸDZ‹ª‘±š-ŸJ¬3ëŸ8‰qPàU¯]zqÂf¨þŠ$‡-U#(Þ4ž~%'ž,ÔL'bI~0m/èܱǢF+²¤¥¼¶„ú¦“‰É…e²Š£±\^­G–­Ü™twçKæДq¤F±/SJ¶ýŸ äL–¦Ý‡R¡¾;N ‹¹ä_ž<#.†Cµß·Xx=™;z(©•ª?c"ä¡XÝ0íf\kÂ#.——Á‘pÏ#)äJôÌ«Gà@œÅ^'"%ƒŸ2ÙPÂàÛÏpðM<.žÛñ#.ÖEy°´:B)clWr<¤Õ±/$Âa#€X&ÀwŸÓà{:|‡nÿçcWŒlãžꪄ¤2îz?2P?ó}f1•~ûrȤT™ÚàØ÷Ÿ#$	Žÿ£èy,»móÅr­¿4⏯ùøñ=6Ǟé%Ì‘¡ý°ŒªØ‹Æ(Ûv!oÖ#)½=ó«YÑ¥Ž$â•IÓmÀLàÅExÁ¨7Cc sÖ­º;õª‰di›Eƒàt²jw»lƒ€†¿Fo3ò	ª9%’.ºwC¼öBÌ»#.Ný€6÷ï‚k!!™×†Oé÷ó†xÜQO3¬Çd!ÔäÎw¢d͇.ýA6#.P­;‘p-!¯ÛÛ#.VúX7çÒî¥ý!ÔJ!ûxãóà8Šœ§+ÝZaH%à€÷m7ìýáJwŸ#O$i$#.µ‚üf=múI |ýZz¸pùcm·Ì’v3$“V:þ¾›½5¢ßø¤^É@å	£ÿ‡û¿èÿWãÿ§þŸüßøÿ«á÷í÷ÿïÏÉïÿ³þ¼¿ýÿßÿ‡úü?ãÿ»ÿ/oñÿ‡ùÿãnÏû¿íýñÕÿüGü?öõ~ÿŸöÉÿþÿ¿þüãÿoü¿òÿÏÿó§Ç¯çÿÏããÿ/'ýŸòìü«ýÿ›ýß/OåýÜïýe¡WúÀýu0 NGöt”äœÌØ?Ò<OéÀēL¡¹ýÏüb§p_îª"‘:ÍŸ”>ö—»ïy㻦L$Êeõ­}7¶”#)<ÀéhŽY鐐G“]¡ݦÑ#.qârB¦¢œ#)1|ïá‹üßô`Ò>ÿ«ãÏt¤N=ÁÏ;‘ó±ëÅÿ”3#/ّnÉéߕàˆÓר7/kLfv̧€¾#)cšž_Û‰å5åú—ûÇûóô¼<<ýÜM*,ɀªHÿ	xLÑ7ŠAЃ@ÊÖÞì?Ú£ƒp—·_GÿË]熸Ì+LpŠÔgR¬Ï÷¸»#)ÞëL*‚ú²5ìÚÓlkzšY(ÁÀïâ•ÄÆlÿgµb¥‹±tÓ˜–1ì²" «¿¦wpZeX´“‹h=Í0Ñ&‰ž5I„Ë7j²î=Ê:2Ãt#EÛ/5£zÚa¦„œ‡<CêF2UÜFÄ¡®kÙℰ›wÌÈ?w&±db:4rø>†G7®'?՗fÀ¾G…݆Ê‘ÄÌY³DÁ¦Œ.^x1«k##½pW„ÛŒ!ÑúQŠÀçfò]„d–½Žæ¬ß݀«¦8±˜Ï˜\û·ûÂLGÃð€¡ŒØÕKÛן{RœW_Ç=h¦'b¢:äŽvvÙ4ÂQªâÙ0Q€dƒƒæ_i¯wÃp‡ÛYQFîɵÐÓÓ¯.ÏÖc©Œøi0<X™¢1„FBß:©”“Îûs#1ïÎËXÍ¡&Hc|·E!l$z–Qõ«B$/˜c^:t€mRŠPL(Qþq±ç³³Šð´ªØF%½D«ï˜–&f‘ÿî¸X('ÎñÐ+éÉë:€X ÜCæ#/=œ9À˜” ÛLY¡**#.¨Öҙ+Y-IjdË1QÏ#1ANùSD‚lýí!ã	ý„'üD®Ç†4f£¼ô¸PAMècÂØ,1Y>z¨}áåïÏ4ߥŠ¤üÊËdüæ¾÷å5Ðÿ¿Lcåu¦|àmð5ŸOÏë;á³OVöʘgŒé·]ƈëf­~º™WÖ!z•› Ö­ÊÞË5»§­[3^Á£N#.„*cÜAy9·¶Hs¿ÇbËï*ºQHYX“	.0é¾]qdc"Šd”Ÿ:,¶›¤Ú‹¾CÞÆÙÊß"Ømþ¿oGӇ;¦¹VÓ£9õ,)’$Îò=”šÜ¼ó¨ÔmË^•¼Z–´¥¶¼o“Uxµ&ÅQª!„¤¡@ÞéÇZ#.Û!ۚN´e×÷ñàmÒb‹ÄãþìCþsaî&©HŽÆ.á.òžÑUAN6’ÑŸ§¨¸ÿãŸ1GTxJ®|™*s#/A‡ÇÿûoÉä‡w–ž•×pû—™mÿ#äzÈ÷ñ<QìG‹òïہ¯üàÀgp‡z~C ÿÑ€Á"oZ=‡¦æ	b…Š‘#/I•)ŠHs» ©`˜7Ð{②f#)x«åôà{??ã鍬	µs ç\8§;úÕ)‘6‹¢ƛÛ&Ú“ɐ÷Ʉ7Üb{??ë°B½?¦^•	`ÿÃS3¿ýEÇbŸËþáÝ«èÿçP½‚„ëåÿ|gøYñ?êߏB¿Y”nýK‹Oü<õxÿ×÷FÏ[ý^¯Á6sìk;ts#©m·¢ òcÛZ=>©(|è'¥A³Ûêæ8¥óTlâs¹ÿêóÑÎmÿ¸¢o—ívö&’j BøÓ6	Šd™J?Cƒ"ϧ†“oÿ5°ü¥kݛqEc„õxæþ[¾rÂsóÄ%ÿ©Ä,¨IÔ"ʼnɲe¾Jíã%S8 új¹åË5ï€pCÙõÑ9vÙ8˜ÊÉN`þçIbðŽë-bY㗄hœTKY¸Ä!kÁåôéË0"°ÉB7§ÆGBYțJU3Dœ!‡×{`‘&øVW´Ð7-ìÓîÿÑYóŒâ;“×)UŒEø†¿íëzÏÞ"„ýß_à`aÿâîH§#/¹)Ž€
+#<==
diff --git a/NFD/websocketpp b/NFD/websocketpp
new file mode 160000
index 0000000..4309749
--- /dev/null
+++ b/NFD/websocketpp
@@ -0,0 +1 @@
+Subproject commit 4309749dd98937b8a7be5dc0bfe679ba201c5512
diff --git a/NFD/wscript b/NFD/wscript
new file mode 100644
index 0000000..65c249a
--- /dev/null
+++ b/NFD/wscript
@@ -0,0 +1,321 @@
+# -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
+
+"""
+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
+
+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/>.
+"""
+
+VERSION = "0.2.0"
+APPNAME = "nfd"
+BUGREPORT = "http://redmine.named-data.net/projects/nfd"
+URL = "http://named-data.net/doc/NFD/"
+GIT_TAG_PREFIX = "NFD-"
+
+from waflib import Logs, Utils, Context
+import os
+
+def options(opt):
+    opt.load(['compiler_cxx', 'gnu_dirs'])
+    opt.load(['boost', 'unix-socket', 'dependency-checker', 'websocket',
+              'default-compiler-flags', 'coverage', 'pch', 'boost-kqueue',
+              'doxygen', 'sphinx_build', 'type_traits', 'compiler-features'],
+             tooldir=['.waf-tools'])
+
+    nfdopt = opt.add_option_group('NFD Options')
+    opt.addUnixOptions(nfdopt)
+    opt.addWebsocketOptions(nfdopt)
+    opt.addDependencyOptions(nfdopt, 'libpcap')
+    nfdopt.add_option('--without-libpcap', action='store_true', default=False,
+                      dest='without_libpcap',
+                      help='''Disable libpcap (Ethernet face support will be disabled)''')
+
+    opt.addDependencyOptions(nfdopt, 'librt',     '(optional)')
+    opt.addDependencyOptions(nfdopt, 'libresolv', '(optional)')
+
+    nfdopt.add_option('--with-tests', action='store_true', default=False,
+                      dest='with_tests', help='''Build unit tests''')
+    nfdopt.add_option('--with-other-tests', action='store_true', default=False,
+                      dest='with_other_tests', help='''Build other tests''')
+
+def configure(conf):
+    conf.load(['compiler_cxx', 'gnu_dirs',
+               'default-compiler-flags', 'pch', 'boost-kqueue',
+               'boost', 'dependency-checker', 'websocket',
+               'doxygen', 'sphinx_build', 'type_traits', 'compiler-features'])
+
+    conf.find_program('bash', var='BASH')
+
+    if not os.environ.has_key('PKG_CONFIG_PATH'):
+        os.environ['PKG_CONFIG_PATH'] = ':'.join([
+            '/usr/local/lib/pkgconfig',
+            '/opt/local/lib/pkgconfig'])
+    conf.check_cfg(package='libndn-cxx', args=['--cflags', '--libs'],
+                   uselib_store='NDN_CXX', mandatory=True)
+
+    conf.checkDependency(name='librt', lib='rt', mandatory=False)
+    conf.checkDependency(name='libresolv', lib='resolv', mandatory=False)
+
+    boost_libs = 'system chrono program_options random'
+    if conf.options.with_tests:
+        conf.env['WITH_TESTS'] = 1
+        conf.define('WITH_TESTS', 1);
+        boost_libs += ' unit_test_framework'
+
+    if conf.options.with_other_tests:
+        conf.env['WITH_OTHER_TESTS'] = 1
+
+    conf.check_boost(lib=boost_libs)
+    if conf.env.BOOST_VERSION_NUMBER < 104800:
+        Logs.error("Minimum required boost version is 1.48.0")
+        Logs.error("Please upgrade your distribution or install custom boost libraries" +
+                   " (http://redmine.named-data.net/projects/nfd/wiki/Boost_FAQ)")
+        return
+
+    conf.load('unix-socket')
+    conf.checkWebsocket(mandatory=True)
+
+    if not conf.options.without_libpcap:
+        conf.check_asio_pcap_support()
+        if conf.env['HAVE_ASIO_PCAP_SUPPORT']:
+            conf.checkDependency(name='libpcap', lib='pcap', mandatory=True,
+                                 errmsg='not found, but required for Ethernet face support. '
+                                        'Specify --without-libpcap to disable Ethernet face support.')
+        else:
+            Logs.warn('Warning: Ethernet face support is not supported on this platform with Boost libraries version 1.56. '
+                      'See http://redmine.named-data.net/issues/1877 for more details')
+    if conf.env['HAVE_LIBPCAP']:
+        conf.check_cxx(function_name='pcap_set_immediate_mode', header_name='pcap/pcap.h',
+                       cxxflags='-Wno-error', use='LIBPCAP', mandatory=False)
+
+    conf.load('coverage')
+
+    conf.define('DEFAULT_CONFIG_FILE', '%s/ndn/nfd.conf' % conf.env['SYSCONFDIR'])
+
+    # disable assertions in release builds
+    if not conf.options.debug:
+        conf.define('NDEBUG', 1)
+
+    conf.write_config_header('config.hpp')
+
+def build(bld):
+    version(bld)
+
+    bld(features="subst",
+        name='version',
+        source='version.hpp.in',
+        target='version.hpp',
+        install_path=None,
+        VERSION_STRING=VERSION_BASE,
+        VERSION_BUILD=VERSION,
+        VERSION=int(VERSION_SPLIT[0]) * 1000000 +
+                int(VERSION_SPLIT[1]) * 1000 +
+                int(VERSION_SPLIT[2]),
+        VERSION_MAJOR=VERSION_SPLIT[0],
+        VERSION_MINOR=VERSION_SPLIT[1],
+        VERSION_PATCH=VERSION_SPLIT[2],
+        )
+
+    core = bld(
+        target='core-objects',
+        name='core-objects',
+        features='cxx pch',
+        source=bld.path.ant_glob(['core/**/*.cpp']),
+        use='version BOOST NDN_CXX LIBRT',
+        includes='. core',
+        export_includes='. core',
+        headers='common.hpp',
+        )
+
+    nfd_objects = bld(
+        target='daemon-objects',
+        name='daemon-objects',
+        features='cxx',
+        source=bld.path.ant_glob(['daemon/**/*.cpp'],
+                                 excl=['daemon/face/ethernet-*.cpp',
+                                       'daemon/face/unix-*.cpp',
+                                       'daemon/face/websocket-*.cpp',
+                                       'daemon/main.cpp']),
+        use='core-objects WEBSOCKET',
+        includes='daemon',
+        export_includes='daemon',
+        )
+
+    if bld.env['HAVE_LIBPCAP']:
+        nfd_objects.source += bld.path.ant_glob('daemon/face/ethernet-*.cpp')
+        nfd_objects.use += ' LIBPCAP'
+
+    if bld.env['HAVE_UNIX_SOCKETS']:
+        nfd_objects.source += bld.path.ant_glob('daemon/face/unix-*.cpp')
+
+    if bld.env['HAVE_WEBSOCKET']:
+        nfd_objects.source += bld.path.ant_glob('daemon/face/websocket-*.cpp')
+
+    bld(target='bin/nfd',
+        features='cxx cxxprogram',
+        source='daemon/main.cpp',
+        use='daemon-objects',
+        )
+
+    rib_objects = bld(
+        target='rib-objects',
+        name='rib-objects',
+        features='cxx',
+        source=bld.path.ant_glob(['rib/**/*.cpp'],
+                                 excl=['rib/main.cpp']),
+        use='core-objects',
+        )
+
+    bld(target='bin/nrd',
+        features='cxx cxxprogram',
+        source='rib/main.cpp',
+        use='rib-objects',
+        )
+
+    for app in bld.path.ant_glob('tools/*.cpp'):
+        bld(features=['cxx', 'cxxprogram'],
+            target='bin/%s' % (str(app.change_ext(''))),
+            source=['tools/%s' % (str(app))],
+            use='core-objects LIBRESOLV',
+            )
+
+    bld.recurse("tests")
+
+    bld(features="subst",
+        source='nfd.conf.sample.in',
+        target='nfd.conf.sample',
+        install_path="${SYSCONFDIR}/ndn",
+        IF_HAVE_LIBPCAP="" if bld.env['HAVE_LIBPCAP'] else "; ",
+        IF_HAVE_WEBSOCKET="" if bld.env['HAVE_WEBSOCKET'] else "; ")
+
+    if bld.env['SPHINX_BUILD']:
+        bld(features="sphinx",
+            builder="man",
+            outdir="docs/manpages",
+            config="docs/conf.py",
+            source=bld.path.ant_glob('docs/manpages/**/*.rst'),
+            install_path="${MANDIR}/",
+            VERSION=VERSION)
+
+    for script in bld.path.ant_glob(['tools/*.sh', 'tools/*.py']):
+        bld(features='subst',
+            source='tools/%s' % (str(script)),
+            target='bin/%s' % (str(script.change_ext(''))),
+            install_path="${BINDIR}",
+            chmod=Utils.O755,
+            VERSION=VERSION)
+
+    bld.install_files("${DATAROOTDIR}/ndn",
+                      bld.path.ant_glob('tools/nfd-status-http-server-files/*'))
+
+def docs(bld):
+    from waflib import Options
+    Options.commands = ['doxygen', 'sphinx'] + Options.commands
+
+def doxygen(bld):
+    version(bld)
+
+    if not bld.env.DOXYGEN:
+        Logs.error("ERROR: cannot build documentation (`doxygen' is not found in $PATH)")
+    else:
+        bld(features="subst",
+            name="doxygen-conf",
+            source=["docs/doxygen.conf.in",
+                    "docs/named_data_theme/named_data_footer-with-analytics.html.in"],
+            target=["docs/doxygen.conf",
+                    "docs/named_data_theme/named_data_footer-with-analytics.html"],
+            VERSION=VERSION_BASE,
+            HTML_FOOTER="../build/docs/named_data_theme/named_data_footer-with-analytics.html" \
+                          if os.getenv('GOOGLE_ANALYTICS', None) \
+                          else "../docs/named_data_theme/named_data_footer.html",
+            GOOGLE_ANALYTICS=os.getenv('GOOGLE_ANALYTICS', ""),
+            )
+
+        bld(features="doxygen",
+            doxyfile='docs/doxygen.conf',
+            use="doxygen-conf")
+
+def sphinx(bld):
+    version(bld)
+
+    if not bld.env.SPHINX_BUILD:
+        bld.fatal("ERROR: cannot build documentation (`sphinx-build' is not found in $PATH)")
+    else:
+        bld(features="sphinx",
+            outdir="docs",
+            source=bld.path.ant_glob('docs/**/*.rst'),
+            config="docs/conf.py",
+            VERSION=VERSION_BASE)
+
+def version(ctx):
+    if getattr(Context.g_module, 'VERSION_BASE', None):
+        return
+
+    Context.g_module.VERSION_BASE = Context.g_module.VERSION
+    Context.g_module.VERSION_SPLIT = [v for v in VERSION_BASE.split('.')]
+
+    didGetVersion = False
+    try:
+        cmd = ['git', 'describe', '--always', '--match', '%s*' % GIT_TAG_PREFIX]
+        p = Utils.subprocess.Popen(cmd, stdout=Utils.subprocess.PIPE,
+                                   stderr=None, stdin=None)
+        out = str(p.communicate()[0].strip())
+        didGetVersion = (p.returncode == 0 and out != "")
+        if didGetVersion:
+            if out.startswith(GIT_TAG_PREFIX):
+                Context.g_module.VERSION = out[len(GIT_TAG_PREFIX):]
+            else:
+                Context.g_module.VERSION = "%s-commit-%s" % (Context.g_module.VERSION_BASE, out)
+    except OSError:
+        pass
+
+    versionFile = ctx.path.find_node('VERSION')
+
+    if not didGetVersion and versionFile is not None:
+        try:
+            Context.g_module.VERSION = versionFile.read()
+            return
+        except (OSError, IOError):
+            pass
+
+    # version was obtained from git, update VERSION file if necessary
+    if versionFile is not None:
+        try:
+            version = versionFile.read()
+            if version == Context.g_module.VERSION:
+                return # no need to update
+        except (OSError, IOError):
+            Logs.warn("VERSION file exists, but not readable")
+    else:
+        versionFile = ctx.path.make_node('VERSION')
+
+    if versionFile is None:
+        return
+
+    try:
+        versionFile.write(Context.g_module.VERSION)
+    except (OSError, IOError):
+        Logs.warn("VERSION file is not writeable")
+
+def dist(ctx):
+    version(ctx)
+
+def distcheck(ctx):
+    version(ctx)