BIND 10 trac1483, updated. a677ae91c9d7a5bd2ef8574f84e9f33f90c45e44 [master] ChangeLog for Trac 1383
BIND 10 source code commits
bind10-changes at lists.isc.org
Fri Dec 9 13:00:47 UTC 2011
The branch, trac1483 has been updated
via a677ae91c9d7a5bd2ef8574f84e9f33f90c45e44 (commit)
via 9b2b249d23576c999a65d8c338e008cabe45f0c9 (commit)
via 45274924f3f25b70547809eeda5dbcbe230029b5 (commit)
via a6a35e9970e4937924c1e33c01f6bd7eaf1ed994 (commit)
via c8dc421c5cad0f3296174b44f8deccfb69dec43f (commit)
via 91fb141bfb3aadfdf96f13e157a26636f6e9f9e3 (commit)
via 7f5229256540479ef63d707a14d194c80099fab3 (commit)
via c0be7a6c0e12c78a7e02a2a3b3b259a3382b52bf (commit)
via 86a4ce45115dab4d3978c36dd2dbe07edcac02ac (commit)
via 14a64484a3159a142f1b83a9830ac389a52f6a35 (commit)
via 146203239c50d2a00069986944d4ec168f17b31f (commit)
via 982fc000a3064515ac30f5457b71802577fec90d (commit)
via eb08c5acb5deafa28ae37032910cce9a385d2030 (commit)
via b2186bf05d8d9858b0b58cf9dca5b215afe447f5 (commit)
via 660cf410c0fb41587b977df992879f5dff934c19 (commit)
via b09fdcc6b45d4580b138cc9f59bfc051bd6ad360 (commit)
via 4d97ef5cdb4833a7a36b6679c16338505b07d4e3 (commit)
via 424f32864efcd2c647c6e5303125b6a8afb421ea (commit)
via 7715c727d25d6430cbdbd82e40bdb7b3fa2ea843 (commit)
via a26b979adb54baabdf939ed1a7852b2ee9b8b93c (commit)
via eb703a7e5b3749ca95a43c7582c9cccde564f123 (commit)
via cbe600decbef4db82cb3b070e03b5702540af4aa (commit)
via c44075a40764cbb5dc37e9dd3666ce46bb8c7955 (commit)
via 7019db2a44f39897486eea618f4447c37dbabcf8 (commit)
via 9df50bec4e691dc8cb724547659fb71caad656ab (commit)
via 15dffb02f179974c6726f16aff586c49eec8c7ca (commit)
via 0737908f9e7cb615f80354131dca4df1a8c0bff6 (commit)
via d6d90c1976110dcfb94cba2c56086960054cdeae (commit)
via 936511f6e114f26bf86497466a7f61ef467bf5ad (commit)
via 5a2d958780a4a671cd8df9080d99ff95dd16772d (commit)
via 075e3787986676c7491f157931b6f7da1773db0a (commit)
via 7d2f07481169780071bf564223a20a219b550385 (commit)
via d5e189cf1573446503a4fafa3e909db60eb04623 (commit)
via 0b6937d0e075e1192c41891ae138532f2c733b47 (commit)
via 5371b694b6cc564c3f1899a935769dd024f38e56 (commit)
via 837002896937febe208c141912fc4f8c3beaa2ab (commit)
from 37a11387baa321daec8311fc66d5d83e567886bd (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit a677ae91c9d7a5bd2ef8574f84e9f33f90c45e44
Author: Stephen Morris <stephen at isc.org>
Date: Fri Dec 9 10:48:57 2011 +0000
[master] ChangeLog for Trac 1383
-----------------------------------------------------------------------
Summary of changes:
ChangeLog | 17 ++
configure.ac | 105 +++++--
src/bin/auth/benchmarks/Makefile.am | 2 +-
src/bin/dhcp6/.gitignore | 1 +
src/bin/dhcp6/dhcp6_srv.cc | 30 ++-
src/bin/dhcp6/dhcp6_srv.h | 11 +-
src/bin/dhcp6/iface_mgr.cc | 436 ++++++++++++++++++++---------
src/bin/dhcp6/iface_mgr.h | 269 +++++++++++++++---
src/bin/dhcp6/tests/Makefile.am | 4 +-
src/bin/dhcp6/tests/dhcp6_srv_unittest.cc | 23 +-
src/bin/dhcp6/tests/iface_mgr_unittest.cc | 264 ++++++++++++++----
src/bin/resolver/tests/Makefile.am | 2 +-
src/lib/asiodns/io_fetch.cc | 4 -
src/lib/cryptolink/Makefile.am | 3 +-
src/lib/cryptolink/tests/Makefile.am | 4 +-
src/lib/dhcp/pkt4.cc | 47 ++--
src/lib/dhcp/pkt4.h | 24 ++-
src/lib/dhcp/tests/option_unittest.cc | 2 +
src/lib/dhcp/tests/pkt4_unittest.cc | 13 +
src/lib/dns/tests/Makefile.am | 6 +-
src/lib/log/Makefile.am | 3 +-
src/lib/log/tests/Makefile.am | 10 +-
src/lib/nsas/nameserver_entry.cc | 24 ++-
src/lib/nsas/nsas_messages.mes | 29 ++-
src/lib/python/isc/datasrc/Makefile.am | 1 +
src/lib/resolve/recursive_query.cc | 152 ++++++++---
src/lib/resolve/resolve_messages.mes | 97 ++++++-
src/lib/resolve/response_classifier.h | 4 +-
28 files changed, 1186 insertions(+), 401 deletions(-)
-----------------------------------------------------------------------
diff --git a/ChangeLog b/ChangeLog
index d5849f0..c59c9ec 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,20 @@
+342. [bug] stephen
+ In the resolver, a FORMERR received from an upstream nameserver
+ now rsults in a SERVFAIL being returned as a response to the original
+ query. Additional debug messages added to distinguish between
+ different errors in packets received from upstream nameservers.
+ (Trac #1383, git 9b2b249d23576c999a65d8c338e008cabe45f0c9)
+
+341. [func] tomek
+ libdhcp++: Support for handling both IPv4 and IPv6 added.
+ Also added support for binding IPv4 sockets.
+ (Trac #1238, git 86a4ce45115dab4d3978c36dd2dbe07edcac02ac)
+
+340. [build] jelte
+ Fixed several linker issues related to recent gcc versions, botan
+ and gtest.
+ (Trac #1442, git 91fb141bfb3aadfdf96f13e157a26636f6e9f9e3)
+
339. [bug] jinmei
libxfr, used by b10-auth to share TCP sockets with b10-xfrout,
incorrectly propagated ASIO specific exceptions to the application
diff --git a/configure.ac b/configure.ac
index 973842f..e370e21 100644
--- a/configure.ac
+++ b/configure.ac
@@ -480,23 +480,33 @@ else
fi
fi
-BOTAN_LDFLAGS=`${BOTAN_CONFIG} --libs`
+BOTAN_LIBS=`${BOTAN_CONFIG} --libs`
BOTAN_INCLUDES=`${BOTAN_CONFIG} --cflags`
# We expect botan-config --libs to contain -L<path_to_libbotan>, but
# this is not always the case. As a heuristics workaround we add
-# -L`botan-config --prefix/lib` in this case. Same for BOTAN_INCLUDES
-# (but using include instead of lib) below.
+# -L`botan-config --prefix/lib` in this case (if not present already).
+# Same for BOTAN_INCLUDES (but using include instead of lib) below.
if [ $BOTAN_CONFIG --prefix >/dev/null 2>&1 ] ; then
- echo ${BOTAN_LDFLAGS} | grep -- -L > /dev/null || \
- BOTAN_LDFLAGS="-L`${BOTAN_CONFIG} --prefix`/lib ${BOTAN_LDFLAGS}"
+ echo ${BOTAN_LIBS} | grep -- -L > /dev/null || \
+ BOTAN_LIBS="-L`${BOTAN_CONFIG} --prefix`/lib ${BOTAN_LIBS}"
echo ${BOTAN_INCLUDES} | grep -- -I > /dev/null || \
BOTAN_INCLUDES="-I`${BOTAN_CONFIG} --prefix`/include ${BOTAN_INCLUDES}"
fi
+
+# botan-config script (and the way we call pkg-config) returns -L and -l
+# as one string, but we need them in separate values
+BOTAN_LDFLAGS=
+BOTAN_NEWLIBS=
+for flag in ${BOTAN_LIBS}; do
+ BOTAN_LDFLAGS="${BOTAN_LDFLAGS} `echo $flag | sed -ne '/^\(\-L\)/p'`"
+ BOTAN_LIBS="${BOTAN_LIBS} `echo $flag | sed -ne '/^\(\-l\)/p'`"
+done
+
# See python_rpath for some info on why we do this
if test $rpath_available = yes; then
BOTAN_RPATH=
- for flag in ${BOTAN_LDFLAGS}; do
+ for flag in ${BOTAN_LIBS}; do
BOTAN_RPATH="${BOTAN_RPATH} `echo $flag | sed -ne 's/^\(\-L\)/-R/p'`"
done
AC_SUBST(BOTAN_RPATH)
@@ -512,13 +522,13 @@ AC_SUBST(BOTAN_RPATH)
fi
AC_SUBST(BOTAN_LDFLAGS)
+AC_SUBST(BOTAN_LIBS)
AC_SUBST(BOTAN_INCLUDES)
CPPFLAGS_SAVED=$CPPFLAGS
CPPFLAGS="$BOTAN_INCLUDES $CPPFLAGS"
-LDFLAGS_SAVED="$LDFLAGS"
-LDFLAGS="$BOTAN_LDFLAGS $LDFLAGS"
-
+LIBS_SAVED="$LIBS"
+LIBS="$LIBS $BOTAN_LIBS"
AC_CHECK_HEADERS([botan/botan.h],,AC_MSG_ERROR([Missing required header files.]))
AC_LINK_IFELSE(
[AC_LANG_PROGRAM([#include <botan/botan.h>
@@ -533,7 +543,7 @@ AC_LINK_IFELSE(
AC_MSG_ERROR([Needs Botan library 1.8 or higher])]
)
CPPFLAGS=$CPPFLAGS_SAVED
-LDFLAGS=$LDFLAGS_SAVED
+LIBS=$LIBS_SAVED
# Check for log4cplus
log4cplus_path="yes"
@@ -545,7 +555,7 @@ if test "${log4cplus_path}" = "no" ; then
AC_MSG_ERROR([Need log4cplus])
elif test "${log4cplus_path}" != "yes" ; then
LOG4CPLUS_INCLUDES="-I${log4cplus_path}/include"
- LOG4CPLUS_LDFLAGS="-L${log4cplus_path}/lib"
+ LOG4CPLUS_LIBS="-L${log4cplus_path}/lib"
else
# If not specified, try some common paths.
log4cplusdirs="/usr/local /usr/pkg /opt /opt/local"
@@ -553,21 +563,21 @@ else
do
if test -f $d/include/log4cplus/logger.h; then
LOG4CPLUS_INCLUDES="-I$d/include"
- LOG4CPLUS_LDFLAGS="-L$d/lib"
+ LOG4CPLUS_LIBS="-L$d/lib"
break
fi
done
fi
-LOG4CPLUS_LDFLAGS="$LOG4CPLUS_LDFLAGS -llog4cplus $MULTITHREADING_FLAG"
+LOG4CPLUS_LIBS="$LOG4CPLUS_LIBS -llog4cplus $MULTITHREADING_FLAG"
-AC_SUBST(LOG4CPLUS_LDFLAGS)
+AC_SUBST(LOG4CPLUS_LIBS)
AC_SUBST(LOG4CPLUS_INCLUDES)
CPPFLAGS_SAVED=$CPPFLAGS
CPPFLAGS="$LOG4CPLUS_INCLUDES $CPPFLAGS"
-LDFLAGS_SAVED="$LDFLAGS"
-LDFLAGS="$LOG4CPLUS_LDFLAGS $LDFLAGS"
+LIBS_SAVED="$LIBS"
+LIBS="$LOG4CPLUS_LIBS $LIBS"
AC_CHECK_HEADERS([log4cplus/logger.h],,AC_MSG_ERROR([Missing required header files.]))
AC_LINK_IFELSE(
@@ -582,7 +592,7 @@ AC_LINK_IFELSE(
)
CPPFLAGS=$CPPFLAGS_SAVED
-LDFLAGS=$LDFLAGS_SAVED
+LIBS=$LIBS_SAVED
#
# Configure Boost header path
@@ -675,6 +685,13 @@ else
AM_CONDITIONAL(NEED_LIBBOOST_THREAD, test "${use_boost_threads}" = "yes")
fi
+# I can't get some of the #include <asio.hpp> right without this
+# TODO: find the real cause of asio/boost wanting pthreads
+# (this currently only occurs for src/lib/cc/session_unittests)
+PTHREAD_LDFLAGS=
+AC_CHECK_LIB(pthread, pthread_create,[ PTHREAD_LDFLAGS=-lpthread ], [])
+AC_SUBST(PTHREAD_LDFLAGS)
+AC_SUBST(MULTITHREADING_FLAG)
#
# Check availability of gtest, which will be used for unit tests.
@@ -711,6 +728,48 @@ then
GTEST_LDFLAGS="-L$dir/lib"
GTEST_LDADD="-lgtest"
GTEST_FOUND="true"
+ # There is no gtest-config script on this
+ # system, which is supposed to inform us
+ # whether we need pthreads as well (a
+ # gtest compile-time option). So we still
+ # need to test that manually.
+ CPPFLAGS_SAVED="$CPPFLAGS"
+ CPPFLAGS="$CPPFLAGS $GTEST_INCLUDES"
+ LDFLAGS_SAVED="$LDFLAGS"
+ LDFLAGS="$LDFLAGS $GTEST_LDFLAGS"
+ LIBS_SAVED=$LIBS
+ LIBS="$LIBS $GTEST_LDADD"
+ AC_MSG_CHECKING([Checking whether gtest tests need pthreads])
+ # First try to compile without pthreads
+ AC_TRY_LINK([
+ #include <gtest/gtest.h>
+ ],[
+ int i = 0;
+ char* c = NULL;
+ ::testing::InitGoogleTest(&i, &c);
+ return (0);
+ ],
+ [ AC_MSG_RESULT(no) ],
+ [
+ LIBS="$SAVED_LIBS $GTEST_LDADD $PTHREAD_LDFLAGS"
+ # Now try to compile with pthreads
+ AC_TRY_LINK([
+ #include <gtest/gtest.h>
+ ],[
+ int i = 0;
+ char* c = NULL;
+ ::testing::InitGoogleTest(&i, &c);
+ return (0);
+ ],
+ [ AC_MSG_RESULT(yes)
+ GTEST_LDADD="$GTEST_LDADD $PTHREAD_LDFLAGS"
+ ],
+ # Apparently we can't compile it at all
+ [ AC_MSG_ERROR(unable to compile with gtest) ])
+ ])
+ CPPFLAGS=$CPPFLAGS_SAVED
+ LDFLAGS=$LDFLAGS_SAVED
+ LIBS=$LIBS_SAVED
break
fi
done
@@ -737,15 +796,6 @@ if test "x$HAVE_PKG_CONFIG" = "xno" ; then
fi
PKG_CHECK_MODULES(SQLITE, sqlite3 >= 3.3.9, enable_features="$enable_features SQLite3")
-# I can't get some of the #include <asio.hpp> right without this
-# TODO: find the real cause of asio/boost wanting pthreads
-# (this currently only occurs for src/lib/cc/session_unittests)
-PTHREAD_LDFLAGS=
-AC_CHECK_LIB(pthread, pthread_create,[ PTHREAD_LDFLAGS=-lpthread ], [])
-AC_SUBST(PTHREAD_LDFLAGS)
-
-AC_SUBST(MULTITHREADING_FLAG)
-
#
# ASIO: we extensively use it as the C++ event management module.
#
@@ -1088,8 +1138,9 @@ dnl includes too
Boost: ${BOOST_INCLUDES}
Botan: ${BOTAN_INCLUDES}
${BOTAN_LDFLAGS}
+ ${BOTAN_LIBS}
Log4cplus: ${LOG4CPLUS_INCLUDES}
- ${LOG4CPLUS_LDFLAGS}
+ ${LOG4CPLUS_LIBS}
SQLite: $SQLITE_CFLAGS
$SQLITE_LIBS
diff --git a/src/bin/auth/benchmarks/Makefile.am b/src/bin/auth/benchmarks/Makefile.am
index 53c019f..dd00ea5 100644
--- a/src/bin/auth/benchmarks/Makefile.am
+++ b/src/bin/auth/benchmarks/Makefile.am
@@ -32,8 +32,8 @@ query_bench_LDADD += $(top_builddir)/src/lib/cc/libcc.la
query_bench_LDADD += $(top_builddir)/src/lib/xfr/libxfr.la
query_bench_LDADD += $(top_builddir)/src/lib/log/liblog.la
query_bench_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
-query_bench_LDADD += $(top_builddir)/src/lib/asiodns/libasiodns.la
query_bench_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
query_bench_LDADD += $(top_builddir)/src/lib/server_common/libserver_common.la
+query_bench_LDADD += $(top_builddir)/src/lib/asiodns/libasiodns.la
query_bench_LDADD += $(SQLITE_LIBS)
diff --git a/src/bin/dhcp6/.gitignore b/src/bin/dhcp6/.gitignore
index 6a6060b..e4e8f2d 100644
--- a/src/bin/dhcp6/.gitignore
+++ b/src/bin/dhcp6/.gitignore
@@ -7,3 +7,4 @@ Makefile.in
b10-dhcp6
spec_config.h
spec_config.h.pre
+tests/dhcp6_unittests
diff --git a/src/bin/dhcp6/dhcp6_srv.cc b/src/bin/dhcp6/dhcp6_srv.cc
index ba5afec..d5a969f 100644
--- a/src/bin/dhcp6/dhcp6_srv.cc
+++ b/src/bin/dhcp6/dhcp6_srv.cc
@@ -12,26 +12,32 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
-#include "dhcp/dhcp6.h"
-#include "dhcp/pkt6.h"
-#include "dhcp6/iface_mgr.h"
-#include "dhcp6/dhcp6_srv.h"
-#include "dhcp/option6_ia.h"
-#include "dhcp/option6_iaaddr.h"
-#include "asiolink/io_address.h"
+#include <dhcp/dhcp6.h>
+#include <dhcp/pkt6.h>
+#include <dhcp6/iface_mgr.h>
+#include <dhcp6/dhcp6_srv.h>
+#include <dhcp/option6_ia.h>
+#include <dhcp/option6_iaaddr.h>
+#include <asiolink/io_address.h>
+#include <exceptions/exceptions.h>
using namespace std;
using namespace isc;
using namespace isc::dhcp;
using namespace isc::asiolink;
-Dhcpv6Srv::Dhcpv6Srv() {
+Dhcpv6Srv::Dhcpv6Srv(uint16_t port) {
+
+//void Dhcpv6Srv::Dhcpv6Srv_impl(uint16_t port) {
cout << "Initialization" << endl;
- // first call to instance() will create IfaceMgr (it's a singleton)
- // it may throw something if things go wrong
+ // First call to instance() will create IfaceMgr (it's a singleton).
+ // It may throw something if things go wrong.
IfaceMgr::instance();
+ // Now try to open IPv6 sockets on detected interfaces.
+ IfaceMgr::instance().openSockets(port);
+
/// @todo: instantiate LeaseMgr here once it is imlpemented.
setServerID();
@@ -41,6 +47,8 @@ Dhcpv6Srv::Dhcpv6Srv() {
Dhcpv6Srv::~Dhcpv6Srv() {
cout << "DHCPv6 Srv shutdown." << endl;
+
+ IfaceMgr::instance().closeSockets();
}
bool
@@ -49,7 +57,7 @@ Dhcpv6Srv::run() {
boost::shared_ptr<Pkt6> query; // client's message
boost::shared_ptr<Pkt6> rsp; // server's response
- query = IfaceMgr::instance().receive();
+ query = IfaceMgr::instance().receive6();
if (query) {
if (!query->unpack()) {
diff --git a/src/bin/dhcp6/dhcp6_srv.h b/src/bin/dhcp6/dhcp6_srv.h
index 4daef3a..bcc7818 100644
--- a/src/bin/dhcp6/dhcp6_srv.h
+++ b/src/bin/dhcp6/dhcp6_srv.h
@@ -17,8 +17,9 @@
#include <boost/shared_ptr.hpp>
#include <boost/noncopyable.hpp>
-#include "dhcp/pkt6.h"
-#include "dhcp/option.h"
+#include <dhcp/dhcp6.h>
+#include <dhcp/pkt6.h>
+#include <dhcp/option.h>
#include <iostream>
namespace isc {
@@ -41,10 +42,12 @@ public:
/// In particular, creates IfaceMgr that will be responsible for
/// network interaction. Will instantiate lease manager, and load
/// old or create new DUID.
- Dhcpv6Srv();
+ ///
+ /// @param port port on will all sockets will listen
+ Dhcpv6Srv(uint16_t port = DHCP6_SERVER_PORT);
/// @brief Destructor. Used during DHCPv6 service shutdown.
- ~Dhcpv6Srv();
+ virtual ~Dhcpv6Srv();
/// @brief Returns server-intentifier option
///
diff --git a/src/bin/dhcp6/iface_mgr.cc b/src/bin/dhcp6/iface_mgr.cc
index a96db07..60dac63 100644
--- a/src/bin/dhcp6/iface_mgr.cc
+++ b/src/bin/dhcp6/iface_mgr.cc
@@ -18,9 +18,9 @@
#include <netinet/in.h>
#include <arpa/inet.h>
-#include "dhcp/dhcp6.h"
-#include "dhcp6/iface_mgr.h"
-#include "exceptions/exceptions.h"
+#include <dhcp/dhcp6.h>
+#include <dhcp6/iface_mgr.h>
+#include <exceptions/exceptions.h>
using namespace std;
using namespace isc;
@@ -79,6 +79,30 @@ IfaceMgr::Iface::getPlainMac() const {
return (tmp.str());
}
+bool IfaceMgr::Iface::delAddress(const isc::asiolink::IOAddress& addr) {
+
+ // Let's delete all addresses that match. It really shouldn't matter
+ // if we delete first or all, as the OS should allow to add a single
+ // address to an interface only once. If OS allows multiple instances
+ // of the same address added, we are in deep problems anyway.
+ size_t size = addrs_.size();
+ addrs_.erase(remove(addrs_.begin(), addrs_.end(), addr), addrs_.end());
+ return (addrs_.size() < size);
+}
+
+bool IfaceMgr::Iface::delSocket(uint16_t sockfd) {
+ list<SocketInfo>::iterator sock = sockets_.begin();
+ while (sock!=sockets_.end()) {
+ if (sock->sockfd_ == sockfd) {
+ close(sockfd);
+ sockets_.erase(sock);
+ return (true); //socket found
+ }
+ ++sock;
+ }
+ return (false); // socket not found
+}
+
IfaceMgr::IfaceMgr()
:control_buf_len_(CMSG_SPACE(sizeof(struct in6_pktinfo))),
control_buf_(new char[control_buf_len_])
@@ -95,9 +119,6 @@ IfaceMgr::IfaceMgr()
detectIfaces();
- if (!openSockets()) {
- isc_throw(Unexpected, "Failed to open/bind sockets.");
- }
} catch (const std::exception& ex) {
cout << "IfaceMgr creation failed:" << ex.what() << endl;
@@ -109,7 +130,23 @@ IfaceMgr::IfaceMgr()
}
}
+void IfaceMgr::closeSockets() {
+ for (IfaceCollection::iterator iface = ifaces_.begin();
+ iface != ifaces_.end(); ++iface) {
+
+ for (SocketCollection::iterator sock = iface->sockets_.begin();
+ sock != iface->sockets_.end(); ++sock) {
+ cout << "Closing socket " << sock->sockfd_ << endl;
+ close(sock->sockfd_);
+ }
+ iface->sockets_.clear();
+ }
+
+}
+
IfaceMgr::~IfaceMgr() {
+ closeSockets();
+
// control_buf_ is deleted automatically (scoped_ptr)
control_buf_len_ = 0;
}
@@ -139,8 +176,8 @@ IfaceMgr::detectIfaces() {
Iface iface(ifaceName, if_nametoindex( ifaceName.c_str() ) );
IOAddress addr(linkLocal);
- iface.addrs_.push_back(addr);
- ifaces_.push_back(iface);
+ iface.addAddress(addr);
+ addInterface(iface);
interfaces.close();
} catch (const std::exception& ex) {
// TODO: deallocate whatever memory we used
@@ -154,51 +191,55 @@ IfaceMgr::detectIfaces() {
}
}
-bool
-IfaceMgr::openSockets() {
- int sock;
+void
+IfaceMgr::openSockets(uint16_t port) {
+ int sock1, sock2;
+
+ for (IfaceCollection::iterator iface = ifaces_.begin();
+ iface != ifaces_.end(); ++iface) {
- for (IfaceLst::iterator iface=ifaces_.begin();
- iface!=ifaces_.end();
- ++iface) {
+ AddressCollection addrs = iface->getAddresses();
- for (Addr6Lst::iterator addr=iface->addrs_.begin();
- addr!=iface->addrs_.end();
+ for (AddressCollection::iterator addr = addrs.begin();
+ addr != addrs.end();
++addr) {
- sock = openSocket(iface->name_, *addr,
- DHCP6_SERVER_PORT);
- if (sock<0) {
- cout << "Failed to open unicast socket." << endl;
- return (false);
+ sock1 = openSocket(iface->getName(), *addr, port);
+ if (sock1 < 0) {
+ isc_throw(Unexpected, "Failed to open unicast socket on "
+ << " interface " << iface->getFullName());
}
- sendsock_ = sock;
-
- sock = openSocket(iface->name_,
- IOAddress(ALL_DHCP_RELAY_AGENTS_AND_SERVERS),
- DHCP6_SERVER_PORT);
- if (sock<0) {
- cout << "Failed to open multicast socket." << endl;
- close(sendsock_);
- return (false);
+
+ if ( !joinMcast(sock1, iface->getName(),
+ string(ALL_DHCP_RELAY_AGENTS_AND_SERVERS) ) ) {
+ close(sock1);
+ isc_throw(Unexpected, "Failed to join " << ALL_DHCP_RELAY_AGENTS_AND_SERVERS
+ << " multicast group.");
+ }
+
+ // this doesn't work too well on NetBSD
+ sock2 = openSocket(iface->getName(),
+ IOAddress(ALL_DHCP_RELAY_AGENTS_AND_SERVERS),
+ port);
+ if (sock2 < 0) {
+ isc_throw(Unexpected, "Failed to open multicast socket on "
+ << " interface " << iface->getFullName());
+ iface->delSocket(sock1); // delete previously opened socket
}
- recvsock_ = sock;
}
}
-
- return (true);
}
void
IfaceMgr::printIfaces(std::ostream& out /*= std::cout*/) {
- for (IfaceLst::const_iterator iface=ifaces_.begin();
- iface!=ifaces_.end();
- ++iface) {
+ for (IfaceCollection::const_iterator iface = ifaces_.begin();
+ iface != ifaces_.end(); ++iface) {
out << "Detected interface " << iface->getFullName() << endl;
- out << " " << iface->addrs_.size() << " addr(s):" << endl;
- for (Addr6Lst::const_iterator addr=iface->addrs_.begin();
- addr != iface->addrs_.end();
- ++addr) {
+ out << " " << iface->getAddresses().size() << " addr(s):" << endl;
+ const AddressCollection addrs = iface->getAddresses();
+
+ for (AddressCollection::const_iterator addr = addrs.begin();
+ addr != addrs.end(); ++addr) {
out << " " << addr->toText() << endl;
}
out << " mac: " << iface->getPlainMac() << endl;
@@ -207,11 +248,11 @@ IfaceMgr::printIfaces(std::ostream& out /*= std::cout*/) {
IfaceMgr::Iface*
IfaceMgr::getIface(int ifindex) {
- for (IfaceLst::iterator iface=ifaces_.begin();
- iface!=ifaces_.end();
- ++iface) {
- if (iface->ifindex_ == ifindex)
+ for (IfaceCollection::iterator iface = ifaces_.begin();
+ iface != ifaces_.end(); ++iface) {
+ if (iface->getIndex() == ifindex) {
return (&(*iface));
+ }
}
return (NULL); // not found
@@ -219,29 +260,87 @@ IfaceMgr::getIface(int ifindex) {
IfaceMgr::Iface*
IfaceMgr::getIface(const std::string& ifname) {
- for (IfaceLst::iterator iface=ifaces_.begin();
- iface!=ifaces_.end();
- ++iface) {
- if (iface->name_ == ifname)
+ for (IfaceCollection::iterator iface = ifaces_.begin();
+ iface != ifaces_.end(); ++iface) {
+ if (iface->getName() == ifname) {
return (&(*iface));
+ }
}
return (NULL); // not found
}
int
-IfaceMgr::openSocket(const std::string& ifname,
- const IOAddress& addr,
+IfaceMgr::openSocket(const std::string& ifname, const IOAddress& addr,
int port) {
- struct sockaddr_in6 addr6;
+ Iface* iface = getIface(ifname);
+ if (!iface) {
+ isc_throw(BadValue, "There is no " << ifname << " interface present.");
+ }
+ switch (addr.getFamily()) {
+ case AF_INET:
+ return openSocket4(*iface, addr, port);
+ case AF_INET6:
+ return openSocket6(*iface, addr, port);
+ default:
+ isc_throw(BadValue, "Failed to detect family of address: "
+ << addr.toText());
+ }
+}
+
+int
+IfaceMgr::openSocket4(Iface& iface, const IOAddress& addr, int port) {
+
+ cout << "Creating UDP4 socket on " << iface.getFullName()
+ << " " << addr.toText() << "/port=" << port << endl;
+
+ struct sockaddr_in addr4;
+ memset(&addr4, 0, sizeof(sockaddr));
+ addr4.sin_family = AF_INET;
+ addr4.sin_port = htons(port);
+ memcpy(&addr4.sin_addr, addr.getAddress().to_v4().to_bytes().data(),
+ sizeof(addr4.sin_addr));
+
+ int sock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock < 0) {
+ isc_throw(Unexpected, "Failed to create UDP6 socket.");
+ }
+
+ if (bind(sock, (struct sockaddr *)&addr4, sizeof(addr4)) < 0) {
+ close(sock);
+ isc_throw(Unexpected, "Failed to bind socket " << sock << " to " << addr.toText()
+ << "/port=" << port);
+ }
+
+ // If there is no support for IP_PKTINFO, we are really out of luck.
+ // It will be difficult to understand, where this packet came from.
+#if defined(IP_PKTINFO)
+ int flag = 1;
+ if (setsockopt(sock, IPPROTO_IP, IP_PKTINFO, &flag, sizeof(flag)) != 0) {
+ close(sock);
+ isc_throw(Unexpected, "setsockopt: IP_PKTINFO: failed.");
+ }
+#endif
+
+ cout << "Created socket " << sock << " on " << iface.getName() << "/" <<
+ addr.toText() << "/port=" << port << endl;
- cout << "Creating socket on " << ifname << "/" << addr.toText()
- << "/port=" << port << endl;
+ iface.addSocket(SocketInfo(sock, addr, port));
+ return (sock);
+}
+
+int
+IfaceMgr::openSocket6(Iface& iface, const IOAddress& addr, int port) {
+
+ cout << "Creating UDP6 socket on " << iface.getFullName()
+ << " " << addr.toText() << "/port=" << port << endl;
+
+ struct sockaddr_in6 addr6;
memset(&addr6, 0, sizeof(addr6));
addr6.sin6_family = AF_INET6;
addr6.sin6_port = htons(port);
- addr6.sin6_scope_id = if_nametoindex(ifname.c_str());
+ addr6.sin6_scope_id = if_nametoindex(iface.getName().c_str());
memcpy(&addr6.sin6_addr,
addr.getAddress().to_v6().to_bytes().data(),
@@ -255,61 +354,58 @@ IfaceMgr::openSocket(const std::string& ifname,
// make a socket
int sock = socket(AF_INET6, SOCK_DGRAM, 0);
if (sock < 0) {
- cout << "Failed to create UDP6 socket." << endl;
- return (-1);
+ isc_throw(Unexpected, "Failed to create UDP6 socket.");
}
- /* Set the REUSEADDR option so that we don't fail to start if
- we're being restarted. */
+ // Set the REUSEADDR option so that we don't fail to start if
+ // we're being restarted.
int flag = 1;
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
(char *)&flag, sizeof(flag)) < 0) {
- cout << "Can't set SO_REUSEADDR option on dhcpv6 socket." << endl;
close(sock);
- return (-1);
+ isc_throw(Unexpected, "Can't set SO_REUSEADDR option on dhcpv6 socket.");
}
if (bind(sock, (struct sockaddr *)&addr6, sizeof(addr6)) < 0) {
- cout << "Failed to bind socket " << sock << " to " << addr.toText()
- << "/port=" << port << endl;
close(sock);
- return (-1);
+ isc_throw(Unexpected, "Failed to bind socket " << sock << " to " << addr.toText()
+ << "/port=" << port);
}
#ifdef IPV6_RECVPKTINFO
- /* RFC3542 - a new way */
+ // RFC3542 - a new way
if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO,
&flag, sizeof(flag)) != 0) {
- cout << "setsockopt: IPV6_RECVPKTINFO failed." << endl;
close(sock);
- return (-1);
+ isc_throw(Unexpected, "setsockopt: IPV6_RECVPKTINFO failed.");
}
#else
- /* RFC2292 - an old way */
+ // RFC2292 - an old way
if (setsockopt(sock, IPPROTO_IPV6, IPV6_PKTINFO,
&flag, sizeof(flag)) != 0) {
- cout << "setsockopt: IPV6_PKTINFO: failed." << endl;
close(sock);
- return (-1);
+ isc_throw(Unexpected, "setsockopt: IPV6_PKTINFO: failed.");
}
#endif
// multicast stuff
-
if (addr.getAddress().to_v6().is_multicast()) {
// both mcast (ALL_DHCP_RELAY_AGENTS_AND_SERVERS and ALL_DHCP_SERVERS)
// are link and site-scoped, so there is no sense to join those groups
// with global addresses.
- if ( !joinMcast( sock, ifname,
+ if ( !joinMcast( sock, iface.getName(),
string(ALL_DHCP_RELAY_AGENTS_AND_SERVERS) ) ) {
close(sock);
- return (-1);
+ isc_throw(Unexpected, "Failed to join " << ALL_DHCP_RELAY_AGENTS_AND_SERVERS
+ << " multicast group.");
}
}
- cout << "Created socket " << sock << " on " << ifname << "/" <<
+ cout << "Created socket " << sock << " on " << iface.getName() << "/" <<
addr.toText() << "/port=" << port << endl;
+ iface.addSocket(SocketInfo(sock, addr, port));
+
return (sock);
}
@@ -345,16 +441,19 @@ IfaceMgr::send(boost::shared_ptr<Pkt6>& pkt) {
int result;
struct in6_pktinfo *pktinfo;
struct cmsghdr *cmsg;
+
+ Iface* iface = getIface(pkt->iface_);
+ if (!iface) {
+ isc_throw(BadValue, "Unable to send Pkt6. Invalid interface ("
+ << pkt->iface_ << ") specified.");
+ }
+
memset(&control_buf_[0], 0, control_buf_len_);
- /*
- * Initialize our message header structure.
- */
+ // Initialize our message header structure.
memset(&m, 0, sizeof(m));
- /*
- * Set the target address we're sending to.
- */
+ // Set the target address we're sending to.
sockaddr_in6 to;
memset(&to, 0, sizeof(to));
to.sin6_family = AF_INET6;
@@ -367,24 +466,20 @@ IfaceMgr::send(boost::shared_ptr<Pkt6>& pkt) {
m.msg_name = &to;
m.msg_namelen = sizeof(to);
- /*
- * Set the data buffer we're sending. (Using this wacky
- * "scatter-gather" stuff... we only have a single chunk
- * of data to send, so we declare a single vector entry.)
- */
+ // Set the data buffer we're sending. (Using this wacky
+ // "scatter-gather" stuff... we only have a single chunk
+ // of data to send, so we declare a single vector entry.)
v.iov_base = (char *) &pkt->data_[0];
v.iov_len = pkt->data_len_;
m.msg_iov = &v;
m.msg_iovlen = 1;
- /*
- * Setting the interface is a bit more involved.
- *
- * We have to create a "control message", and set that to
- * define the IPv6 packet information. We could set the
- * source address if we wanted, but we can safely let the
- * kernel decide what that should be.
- */
+ // Setting the interface is a bit more involved.
+ //
+ // We have to create a "control message", and set that to
+ // define the IPv6 packet information. We could set the
+ // source address if we wanted, but we can safely let the
+ // kernel decide what that should be.
m.msg_control = &control_buf_[0];
m.msg_controllen = control_buf_len_;
cmsg = CMSG_FIRSTHDR(&m);
@@ -396,14 +491,12 @@ IfaceMgr::send(boost::shared_ptr<Pkt6>& pkt) {
pktinfo->ipi6_ifindex = pkt->ifindex_;
m.msg_controllen = cmsg->cmsg_len;
- result = sendmsg(sendsock_, &m, 0);
+ result = sendmsg(getSocket(*pkt), &m, 0);
if (result < 0) {
cout << "Send packet failed." << endl;
}
- cout << "Sent " << result << " bytes." << endl;
-
- cout << "Sent " << pkt->data_len_ << " bytes over "
- << pkt->iface_ << "/" << pkt->ifindex_ << " interface: "
+ cout << "Sent " << pkt->data_len_ << " bytes over socket " << getSocket(*pkt)
+ << " on " << iface->getFullName() << " interface: "
<< " dst=" << pkt->remote_addr_.toText()
<< ", src=" << pkt->local_addr_.toText()
<< endl;
@@ -411,8 +504,24 @@ IfaceMgr::send(boost::shared_ptr<Pkt6>& pkt) {
return (result);
}
+bool
+IfaceMgr::send(boost::shared_ptr<Pkt4>& )
+{
+ /// TODO: Implement this (ticket #1240)
+ isc_throw(NotImplemented, "Pkt4 send not implemented yet.");
+}
+
+
+boost::shared_ptr<Pkt4>
+IfaceMgr::receive4() {
+ isc_throw(NotImplemented, "Pkt4 reception not implemented yet.");
+
+ // TODO: To be implemented (ticket #1239)
+ return (boost::shared_ptr<Pkt4>()); // NULL
+}
+
boost::shared_ptr<Pkt6>
-IfaceMgr::receive() {
+IfaceMgr::receive6() {
struct msghdr m;
struct iovec v;
int result;
@@ -442,49 +551,66 @@ IfaceMgr::receive() {
memset(&from, 0, sizeof(from));
memset(&to_addr, 0, sizeof(to_addr));
- /*
- * Initialize our message header structure.
- */
+ // Initialize our message header structure.
memset(&m, 0, sizeof(m));
- /*
- * Point so we can get the from address.
- */
+ // Point so we can get the from address.
m.msg_name = &from;
m.msg_namelen = sizeof(from);
- /*
- * Set the data buffer we're receiving. (Using this wacky
- * "scatter-gather" stuff... but we that doesn't really make
- * sense for us, so we use a single vector entry.)
- */
+ // Set the data buffer we're receiving. (Using this wacky
+ // "scatter-gather" stuff... but we that doesn't really make
+ // sense for us, so we use a single vector entry.)
v.iov_base = (void*)&pkt->data_[0];
v.iov_len = pkt->data_len_;
m.msg_iov = &v;
m.msg_iovlen = 1;
- /*
- * Getting the interface is a bit more involved.
- *
- * We set up some space for a "control message". We have
- * previously asked the kernel to give us packet
- * information (when we initialized the interface), so we
- * should get the destination address from that.
- */
+ // Getting the interface is a bit more involved.
+ //
+ // We set up some space for a "control message". We have
+ // previously asked the kernel to give us packet
+ // information (when we initialized the interface), so we
+ // should get the destination address from that.
m.msg_control = &control_buf_[0];
m.msg_controllen = control_buf_len_;
- result = recvmsg(recvsock_, &m, 0);
+ /// TODO: Need to move to select() and pool over
+ /// all available sockets. For now, we just take the
+ /// first interface and use first socket from it.
+ IfaceCollection::const_iterator iface = ifaces_.begin();
+ if (iface == ifaces_.end()) {
+ isc_throw(Unexpected, "No interfaces detected. Can't receive anything.");
+ }
+ SocketCollection::const_iterator s = iface->sockets_.begin();
+ const SocketInfo* candidate = 0;
+ while (s != iface->sockets_.end()) {
+ if (s->addr_.getAddress().to_v6().is_multicast()) {
+ candidate = &(*s);
+ break;
+ }
+ if (!candidate) {
+ candidate = &(*s); // it's not multicast, but it's better than none
+ }
+ ++s;
+ }
+ if (!candidate) {
+ isc_throw(Unexpected, "Interface " << iface->getFullName()
+ << " does not have any sockets open.");
+ }
+
+ cout << "Trying to receive over socket " << candidate->sockfd_ << " bound to "
+ << candidate->addr_.toText() << "/port=" << candidate->port_ << " on "
+ << iface->getFullName() << endl;
+ result = recvmsg(candidate->sockfd_, &m, 0);
if (result >= 0) {
- /*
- * If we did read successfully, then we need to loop
- * through the control messages we received and
- * find the one with our destination address.
- *
- * We also keep a flag to see if we found it. If we
- * didn't, then we consider this to be an error.
- */
+ // If we did read successfully, then we need to loop
+ // through the control messages we received and
+ // find the one with our destination address.
+ //
+ // We also keep a flag to see if we found it. If we
+ // didn't, then we consider this to be an error.
int found_pktinfo = 0;
cmsg = CMSG_FIRSTHDR(&m);
while (cmsg != NULL) {
@@ -520,7 +646,7 @@ IfaceMgr::receive() {
Iface* received = getIface(pkt->ifindex_);
if (received) {
- pkt->iface_ = received->name_;
+ pkt->iface_ = received->getName();
} else {
cout << "Received packet over unknown interface (ifindex="
<< pkt->ifindex_ << ")." << endl;
@@ -539,4 +665,60 @@ IfaceMgr::receive() {
return (pkt);
}
+uint16_t
+IfaceMgr::getSocket(isc::dhcp::Pkt6 const& pkt) {
+ Iface* iface = getIface(pkt.iface_);
+ if (!iface) {
+ isc_throw(BadValue, "Tried to find socket for non-existent interface "
+ << pkt.iface_);
+ }
+
+ SocketCollection::const_iterator s;
+ for (s = iface->sockets_.begin(); s != iface->sockets_.end(); ++s) {
+ if (s->family_ != AF_INET6) {
+ // don't use IPv4 sockets
+ continue;
+ }
+ if (s->addr_.getAddress().to_v6().is_multicast()) {
+ // don't use IPv6 sockets bound to multicast address
+ continue;
+ }
+ /// TODO: Add more checks here later. If remote address is
+ /// not link-local, we can't use link local bound socket
+ /// to send data.
+
+ return (s->sockfd_);
+ }
+
+ isc_throw(Unexpected, "Interface " << iface->getFullName()
+ << " does not have any suitable IPv6 sockets open.");
+}
+
+uint16_t
+IfaceMgr::getSocket(isc::dhcp::Pkt4 const& pkt) {
+ Iface* iface = getIface(pkt.getIface());
+ if (!iface) {
+ isc_throw(BadValue, "Tried to find socket for non-existent interface "
+ << pkt.getIface());
+ }
+
+ SocketCollection::const_iterator s;
+ for (s = iface->sockets_.begin(); s != iface->sockets_.end(); ++s) {
+ if (s->family_ != AF_INET) {
+ // don't use IPv4 sockets
+ continue;
+ }
+ /// TODO: Add more checks here later. If remote address is
+ /// not link-local, we can't use link local bound socket
+ /// to send data.
+
+ return (s->sockfd_);
+ }
+
+ isc_throw(Unexpected, "Interface " << iface->getFullName()
+ << " does not have any suitable IPv4 sockets open.");
+}
+
+
+
}
diff --git a/src/bin/dhcp6/iface_mgr.h b/src/bin/dhcp6/iface_mgr.h
index 249c7ef..0aa2592 100644
--- a/src/bin/dhcp6/iface_mgr.h
+++ b/src/bin/dhcp6/iface_mgr.h
@@ -19,8 +19,9 @@
#include <boost/shared_ptr.hpp>
#include <boost/scoped_array.hpp>
#include <boost/noncopyable.hpp>
-#include "asiolink/io_address.h"
-#include "dhcp/pkt6.h"
+#include <asiolink/io_address.h>
+#include <dhcp/pkt4.h>
+#include <dhcp/pkt6.h>
namespace isc {
@@ -34,26 +35,119 @@ namespace dhcp {
class IfaceMgr : public boost::noncopyable {
public:
/// type that defines list of addresses
- typedef std::list<isc::asiolink::IOAddress> Addr6Lst;
+ typedef std::vector<isc::asiolink::IOAddress> AddressCollection;
/// maximum MAC address length (Infiniband uses 20 bytes)
static const unsigned int MAX_MAC_LEN = 20;
+ /// Holds information about socket.
+ struct SocketInfo {
+ uint16_t sockfd_; /// socket descriptor
+ isc::asiolink::IOAddress addr_; /// bound address
+ uint16_t port_; /// socket port
+ uint16_t family_; /// IPv4 or IPv6
+
+ /// @brief SocketInfo constructor.
+ ///
+ /// @param sockfd socket descriptor
+ /// @param addr an address the socket is bound to
+ /// @param port a port the socket is bound to
+ SocketInfo(uint16_t sockfd, const isc::asiolink::IOAddress& addr,
+ uint16_t port)
+ :sockfd_(sockfd), addr_(addr), port_(port), family_(addr.getFamily()) { }
+ };
+
+ /// type that holds a list of socket informations
+ typedef std::list<SocketInfo> SocketCollection;
+
/// @brief represents a single network interface
///
/// Iface structure represents network interface with all useful
/// information, like name, interface index, MAC address and
/// list of assigned addresses
- struct Iface {
- /// constructor
+ class Iface {
+ public:
+ /// @brief Iface constructor.
+ ///
+ /// Creates Iface object that represents network interface.
+ ///
+ /// @param name name of the interface
+ /// @param ifindex interface index (unique integer identifier)
Iface(const std::string& name, int ifindex);
- /// returns full interface name in format ifname/ifindex
+ /// @brief Returns full interface name as "ifname/ifindex" string.
+ ///
+ /// @return string with interface name
std::string getFullName() const;
- /// returns link-layer address a plain text
+ /// @brief Returns link-layer address a plain text.
+ ///
+ /// @return MAC address as a plain text (string)
std::string getPlainMac() const;
+ /// @brief Returns interface index.
+ ///
+ /// @return interface index
+ uint16_t getIndex() const { return ifindex_; }
+
+ /// @brief Returns interface name.
+ ///
+ /// @return interface name
+ std::string getName() const { return name_; };
+
+ /// @brief Returns all interfaces available on an interface.
+ ///
+ /// Care should be taken to not use this collection after Iface object
+ /// ceases to exist. That is easy in most cases as Iface objects are
+ /// created by IfaceMgr that is a singleton an is expected to be
+ /// available at all time. We may revisit this if we ever decide to
+ /// implement dynamic interface detection, but such fancy feature would
+ /// mostly be useful for clients with wifi/vpn/virtual interfaces.
+ ///
+ /// @return collection of addresses
+ const AddressCollection& getAddresses() const { return addrs_; }
+
+ /// @brief Adds an address to an interface.
+ ///
+ /// This only adds an address to collection, it does not physically
+ /// configure address on actual network interface.
+ ///
+ /// @param addr address to be added
+ void addAddress(const isc::asiolink::IOAddress& addr) {
+ addrs_.push_back(addr);
+ }
+
+ /// @brief Deletes an address from an interface.
+ ///
+ /// This only deletes address from collection, it does not physically
+ /// remove address configuration from actual network interface.
+ ///
+ /// @param addr address to be removed.
+ ///
+ /// @return true if removal was successful (address was in collection),
+ /// false otherwise
+ bool delAddress(const isc::asiolink::IOAddress& addr);
+
+ /// @brief Adds socket descriptor to an interface.
+ ///
+ /// @param socket SocketInfo structure that describes socket.
+ void addSocket(const SocketInfo& sock)
+ { sockets_.push_back(sock); }
+
+ /// @brief Closes socket.
+ ///
+ /// Closes socket and removes corresponding SocketInfo structure
+ /// from an interface.
+ ///
+ /// @param socket descriptor to be closed/removed.
+ /// @return true if there was such socket, false otherwise
+ bool delSocket(uint16_t sockfd);
+
+ /// socket used to sending data
+ /// TODO: this should be protected
+ SocketCollection sockets_;
+
+ protected:
/// network interface name
std::string name_;
@@ -61,19 +155,13 @@ public:
int ifindex_;
/// list of assigned addresses
- Addr6Lst addrs_;
+ AddressCollection addrs_;
/// link-layer address
uint8_t mac_[MAX_MAC_LEN];
/// length of link-layer address (usually 6)
int mac_len_;
-
- /// socket used to sending data
- int sendsock_;
-
- /// socket used for receiving data
- int recvsock_;
};
// TODO performance improvement: we may change this into
@@ -81,7 +169,7 @@ public:
// also hide it (make it public make tests easier for now)
/// type that holds a list of interfaces
- typedef std::list<Iface> IfaceLst;
+ typedef std::list<Iface> IfaceCollection;
/// IfaceMgr is a singleton class. This method returns reference
/// to its sole instance.
@@ -109,27 +197,63 @@ public:
Iface*
getIface(const std::string& ifname);
+ /// @brief Return most suitable socket for transmitting specified IPv6 packet.
+ ///
+ /// This method takes Pkt6 (see overloaded implementation that takes
+ /// Pkt4) and chooses appropriate socket to send it. This method
+ /// may throw BadValue if specified packet does not have outbound
+ /// interface specified, no such interface exists, or specified
+ /// interface does not have any appropriate sockets open.
+ ///
+ /// @param pkt a packet to be transmitted
+ ///
+ /// @return a socket descriptor
+ uint16_t getSocket(const isc::dhcp::Pkt6& pkt);
+
+ /// @brief Return most suitable socket for transmitting specified IPv6 packet.
+ ///
+ /// This method takes Pkt4 (see overloaded implementation that takes
+ /// Pkt6) and chooses appropriate socket to send it. This method
+ /// may throw BadValue if specified packet does not have outbound
+ /// interface specified, no such interface exists, or specified
+ /// interface does not have any appropriate sockets open.
+ ///
+ /// @param pkt a packet to be transmitted
+ ///
+ /// @return a socket descriptor
+ uint16_t getSocket(const isc::dhcp::Pkt4& pkt);
+
/// debugging method that prints out all available interfaces
///
/// @param out specifies stream to print list of interfaces to
void
printIfaces(std::ostream& out = std::cout);
- /// @brief Sends a packet.
+ /// @brief Sends an IPv6 packet.
///
- /// Sends a packet. All parameters for actual transmission are specified in
+ /// Sends an IPv6 packet. All parameters for actual transmission are specified in
/// Pkt6 structure itself. That includes destination address, src/dst port
/// and interface over which data will be sent.
///
/// @param pkt packet to be sent
///
/// @return true if sending was successful
- bool
- send(boost::shared_ptr<Pkt6>& pkt);
+ bool send(boost::shared_ptr<Pkt6>& pkt);
- /// @brief Tries to receive packet over open sockets.
+ /// @brief Sends an IPv4 packet.
///
- /// Attempts to receive a single packet of any of the open sockets.
+ /// Sends an IPv4 packet. All parameters for actual transmission are specified
+ /// in Pkt4 structure itself. That includes destination address, src/dst
+ /// port and interface over which data will be sent.
+ ///
+ /// @param pkt a packet to be sent
+ ///
+ /// @return true if sending was successful
+ bool send(boost::shared_ptr<Pkt4>& pkt);
+
+ /// @brief Tries to receive IPv6 packet over open IPv6 sockets.
+ ///
+ /// Attempts to receive a single IPv6 packet of any of the open IPv6 sockets.
/// If reception is successful and all information about its sender
/// are obtained, Pkt6 object is created and returned.
///
@@ -138,7 +262,49 @@ public:
/// (e.g. remove expired leases)
///
/// @return Pkt6 object representing received packet (or NULL)
- boost::shared_ptr<Pkt6> receive();
+ boost::shared_ptr<Pkt6> receive6();
+
+ /// @brief Tries to receive IPv4 packet over open IPv4 sockets.
+ ///
+ /// Attempts to receive a single IPv4 packet of any of the open IPv4 sockets.
+ /// If reception is successful and all information about its sender
+ /// are obtained, Pkt4 object is created and returned.
+ ///
+ /// TODO Start using select() and add timeout to be able
+ /// to not wait infinitely, but rather do something useful
+ /// (e.g. remove expired leases)
+ ///
+ /// @return Pkt4 object representing received packet (or NULL)
+ boost::shared_ptr<Pkt4> receive4();
+
+ /// Opens UDP/IP socket and binds it to address, interface and port.
+ ///
+ /// Specific type of socket (UDP/IPv4 or UDP/IPv6) depends on passed addr
+ /// family.
+ ///
+ /// @param ifname name of the interface
+ /// @param addr address to be bound.
+ /// @param port UDP port.
+ ///
+ /// Method will throw if socket creation, socket binding or multicast
+ /// join fails.
+ ///
+ /// @return socket descriptor, if socket creation, binding and multicast
+ /// group join were all successful.
+ int openSocket(const std::string& ifname,
+ const isc::asiolink::IOAddress& addr, int port);
+
+ /// Opens IPv6 sockets on detected interfaces.
+ ///
+ /// Will throw exception if socket creation fails.
+ ///
+ /// @param port specifies port number (usually DHCP6_SERVER_PORT)
+ void openSockets(uint16_t port);
+
+
+ /// @brief Closes all open sockets.
+ /// Is used in destructor, but also from Dhcpv4_srv and Dhcpv6_srv classes.
+ void closeSockets();
// don't use private, we need derived classes in tests
protected:
@@ -146,11 +312,44 @@ protected:
/// @brief Protected constructor.
///
/// Protected constructor. This is a singleton class. We don't want
- /// anyone to create instances of IfaceMgr. Use instance() method
+ /// anyone to create instances of IfaceMgr. Use instance() method instead.
IfaceMgr();
~IfaceMgr();
+ /// @brief Opens IPv4 socket.
+ ///
+ /// Please do not use this method directly. Use openSocket instead.
+ ///
+ /// This method may throw exception if socket creation fails.
+ ///
+ /// @param iface reference to interface structure.
+ /// @param addr an address the created socket should be bound to
+ /// @param port a port that created socket should be bound to
+ ///
+ /// @return socket descriptor
+ int openSocket4(Iface& iface, const isc::asiolink::IOAddress& addr, int port);
+
+ /// @brief Opens IPv6 socket.
+ ///
+ /// Please do not use this method directly. Use openSocket instead.
+ ///
+ /// This method may throw exception if socket creation fails.
+ ///
+ /// @param iface reference to interface structure.
+ /// @param addr an address the created socket should be bound to
+ /// @param port a port that created socket should be bound to
+ ///
+ /// @return socket descriptor
+ int openSocket6(Iface& iface, const isc::asiolink::IOAddress& addr, int port);
+
+ /// @brief Adds an interface to list of known interfaces.
+ ///
+ /// @param iface reference to Iface object.
+ void addInterface(const Iface& iface) {
+ ifaces_.push_back(iface);
+ }
+
/// @brief Detects network interfaces.
///
/// This method will eventually detect available interfaces. For now
@@ -159,24 +358,11 @@ protected:
void
detectIfaces();
- ///
- /// Opens UDP/IPv6 socket and binds it to address, interface and port.
- ///
- /// @param ifname name of the interface
- /// @param addr address to be bound.
- /// @param port UDP port.
- ///
- /// @return socket descriptor, if socket creation, binding and multicast
- /// group join were all successful. -1 otherwise.
- int openSocket(const std::string& ifname,
- const isc::asiolink::IOAddress& addr,
- int port);
-
// TODO: having 2 maps (ifindex->iface and ifname->iface would)
// probably be better for performance reasons
/// List of available interfaces
- IfaceLst ifaces_;
+ IfaceCollection ifaces_;
/// a pointer to a sole instance of this class (a singleton)
static IfaceMgr * instance_;
@@ -184,8 +370,9 @@ protected:
// TODO: Also keep this interface on Iface once interface detection
// is implemented. We may need it e.g. to close all sockets on
// specific interface
- int recvsock_; // TODO: should be fd_set eventually, but we have only
- int sendsock_; // 2 sockets for now. Will do for until next release
+ //int recvsock_; // TODO: should be fd_set eventually, but we have only
+ //int sendsock_; // 2 sockets for now. Will do for until next release
+
// we can't use the same socket, as receiving socket
// is bound to multicast address. And we all know what happens
// to people who try to use multicast as source address.
@@ -197,9 +384,6 @@ protected:
boost::scoped_array<char> control_buf_;
private:
- /// Opens sockets on detected interfaces.
- bool
- openSockets();
/// creates a single instance of this class (a singleton implementation)
static void
@@ -221,6 +405,7 @@ private:
bool
joinMcast(int sock, const std::string& ifname,
const std::string& mcast);
+
};
}; // namespace isc::dhcp
diff --git a/src/bin/dhcp6/tests/Makefile.am b/src/bin/dhcp6/tests/Makefile.am
index 985368e..f37194c 100644
--- a/src/bin/dhcp6/tests/Makefile.am
+++ b/src/bin/dhcp6/tests/Makefile.am
@@ -25,8 +25,6 @@ check-local:
AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
AM_CPPFLAGS += -I$(top_builddir)/src/bin # for generated spec_config.h header
AM_CPPFLAGS += -I$(top_srcdir)/src/bin
-AM_CPPFLAGS += -I$(top_builddir)/src/lib/cc
-AM_CPPFLAGS += -I$(top_srcdir)/src/lib/asiolink
AM_CPPFLAGS += $(BOOST_INCLUDES)
AM_CPPFLAGS += -DTEST_DATA_DIR=\"$(abs_top_srcdir)/src/lib/testutils/testdata\"
AM_CPPFLAGS += -DTEST_DATA_BUILDDIR=\"$(abs_top_builddir)/src/bin/dhcp6/tests\"
@@ -57,8 +55,8 @@ dhcp6_unittests_LDADD = $(GTEST_LDADD)
dhcp6_unittests_LDADD += $(SQLITE_LIBS)
dhcp6_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
dhcp6_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libdhcp.la
-dhcp6_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
dhcp6_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
+dhcp6_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
endif
noinst_PROGRAMS = $(TESTS)
diff --git a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
index 72e48e4..50f37af 100644
--- a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
+++ b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
@@ -34,7 +34,7 @@ namespace test {
class NakedDhcpv6Srv: public Dhcpv6Srv {
// "naked" Interface Manager, exposes internal fields
public:
- NakedDhcpv6Srv() { }
+ NakedDhcpv6Srv():Dhcpv6Srv(DHCP6_SERVER_PORT + 10000) { }
boost::shared_ptr<Pkt6>
processSolicit(boost::shared_ptr<Pkt6>& request) {
@@ -53,30 +53,27 @@ public:
};
TEST_F(Dhcpv6SrvTest, basic) {
- // there's almost no code now. What's there provides echo capability
- // that is just a proof of concept and will be removed soon
- // No need to thoroughly test it
-
// srv has stubbed interface detection. It will read
// interfaces.txt instead. It will pretend to have detected
// fe80::1234 link-local address on eth0 interface. Obviously
// an attempt to bind this socket will fail.
- EXPECT_NO_THROW( {
- Dhcpv6Srv * srv = new Dhcpv6Srv();
-
- delete srv;
- });
+ Dhcpv6Srv* srv = 0;
+ ASSERT_NO_THROW( {
+ // open an unpriviledged port
+ srv = new Dhcpv6Srv(DHCP6_SERVER_PORT + 10000);
+ });
+ delete srv;
}
TEST_F(Dhcpv6SrvTest, Solicit_basic) {
NakedDhcpv6Srv * srv = 0;
- EXPECT_NO_THROW( srv = new NakedDhcpv6Srv(); );
+ ASSERT_NO_THROW( srv = new NakedDhcpv6Srv(); );
// a dummy content for client-id
boost::shared_array<uint8_t> clntDuid(new uint8_t[32]);
- for (int i=0; i<32; i++)
- clntDuid[i] = 100+i;
+ for (int i = 0; i < 32; i++)
+ clntDuid[i] = 100 + i;
boost::shared_ptr<Pkt6> sol =
boost::shared_ptr<Pkt6>(new Pkt6(DHCPV6_SOLICIT,
diff --git a/src/bin/dhcp6/tests/iface_mgr_unittest.cc b/src/bin/dhcp6/tests/iface_mgr_unittest.cc
index f126e6a..3cc2ae8 100644
--- a/src/bin/dhcp6/tests/iface_mgr_unittest.cc
+++ b/src/bin/dhcp6/tests/iface_mgr_unittest.cc
@@ -20,9 +20,10 @@
#include <arpa/inet.h>
#include <gtest/gtest.h>
-#include "io_address.h"
-#include "dhcp/pkt6.h"
-#include "dhcp6/iface_mgr.h"
+#include <asiolink/io_address.h>
+#include <dhcp/pkt6.h>
+#include <dhcp6/iface_mgr.h>
+#include <dhcp/dhcp4.h>
using namespace std;
using namespace isc;
@@ -39,16 +40,7 @@ class NakedIfaceMgr: public IfaceMgr {
// "naked" Interface Manager, exposes internal fields
public:
NakedIfaceMgr() { }
- IfaceLst & getIfacesLst() { return ifaces_; }
- void setSendSock(int sock) { sendsock_ = sock; }
- void setRecvSock(int sock) { recvsock_ = sock; }
-
- int openSocket(const std::string& ifname,
- const isc::asiolink::IOAddress& addr,
- int port) {
- return IfaceMgr::openSocket(ifname, addr, port);
- }
-
+ IfaceCollection & getIfacesLst() { return ifaces_; }
};
// dummy class for now, but this will be expanded when needed
@@ -56,6 +48,13 @@ class IfaceMgrTest : public ::testing::Test {
public:
IfaceMgrTest() {
}
+
+ void createLoInterfacesTxt() {
+ unlink(INTERFACE_FILE);
+ fstream fakeifaces(INTERFACE_FILE, ios::out|ios::trunc);
+ fakeifaces << LOOPBACK << " ::1";
+ fakeifaces.close();
+ }
};
// We need some known interface to work reliably. Loopback interface
@@ -109,6 +108,7 @@ TEST_F(IfaceMgrTest, dhcp6Sniffer) {
while (true) {
pkt = ifacemgr->receive();
+ cout << "// this code is autogenerated. Do NOT edit." << endl;
cout << "// Received " << pkt->data_len_ << " bytes packet:" << endl;
cout << "Pkt6 *capture" << cnt++ << "() {" << endl;
cout << " Pkt6* pkt;" << endl;
@@ -183,10 +183,10 @@ TEST_F(IfaceMgrTest, getIface) {
cout << "There are " << ifacemgr->getIfacesLst().size()
<< " interfaces." << endl;
- for (IfaceMgr::IfaceLst::iterator iface=ifacemgr->getIfacesLst().begin();
+ for (IfaceMgr::IfaceCollection::iterator iface=ifacemgr->getIfacesLst().begin();
iface != ifacemgr->getIfacesLst().end();
++iface) {
- cout << " " << iface->name_ << "/" << iface->ifindex_ << endl;
+ cout << " " << iface->getFullName() << endl;
}
@@ -195,15 +195,15 @@ TEST_F(IfaceMgrTest, getIface) {
// ASSERT_NE(NULL, tmp); is not supported. hmmmm.
ASSERT_TRUE( tmp != NULL );
- EXPECT_STREQ( "en3", tmp->name_.c_str() );
- EXPECT_EQ(5, tmp->ifindex_);
+ EXPECT_EQ( "en3", tmp->getName() );
+ EXPECT_EQ(5, tmp->getIndex());
// check that interface can be retrieved by name
tmp = ifacemgr->getIface("lo1");
ASSERT_TRUE( tmp != NULL );
- EXPECT_STREQ( "lo1", tmp->name_.c_str() );
- EXPECT_EQ(1, tmp->ifindex_);
+ EXPECT_EQ( "lo1", tmp->getName() );
+ EXPECT_EQ(1, tmp->getIndex());
// check that non-existing interfaces are not returned
EXPECT_EQ(static_cast<void*>(NULL), ifacemgr->getIface("wifi0") );
@@ -231,58 +231,51 @@ TEST_F(IfaceMgrTest, detectIfaces) {
IfaceMgr::Iface * eth0 = ifacemgr->getIface("eth0");
// there should be one address
- EXPECT_EQ(1, eth0->addrs_.size());
+ IfaceMgr::AddressCollection addrs = eth0->getAddresses();
+ ASSERT_EQ(1, addrs.size());
- IOAddress * addr = &(*eth0->addrs_.begin());
- ASSERT_TRUE( addr != NULL );
+ IOAddress addr = *addrs.begin();
- EXPECT_STREQ( "fe80::1234", addr->toText().c_str() );
+ EXPECT_STREQ( "fe80::1234", addr.toText().c_str() );
delete ifacemgr;
}
-// TODO: disabled due to other naming on various systems
-// (lo in Linux, lo0 in BSD systems)
-// Fix for this is available on 1186 branch, will reenable
-// this test once 1186 is merged
-TEST_F(IfaceMgrTest, DISABLED_sockets) {
+TEST_F(IfaceMgrTest, sockets6) {
// testing socket operation in a portable way is tricky
// without interface detection implemented
+ createLoInterfacesTxt();
+
NakedIfaceMgr * ifacemgr = new NakedIfaceMgr();
IOAddress loAddr("::1");
+ Pkt6 pkt6(128);
+ pkt6.iface_ = LOOPBACK;
+
// bind multicast socket to port 10547
int socket1 = ifacemgr->openSocket(LOOPBACK, loAddr, 10547);
EXPECT_GT(socket1, 0); // socket > 0
+ EXPECT_EQ(socket1, ifacemgr->getSocket(pkt6));
+
// bind unicast socket to port 10548
int socket2 = ifacemgr->openSocket(LOOPBACK, loAddr, 10548);
EXPECT_GT(socket2, 0);
- // expect success. This address/port is already bound, but
- // we are using SO_REUSEADDR, so we can bind it twice
- int socket3 = ifacemgr->openSocket(LOOPBACK, loAddr, 10547);
-
- // rebinding succeeds on Linux, fails on BSD
- // TODO: add OS-specific defines here (or modify code to
- // behave the same way on all OSes, but that may not be
- // possible
- // EXPECT_GT(socket3, 0); // socket > 0
-
- // we now have 3 sockets open at the same time. Looks good.
+ // removed code for binding socket twice to the same address/port
+ // as it caused problems on some platforms (e.g. Mac OS X)
close(socket1);
close(socket2);
- close(socket3);
delete ifacemgr;
}
// TODO: disabled due to other naming on various systems
// (lo in Linux, lo0 in BSD systems)
-TEST_F(IfaceMgrTest, DISABLED_socketsMcast) {
+TEST_F(IfaceMgrTest, DISABLED_sockets6Mcast) {
// testing socket operation in a portable way is tricky
// without interface detection implemented
@@ -311,27 +304,21 @@ TEST_F(IfaceMgrTest, DISABLED_socketsMcast) {
delete ifacemgr;
}
-// TODO: disabled due to other naming on various systems
-// (lo in Linux, lo0 in BSD systems)
-// Fix for this is available on 1186 branch, will reenable
-// this test once 1186 is merged
-TEST_F(IfaceMgrTest, DISABLED_sendReceive) {
+TEST_F(IfaceMgrTest, sendReceive6) {
+
// testing socket operation in a portable way is tricky
// without interface detection implemented
+ createLoInterfacesTxt();
- fstream fakeifaces(INTERFACE_FILE, ios::out|ios::trunc);
- fakeifaces << LOOPBACK << " ::1";
- fakeifaces.close();
-
- NakedIfaceMgr * ifacemgr = new NakedIfaceMgr();
+ NakedIfaceMgr* ifacemgr = new NakedIfaceMgr();
// let's assume that every supported OS have lo interface
IOAddress loAddr("::1");
- int socket1 = ifacemgr->openSocket(LOOPBACK, loAddr, 10547);
- int socket2 = ifacemgr->openSocket(LOOPBACK, loAddr, 10546);
-
- ifacemgr->setSendSock(socket2);
- ifacemgr->setRecvSock(socket1);
+ int socket1 = 0, socket2 = 0;
+ EXPECT_NO_THROW(
+ socket1 = ifacemgr->openSocket(LOOPBACK, loAddr, 10547);
+ socket2 = ifacemgr->openSocket(LOOPBACK, loAddr, 10546);
+ );
boost::shared_ptr<Pkt6> sendPkt(new Pkt6(128) );
@@ -349,7 +336,7 @@ TEST_F(IfaceMgrTest, DISABLED_sendReceive) {
EXPECT_EQ(true, ifacemgr->send(sendPkt));
- rcvPkt = ifacemgr->receive();
+ rcvPkt = ifacemgr->receive6();
ASSERT_TRUE( rcvPkt ); // received our own packet
@@ -359,7 +346,168 @@ TEST_F(IfaceMgrTest, DISABLED_sendReceive) {
rcvPkt->data_len_) );
EXPECT_EQ(sendPkt->remote_addr_.toText(), rcvPkt->remote_addr_.toText());
- EXPECT_EQ(rcvPkt->remote_port_, 10546);
+
+ // since we opened 2 sockets on the same interface and none of them is multicast,
+ // none is preferred over the other for sending data, so we really should not
+ // assume the one or the other will always be choosen for sending data. Therefore
+ // we should accept both values as source ports.
+ EXPECT_TRUE( (rcvPkt->remote_port_ == 10546) || (rcvPkt->remote_port_ == 10547) );
+
+ delete ifacemgr;
+}
+
+TEST_F(IfaceMgrTest, socket4) {
+
+ createLoInterfacesTxt();
+ NakedIfaceMgr* ifacemgr = new NakedIfaceMgr();
+
+ // Let's assume that every supported OS have lo interface.
+ IOAddress loAddr("127.0.0.1");
+ // Use unprivileged port (it's convenient for running tests as non-root).
+ int socket1 = 0;
+
+ EXPECT_NO_THROW(
+ socket1 = ifacemgr->openSocket(LOOPBACK, loAddr, DHCP4_SERVER_PORT + 10000);
+ );
+
+ EXPECT_GT(socket1, 0);
+
+ Pkt4 pkt(DHCPDISCOVER, 1234);
+ pkt.setIface(LOOPBACK);
+
+ // Expect that we get the socket that we just opened.
+ EXPECT_EQ(socket1, ifacemgr->getSocket(pkt));
+
+ close(socket1);
+
+ delete ifacemgr;
+}
+
+// Test the Iface structure itself
+TEST_F(IfaceMgrTest, iface) {
+ IfaceMgr::Iface* iface = 0;
+ EXPECT_NO_THROW(
+ iface = new IfaceMgr::Iface("eth0",1);
+ );
+
+ EXPECT_EQ("eth0", iface->getName());
+ EXPECT_EQ(1, iface->getIndex());
+ EXPECT_EQ("eth0/1", iface->getFullName());
+
+ // Let's make a copy of this address collection.
+ IfaceMgr::AddressCollection addrs = iface->getAddresses();
+
+ EXPECT_EQ(0, addrs.size());
+
+ IOAddress addr1("192.0.2.6");
+ iface->addAddress(addr1);
+
+ addrs = iface->getAddresses();
+ ASSERT_EQ(1, addrs.size());
+ EXPECT_EQ("192.0.2.6", addrs.at(0).toText());
+
+ // No such address, should return false.
+ EXPECT_FALSE(iface->delAddress(IOAddress("192.0.8.9")));
+
+ // This address is present, delete it!
+ EXPECT_TRUE(iface->delAddress(IOAddress("192.0.2.6")));
+
+ // Not really necessary, previous reference still points to the same
+ // collection. Let's do it anyway, as test code may serve as example
+ // usage code as well.
+ addrs = iface->getAddresses();
+
+ EXPECT_EQ(0, addrs.size());
+
+ EXPECT_NO_THROW(
+ delete iface;
+ );
+}
+
+TEST_F(IfaceMgrTest, socketInfo) {
+
+ // check that socketinfo for IPv4 socket is functional
+ IfaceMgr::SocketInfo sock1(7, IOAddress("192.0.2.56"), DHCP4_SERVER_PORT + 7);
+ EXPECT_EQ(7, sock1.sockfd_);
+ EXPECT_EQ("192.0.2.56", sock1.addr_.toText());
+ EXPECT_EQ(AF_INET, sock1.family_);
+ EXPECT_EQ(DHCP4_SERVER_PORT + 7, sock1.port_);
+
+ // check that socketinfo for IPv6 socket is functional
+ IfaceMgr::SocketInfo sock2(9, IOAddress("2001:db8:1::56"), DHCP4_SERVER_PORT + 9);
+ EXPECT_EQ(9, sock2.sockfd_);
+ EXPECT_EQ("2001:db8:1::56", sock2.addr_.toText());
+ EXPECT_EQ(AF_INET6, sock2.family_);
+ EXPECT_EQ(DHCP4_SERVER_PORT + 9, sock2.port_);
+
+ // now let's test if IfaceMgr handles socket info properly
+ createLoInterfacesTxt();
+ NakedIfaceMgr * ifacemgr = new NakedIfaceMgr();
+ IfaceMgr::Iface* loopback = ifacemgr->getIface(LOOPBACK);
+ ASSERT_TRUE(loopback);
+ loopback->addSocket(sock1);
+ loopback->addSocket(sock2);
+
+ Pkt6 pkt6(100);
+
+ // pkt6 dos not have interface set yet
+ EXPECT_THROW(
+ ifacemgr->getSocket(pkt6),
+ BadValue
+ );
+
+ // try to send over non-existing interface
+ pkt6.iface_ = "nosuchinterface45";
+ EXPECT_THROW(
+ ifacemgr->getSocket(pkt6),
+ BadValue
+ );
+
+ // this will work
+ pkt6.iface_ = LOOPBACK;
+ EXPECT_EQ(9, ifacemgr->getSocket(pkt6));
+
+ bool deleted = false;
+ EXPECT_NO_THROW(
+ deleted = ifacemgr->getIface(LOOPBACK)->delSocket(9);
+ );
+ EXPECT_EQ(true, deleted);
+
+ // it should throw again, there's no usable socket anymore
+ EXPECT_THROW(
+ ifacemgr->getSocket(pkt6),
+ Unexpected
+ );
+
+ // repeat for pkt4
+ Pkt4 pkt4(DHCPDISCOVER, 1);
+
+ // pkt4 does not have interface set yet.
+ EXPECT_THROW(
+ ifacemgr->getSocket(pkt4),
+ BadValue
+ );
+
+ // Try to send over non-existing interface.
+ pkt4.setIface("nosuchinterface45");
+ EXPECT_THROW(
+ ifacemgr->getSocket(pkt4),
+ BadValue
+ );
+
+ // Socket info is set, packet has well defined interface. It should work.
+ pkt4.setIface(LOOPBACK);
+ EXPECT_EQ(7, ifacemgr->getSocket(pkt4));
+
+ EXPECT_NO_THROW(
+ ifacemgr->getIface(LOOPBACK)->delSocket(7);
+ );
+
+ // It should throw again, there's no usable socket anymore.
+ EXPECT_THROW(
+ ifacemgr->getSocket(pkt4),
+ Unexpected
+ );
delete ifacemgr;
}
diff --git a/src/bin/resolver/tests/Makefile.am b/src/bin/resolver/tests/Makefile.am
index 12ddab3..4d407bb 100644
--- a/src/bin/resolver/tests/Makefile.am
+++ b/src/bin/resolver/tests/Makefile.am
@@ -45,9 +45,9 @@ run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
run_unittests_LDADD += $(top_builddir)/src/lib/xfr/libxfr.la
run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
run_unittests_LDADD += $(top_builddir)/src/lib/server_common/libserver_common.la
+run_unittests_LDADD += $(top_builddir)/src/lib/resolve/libresolve.la
run_unittests_LDADD += $(top_builddir)/src/lib/cache/libcache.la
run_unittests_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
-run_unittests_LDADD += $(top_builddir)/src/lib/resolve/libresolve.la
run_unittests_LDADD += $(top_builddir)/src/lib/acl/libacl.la
run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
diff --git a/src/lib/asiodns/io_fetch.cc b/src/lib/asiodns/io_fetch.cc
index 466be3e..8a91982 100644
--- a/src/lib/asiodns/io_fetch.cc
+++ b/src/lib/asiodns/io_fetch.cc
@@ -355,10 +355,6 @@ IOFetch::stop(Result result) {
// variable should be done inside a mutex (and the stopped_ variable
// declared as "volatile").
//
- // The numeric arguments indicate the debug level, with the lower
- // numbers indicating the most important information. The relative
- // values are somewhat arbitrary.
- //
// TODO: Update testing of stopped_ if threads are used.
data_->stopped = true;
switch (result) {
diff --git a/src/lib/cryptolink/Makefile.am b/src/lib/cryptolink/Makefile.am
index 93f3443..fc12fae 100644
--- a/src/lib/cryptolink/Makefile.am
+++ b/src/lib/cryptolink/Makefile.am
@@ -11,4 +11,5 @@ lib_LTLIBRARIES = libcryptolink.la
libcryptolink_la_SOURCES = cryptolink.h cryptolink.cc
libcryptolink_la_SOURCES += crypto_hmac.h crypto_hmac.cc
-libcryptolink_la_LIBADD = ${BOTAN_LDFLAGS} ${BOTAN_RPATH}
+libcryptolink_la_LDFLAGS = ${BOTAN_LDFLAGS}
+libcryptolink_la_LIBADD = ${BOTAN_LIBS} ${BOTAN_RPATH}
diff --git a/src/lib/cryptolink/tests/Makefile.am b/src/lib/cryptolink/tests/Makefile.am
index fbdd13f..6ac6fdf 100644
--- a/src/lib/cryptolink/tests/Makefile.am
+++ b/src/lib/cryptolink/tests/Makefile.am
@@ -16,8 +16,8 @@ TESTS += run_unittests
run_unittests_SOURCES = run_unittests.cc
run_unittests_SOURCES += crypto_unittests.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
-run_unittests_LDFLAGS = ${BOTAN_LDFLAGS} $(AM_LDFLAGS) $(GTEST_LDFLAGS)
-run_unittests_LDADD = $(GTEST_LDADD)
+run_unittests_LDFLAGS = $(BOTAN_LDFLAGS) $(GTEST_LDFLAGS) $(AM_LDFLAGS)
+run_unittests_LDADD = $(GTEST_LDADD) $(BOTAN_LIBS)
run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
run_unittests_LDADD += $(top_builddir)/src/lib/cryptolink/libcryptolink.la
run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
diff --git a/src/lib/dhcp/pkt4.cc b/src/lib/dhcp/pkt4.cc
index 1f68527..bea93fc 100644
--- a/src/lib/dhcp/pkt4.cc
+++ b/src/lib/dhcp/pkt4.cc
@@ -47,7 +47,6 @@ Pkt4::Pkt4(uint8_t msg_type, uint32_t transid)
yiaddr_(DEFAULT_ADDRESS),
siaddr_(DEFAULT_ADDRESS),
giaddr_(DEFAULT_ADDRESS),
- bufferIn_(NULL, 0), // not used, this is TX packet
bufferOut_(DHCPV4_PKT_HDR_LEN),
msg_type_(msg_type)
{
@@ -71,7 +70,6 @@ Pkt4::Pkt4(const uint8_t* data, size_t len)
yiaddr_(DEFAULT_ADDRESS),
siaddr_(DEFAULT_ADDRESS),
giaddr_(DEFAULT_ADDRESS),
- bufferIn_(data, len),
bufferOut_(0), // not used, this is RX packet
msg_type_(DHCPDISCOVER)
{
@@ -80,6 +78,9 @@ Pkt4::Pkt4(const uint8_t* data, size_t len)
<< " received, at least " << DHCPV4_PKT_HDR_LEN
<< "is expected");
}
+
+ data_.resize(len);
+ memcpy(&data_[0], data, len);
}
size_t
@@ -123,31 +124,35 @@ Pkt4::pack() {
}
bool
Pkt4::unpack() {
- if (bufferIn_.getLength()<DHCPV4_PKT_HDR_LEN) {
+
+ // input buffer (used during message reception)
+ isc::util::InputBuffer bufferIn(&data_[0], data_.size());
+
+ if (bufferIn.getLength()<DHCPV4_PKT_HDR_LEN) {
isc_throw(OutOfRange, "Received truncated DHCPv4 packet (len="
- << bufferIn_.getLength() << " received, at least "
+ << bufferIn.getLength() << " received, at least "
<< DHCPV4_PKT_HDR_LEN << "is expected");
}
- op_ = bufferIn_.readUint8();
- htype_ = bufferIn_.readUint8();
- hlen_ = bufferIn_.readUint8();
- hops_ = bufferIn_.readUint8();
- transid_ = bufferIn_.readUint32();
- secs_ = bufferIn_.readUint16();
- flags_ = bufferIn_.readUint16();
- ciaddr_ = IOAddress(bufferIn_.readUint32());
- yiaddr_ = IOAddress(bufferIn_.readUint32());
- siaddr_ = IOAddress(bufferIn_.readUint32());
- giaddr_ = IOAddress(bufferIn_.readUint32());
- bufferIn_.readData(chaddr_, MAX_CHADDR_LEN);
- bufferIn_.readData(sname_, MAX_SNAME_LEN);
- bufferIn_.readData(file_, MAX_FILE_LEN);
-
- size_t opts_len = bufferIn_.getLength() - bufferIn_.getPosition();
+ op_ = bufferIn.readUint8();
+ htype_ = bufferIn.readUint8();
+ hlen_ = bufferIn.readUint8();
+ hops_ = bufferIn.readUint8();
+ transid_ = bufferIn.readUint32();
+ secs_ = bufferIn.readUint16();
+ flags_ = bufferIn.readUint16();
+ ciaddr_ = IOAddress(bufferIn.readUint32());
+ yiaddr_ = IOAddress(bufferIn.readUint32());
+ siaddr_ = IOAddress(bufferIn.readUint32());
+ giaddr_ = IOAddress(bufferIn.readUint32());
+ bufferIn.readData(chaddr_, MAX_CHADDR_LEN);
+ bufferIn.readData(sname_, MAX_SNAME_LEN);
+ bufferIn.readData(file_, MAX_FILE_LEN);
+
+ size_t opts_len = bufferIn.getLength() - bufferIn.getPosition();
vector<uint8_t> optsBuffer;
// fist use of readVector
- bufferIn_.readVector(optsBuffer, opts_len);
+ bufferIn.readVector(optsBuffer, opts_len);
LibDHCP::unpackOptions4(optsBuffer, options_);
return (true);
diff --git a/src/lib/dhcp/pkt4.h b/src/lib/dhcp/pkt4.h
index 8517091..189d95d 100644
--- a/src/lib/dhcp/pkt4.h
+++ b/src/lib/dhcp/pkt4.h
@@ -299,10 +299,21 @@ public:
///
/// @return returns option of requested type (or NULL)
/// if no such option is present
-
boost::shared_ptr<Option>
getOption(uint8_t opt_type);
+
+ /// @brief set interface over which packet should be sent
+ ///
+ /// @param interface defines outbound interface
+ void setIface(const std::string& interface){ iface_ = interface; }
+
+ /// @brief gets interface over which packet was received or
+ /// will be transmitted
+ ///
+ /// @return name of the interface
+ std::string getIface() const { return iface_; }
+
protected:
/// converts DHCP message type to BOOTP op type
@@ -385,14 +396,15 @@ protected:
// end of real DHCPv4 fields
- /// input buffer (used during message reception)
- /// Note that it must be modifiable as hooks can modify incoming buffer),
- /// thus OutputBuffer, not InputBuffer
- isc::util::InputBuffer bufferIn_;
-
/// output buffer (used during message
isc::util::OutputBuffer bufferOut_;
+ // that's the data of input buffer used in RX packet. Note that
+ // InputBuffer does not store the data itself, but just expects that
+ // data will be valid for the whole life of InputBuffer. Therefore we
+ // need to keep the data around.
+ std::vector<uint8_t> data_;
+
/// message type (e.g. 1=DHCPDISCOVER)
/// TODO: this will eventually be replaced with DHCP Message Type
/// option (option 53)
diff --git a/src/lib/dhcp/tests/option_unittest.cc b/src/lib/dhcp/tests/option_unittest.cc
index db3ee3b..66dce8f 100644
--- a/src/lib/dhcp/tests/option_unittest.cc
+++ b/src/lib/dhcp/tests/option_unittest.cc
@@ -402,6 +402,8 @@ TEST_F(OptionTest, v6_addgetdel) {
// let's try to delete - should fail
EXPECT_TRUE(false == parent->delOption(2));
+
+ delete parent;
}
}
diff --git a/src/lib/dhcp/tests/pkt4_unittest.cc b/src/lib/dhcp/tests/pkt4_unittest.cc
index 0f70442..091bfac 100644
--- a/src/lib/dhcp/tests/pkt4_unittest.cc
+++ b/src/lib/dhcp/tests/pkt4_unittest.cc
@@ -561,4 +561,17 @@ TEST(Pkt4Test, unpackOptions) {
EXPECT_EQ(0, memcmp(&x->getData()[0], v4Opts+22, 3)); // data len=3
}
+// This test verifies methods that are used for manipulating meta fields
+// i.e. fields that are not part of DHCPv4 (e.g. interface name).
+TEST(Pkt4Ttest, metaFields) {
+ Pkt4 pkt(DHCPDISCOVER, 1234);
+
+ pkt.setIface("lo0");
+
+ EXPECT_EQ("lo0", pkt.getIface());
+
+ /// TODO: Expand this test once additonal getters/setters are
+ /// implemented.
+}
+
} // end of anonymous namespace
diff --git a/src/lib/dns/tests/Makefile.am b/src/lib/dns/tests/Makefile.am
index fc6c87c..cfd1286 100644
--- a/src/lib/dns/tests/Makefile.am
+++ b/src/lib/dns/tests/Makefile.am
@@ -62,12 +62,12 @@ run_unittests_SOURCES += tsigrecord_unittest.cc
run_unittests_SOURCES += character_string_unittest.cc
run_unittests_SOURCES += run_unittests.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
-# We shouldn't need to include BOTAN_LDFLAGS here, but there
+# We shouldn't need to include BOTAN_LIBS here, but there
# is one test system where the path for GTEST_LDFLAGS contains
# an older version of botan, and somehow that version gets
# linked if we don't
-run_unittests_LDFLAGS = $(AM_LDFLAGS) $(BOTAN_LDFLAGS) $(GTEST_LDFLAGS)
-run_unittests_LDADD = $(GTEST_LDADD)
+run_unittests_LDFLAGS = $(BOTAN_LDFLAGS) $(GTEST_LDFLAGS) $(AM_LDFLAGS)
+run_unittests_LDADD = $(BOTAN_LIBS) $(GTEST_LDADD)
run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
diff --git a/src/lib/log/Makefile.am b/src/lib/log/Makefile.am
index 957d350..286e9fd 100644
--- a/src/lib/log/Makefile.am
+++ b/src/lib/log/Makefile.am
@@ -46,5 +46,4 @@ if USE_CLANGPP
liblog_la_CXXFLAGS += -Wno-error
endif
liblog_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
-liblog_la_LDFLAGS = $(LOG4CPLUS_LDFLAGS)
-liblog_la_LIBADD = $(top_builddir)/src/lib/util/libutil.la
+liblog_la_LIBADD = $(LOG4CPLUS_LIBS) $(top_builddir)/src/lib/util/libutil.la
diff --git a/src/lib/log/tests/Makefile.am b/src/lib/log/tests/Makefile.am
index a5f793c..53e97a1 100644
--- a/src/lib/log/tests/Makefile.am
+++ b/src/lib/log/tests/Makefile.am
@@ -48,16 +48,18 @@ endif
noinst_PROGRAMS = logger_example
logger_example_SOURCES = logger_example.cc
logger_example_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
-logger_example_LDFLAGS = $(AM_LDFLAGS) $(LOG4CPLUS_LDFLAGS)
-logger_example_LDADD = $(top_builddir)/src/lib/log/liblog.la
+logger_example_LDFLAGS = $(AM_LDFLAGS)
+logger_example_LDADD = $(LOG4CPLUS_LIBS)
+logger_example_LDADD += $(top_builddir)/src/lib/log/liblog.la
logger_example_LDADD += $(top_builddir)/src/lib/util/libutil.la
logger_example_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
noinst_PROGRAMS += init_logger_test
init_logger_test_SOURCES = init_logger_test.cc
init_logger_test_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
-init_logger_test_LDFLAGS = $(AM_LDFLAGS) $(LOG4CPLUS_LDFLAGS)
-init_logger_test_LDADD = $(top_builddir)/src/lib/log/liblog.la
+init_logger_test_LDFLAGS = $(AM_LDFLAGS)
+init_logger_test_LDADD = $(LOG4CPLUS_LIBS)
+init_logger_test_LDADD += $(top_builddir)/src/lib/log/liblog.la
init_logger_test_LDADD += $(top_builddir)/src/lib/util/libutil.la
init_logger_test_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
diff --git a/src/lib/nsas/nameserver_entry.cc b/src/lib/nsas/nameserver_entry.cc
index 553c35d..bca8f73 100644
--- a/src/lib/nsas/nameserver_entry.cc
+++ b/src/lib/nsas/nameserver_entry.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2010-2011 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
@@ -223,7 +223,8 @@ class NameserverEntry::ResolverCallback :
* \short We received the address successfully.
*
* This extracts the addresses out from the response and puts them
- * inside the entry. It tries to reuse the address entries from before (if there were any), to keep their RTTs.
+ * inside the entry. It tries to reuse the address entries from before
+ * (if there were any), to keep their RTTs.
*/
virtual void success(MessagePtr response_message) {
time_t now = time(NULL);
@@ -231,10 +232,21 @@ class NameserverEntry::ResolverCallback :
Lock lock(entry_->mutex_);
// TODO: find the correct RRset, not simply the first
- if (!response_message ||
- response_message->getRcode() != isc::dns::Rcode::NOERROR() ||
+ if (!response_message) {
+ LOG_ERROR(nsas_logger, NSAS_NULL_RESPONSE).arg(entry_->getName());
+ failureInternal(lock);
+ return;
+
+ } else if (response_message->getRcode() != isc::dns::Rcode::NOERROR()) {
+ LOG_DEBUG(nsas_logger, NSAS_DBG_RESULTS, NSAS_ERROR_RESPONSE).
+ arg(response_message->getRcode()).arg(entry_->getName());
+ failureInternal(lock);
+ return;
+
+ } else if (
response_message->getRRCount(isc::dns::Message::SECTION_ANSWER) == 0) {
- LOG_ERROR(nsas_logger, NSAS_INVALID_RESPONSE).arg(entry_->getName());
+ LOG_DEBUG(nsas_logger, NSAS_DBG_RESULTS, NSAS_EMPTY_RESPONSE).
+ arg(entry_->getName());
failureInternal(lock);
return;
}
@@ -371,7 +383,7 @@ class NameserverEntry::ResolverCallback :
}
}
- // Handle a failure to optain data. Dispatches callbacks and leaves
+ // Handle a failure to obtain data. Dispatches callbacks and leaves
// lock unlocked
void failureInternal(Lock &lock) {
// Set state of the addresses
diff --git a/src/lib/nsas/nsas_messages.mes b/src/lib/nsas/nsas_messages.mes
index 512fcd5..6c35172 100644
--- a/src/lib/nsas/nsas_messages.mes
+++ b/src/lib/nsas/nsas_messages.mes
@@ -14,6 +14,16 @@
$NAMESPACE isc::nsas
+% NSAS_EMPTY_RESPONSE response to query for %1 returned an empty answer section
+The NSAS (nameserver address store - part of the resolver) made a query
+for information it needed. The query completed successfully but the
+answer section in the response was empty.
+
+% NSAS_ERROR_RESPONSE error response of %1 returned in query for %2
+The NSAS (nameserver address store - part of the resolver) made a query
+for information it needed. The query completed successfully but the
+RCODE in the response was something other than NOERROR.
+
% NSAS_FIND_NS_ADDRESS asking resolver to obtain A and AAAA records for %1
A debug message issued when the NSAS (nameserver address store - part
of the resolver) is making a callback into the resolver to retrieve the
@@ -24,17 +34,6 @@ A debug message issued when the NSAS (nameserver address store - part
of the resolver) has retrieved the given address for the specified
nameserver through an external query.
-% NSAS_INVALID_RESPONSE queried for %1 but got invalid response
-The NSAS (nameserver address store - part of the resolver) made a query
-for a RR for the specified nameserver but received an invalid response.
-Either the success function was called without a DNS message or the
-message was invalid on some way. (In the latter case, the error should
-have been picked up elsewhere in the processing logic, hence the raising
-of the error here.)
-
-This message indicates an internal error in the NSAS. Please raise a
-bug report.
-
% NSAS_LOOKUP_CANCEL lookup for zone %1 has been canceled
A debug message issued when an NSAS (nameserver address store - part of
the resolver) lookup for a zone has been canceled.
@@ -46,6 +45,14 @@ for the specified nameserver. This is not necessarily a problem - the
nameserver may be unreachable, in which case the NSAS will try other
nameservers in the zone.
+% NSAS_NULL_RESPONSE got null message in success callback for query for %1
+The NSAS (nameserver address store - part of the resolver) made a query
+for information it needed. The query completed successfully, but the
+message passed to the callback was null.
+
+This message indicates an internal error in the NSAS. Please raise a
+bug report.
+
% NSAS_SEARCH_ZONE_NS searching NSAS for nameservers for zone %1
A debug message output when a call is made to the NSAS (nameserver
address store - part of the resolver) to obtain the nameservers for
diff --git a/src/lib/python/isc/datasrc/Makefile.am b/src/lib/python/isc/datasrc/Makefile.am
index fb6d151..47f3dbc 100644
--- a/src/lib/python/isc/datasrc/Makefile.am
+++ b/src/lib/python/isc/datasrc/Makefile.am
@@ -24,6 +24,7 @@ datasrc_la_CXXFLAGS = $(AM_CXXFLAGS) $(PYTHON_CXXFLAGS)
datasrc_la_LDFLAGS = $(PYTHON_LDFLAGS)
datasrc_la_LDFLAGS += -module
datasrc_la_LIBADD = $(top_builddir)/src/lib/datasrc/libdatasrc.la
+datasrc_la_LIBADD += $(top_builddir)/src/lib/cc/libcc.la
datasrc_la_LIBADD += $(top_builddir)/src/lib/dns/python/libpydnspp.la
datasrc_la_LIBADD += $(PYTHON_LIB)
diff --git a/src/lib/resolve/recursive_query.cc b/src/lib/resolve/recursive_query.cc
index 0d3fb4c..1855f7a 100644
--- a/src/lib/resolve/recursive_query.cc
+++ b/src/lib/resolve/recursive_query.cc
@@ -28,9 +28,9 @@
#include <dns/opcode.h>
#include <dns/exceptions.h>
#include <dns/rdataclass.h>
-
#include <resolve/resolve.h>
#include <resolve/resolve_log.h>
+#include <resolve/resolve_messages.h>
#include <cache/resolver_cache.h>
#include <nsas/address_request_callback.h>
#include <nsas/nameserver_address.h>
@@ -39,6 +39,7 @@
#include <asiodns/dns_service.h>
#include <asiodns/io_fetch.h>
#include <asiolink/io_service.h>
+#include <resolve/response_classifier.h>
#include <resolve/recursive_query.h>
using namespace isc::dns;
@@ -430,7 +431,7 @@ private:
.arg(questionText(question_));
isc::resolve::copyResponseMessage(incoming, answer_message_);
cache_.update(*answer_message_);
- return true;
+ return (true);
break;
case isc::resolve::ResponseClassifier::CNAME:
@@ -444,7 +445,7 @@ private:
LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_LONG_CHAIN)
.arg(questionText(question_));
makeSERVFAIL();
- return true;
+ return (true);
}
LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_CNAME)
@@ -460,7 +461,7 @@ private:
LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_FOLLOW_CNAME)
.arg(questionText(question_));
doLookup();
- return false;
+ return (false);
break;
case isc::resolve::ResponseClassifier::NXDOMAIN:
@@ -471,7 +472,7 @@ private:
isc::resolve::copyResponseMessage(incoming, answer_message_);
// no negcache yet
//cache_.update(*answer_message_);
- return true;
+ return (true);
break;
case isc::resolve::ResponseClassifier::REFERRAL:
@@ -520,7 +521,7 @@ private:
nsas_callback_out_ = true;
nsas_.lookup(cur_zone_, question_.getClass(),
nsas_callback_, ANY_OK, glue_hints);
- return false;
+ return (false);
} else {
// Referral was received but did not contain an NS RRset.
LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_NO_NS_RRSET)
@@ -528,48 +529,122 @@ private:
// TODO this will result in answering with the delegation. oh well
isc::resolve::copyResponseMessage(incoming, answer_message_);
- return true;
+ return (true);
}
break;
+
case isc::resolve::ResponseClassifier::TRUNCATED:
// Truncated packet. If the protocol we used for the last one is
// UDP, re-query using TCP. Otherwise regard it as an error.
if (protocol_ == IOFetch::UDP) {
- LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_TRUNCATED)
- .arg(questionText(question_));
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS,
+ RESLIB_TRUNCATED).arg(questionText(question_));
send(IOFetch::TCP);
- return false;
+ return (false);
+ }
+
+ // Was a TCP query so we have received a packet over TCP with the
+ // TC bit set: report an error by dropping down to the common
+ // error code.
+
+ default:
+ // Some error in received packet it. Report it and return SERVFAIL
+ // to the caller.
+ if (logger.isDebugEnabled()) {
+ reportResponseClassifierError(category, incoming.getRcode());
}
- // Was a TCP query so we have received a packet over TCP with the TC
- // bit set: drop through to common error processing.
- // TODO: Can we use what we have received instead of discarding it?
-
- case isc::resolve::ResponseClassifier::EMPTY:
- case isc::resolve::ResponseClassifier::EXTRADATA:
- case isc::resolve::ResponseClassifier::INVNAMCLASS:
- case isc::resolve::ResponseClassifier::INVTYPE:
- case isc::resolve::ResponseClassifier::MISMATQUEST:
- case isc::resolve::ResponseClassifier::MULTICLASS:
- case isc::resolve::ResponseClassifier::NOTONEQUEST:
- case isc::resolve::ResponseClassifier::NOTRESPONSE:
- case isc::resolve::ResponseClassifier::NOTSINGLE:
- case isc::resolve::ResponseClassifier::OPCODE:
- case isc::resolve::ResponseClassifier::RCODE:
- LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_RCODE_ERR)
- .arg(questionText(question_));
- // Should we try a different server rather than SERVFAIL?
makeSERVFAIL();
- return true;
- break;
+ return (true);
}
- // Since we do not have a default in the switch above,
- // the compiler should have errored on any missing case
- // statements.
+ // If we get here, there is some serious logic error (or a missing
+ // "return").
assert(false);
- return true;
+ return (true); // To keep the compiler happy
}
-
+
+ /// \brief Report classification-detected error
+ ///
+ /// When the response classifier has detected an error in the response from
+ /// an upstream query, this method is called to log a debug message giving
+ /// information about the problem.
+ ///
+ /// \param category Classification code for the packet
+ /// \param rcode RCODE value in the packet
+ void reportResponseClassifierError(ResponseClassifier::Category category,
+ const Rcode& rcode)
+ {
+ // We could set up a table of response classifications to message
+ // IDs here and index into that table. But given that (a) C++ does
+ // not have C's named initializers, (b) the codes for the
+ // response classifier are in another module and (c) not all messages
+ // have the same number of arguments, the setup of the table would be
+ // almost as long as the code here: it would need to include a number
+ // of assertions to ensure that any change to the the response
+ // classifier codes was detected, and the checking logic would need to
+ // check that the numeric value of the code lay within the defined
+ // limits of the table.
+
+ if (category == ResponseClassifier::RCODE) {
+
+ // Special case as this message takes two arguments.
+ LOG_DEBUG(logger, RESLIB_DBG_RESULTS, RESLIB_RCODE_ERROR).
+ arg(questionText(question_)).arg(rcode);
+
+ } else {
+
+ isc::log::MessageID message_id;
+ switch (category) {
+ case ResponseClassifier::TRUNCATED:
+ message_id = RESLIB_TCP_TRUNCATED;
+ break;
+
+ case ResponseClassifier::EMPTY:
+ message_id = RESLIB_EMPTY_RESPONSE;
+ break;
+
+ case ResponseClassifier::EXTRADATA:
+ message_id = RESLIB_EXTRADATA_RESPONSE;
+ break;
+
+ case ResponseClassifier::INVNAMCLASS:
+ message_id = RESLIB_INVALID_NAMECLASS_RESPONSE;
+ break;
+
+ case ResponseClassifier::INVTYPE:
+ message_id = RESLIB_INVALID_TYPE_RESPONSE;
+ break;
+
+ case ResponseClassifier::MISMATQUEST:
+ message_id = RESLIB_INVALID_QNAME_RESPONSE;
+ break;
+
+ case ResponseClassifier::MULTICLASS:
+ message_id = RESLIB_MULTIPLE_CLASS_RESPONSE;
+ break;
+
+ case ResponseClassifier::NOTONEQUEST:
+ message_id = RESLIB_NOT_ONE_QNAME_RESPONSE;
+ break;
+
+ case ResponseClassifier::NOTRESPONSE:
+ message_id = RESLIB_NOT_RESPONSE;
+ break;
+
+ case ResponseClassifier::NOTSINGLE:
+ message_id = RESLIB_NOTSINGLE_RESPONSE;
+
+ case ResponseClassifier::OPCODE:
+ message_id = RESLIB_OPCODE_RESPONSE;
+
+ default:
+ message_id = RESLIB_ERROR_RESPONSE;
+ }
+ LOG_DEBUG(logger, RESLIB_DBG_RESULTS, message_id).
+ arg(questionText(question_));
+ }
+ }
+
public:
RunningQuery(IOService& io,
const Question& question,
@@ -734,12 +809,7 @@ public:
incoming.fromWire(ibuf);
buffer_->clear();
- if (incoming.getRcode() == Rcode::NOERROR()) {
- done_ = handleRecursiveAnswer(incoming);
- } else {
- isc::resolve::copyResponseMessage(incoming, answer_message_);
- done_ = true;
- }
+ done_ = handleRecursiveAnswer(incoming);
if (done_) {
callCallback(true);
stop();
diff --git a/src/lib/resolve/resolve_messages.mes b/src/lib/resolve/resolve_messages.mes
index f702d9b..b59fd8c 100644
--- a/src/lib/resolve/resolve_messages.mes
+++ b/src/lib/resolve/resolve_messages.mes
@@ -15,22 +15,61 @@
$NAMESPACE isc::resolve
% RESLIB_ANSWER answer received in response to query for <%1>
-A debug message recording that an answer has been received to an upstream
-query for the specified question. Previous debug messages will have indicated
-the server to which the question was sent.
+A debug message reporting that an answer has been received to an upstream
+query for the specified question. Previous debug messages will have
+indicated the server to which the question was sent.
% RESLIB_CNAME CNAME received in response to query for <%1>
-A debug message recording that CNAME response has been received to an upstream
-query for the specified question. Previous debug messages will have indicated
-the server to which the question was sent.
+A debug message recording that CNAME response has been received to an
+upstream query for the specified question. Previous debug messages will
+have indicated the server to which the question was sent.
% RESLIB_DEEPEST did not find <%1> in cache, deepest delegation found is %2
-A debug message, a cache lookup did not find the specified <name, class,
-type> tuple in the cache; instead, the deepest delegation found is indicated.
+A debug message, a cache lookup did not find the specified <name,
+class, type> tuple in the cache; instead, the deepest delegation found
+is indicated.
+
+% RESLIB_EMPTY_RESPONSE empty response received to query for <%1>
+A debug message, the response to the specified query from an upstream
+nameserver did not contain anything in the answer or authority sections,
+although in all other respects it was a valid response. A SERVFAIL will
+be returned to the system making the original query.
+
+% RESLIB_ERROR_RESPONSE unspecified error received in response to query for <%1>
+A debug message, the response to the specified query to an upstream
+nameserver indicated that the response was classified as an erroneous
+response, but that the nature of the error cannot be identified.
+A SERVFAIL will be returned to the system making the original query.
+
+% RESLIB_EXTRADATA_RESPONSE extra data in response to query for <%1>
+A debug message indicating that the response to the specified query
+from an upstream nameserver contained too much data. This can happen if
+an ANY query was sent and the answer section in the response contained
+multiple RRs with different names. A SERVFAIL will be returned to the
+system making the original query.
% RESLIB_FOLLOW_CNAME following CNAME chain to <%1>
-A debug message, a CNAME response was received and another query is being issued
-for the <name, class, type> tuple.
+A debug message, a CNAME response was received and another query is
+being issued for the <name, class, type> tuple.
+
+% RESLIB_INVALID_NAMECLASS_RESPONSE invalid name or class in response to query for <%1>
+A debug message, the response to the specified query from an upstream
+nameserver (as identified by the ID of the response) contained either
+an answer not matching the query name or an answer having a different
+class to that queried for. A SERVFAIL will be returned to the system
+making the original query.
+
+% RESLIB_INVALID_QNAME_RESPONSE invalid name or class in response to query for <%1>
+A debug message, the response to the specified query from an upstream
+nameserver (as identified by the ID of the response) contained a name
+in the question section that did not match that of the query. A SERVFAIL
+will be returned to the system making the original query.
+
+% RESLIB_INVALID_TYPE_RESPONSE invalid name or class in response to query for <%1>
+A debug message, the response to the specified query from an upstream
+nameserver (as identified by the ID of the response) contained an
+invalid type field. A SERVFAIL will be returned to the system making
+the original query.
% RESLIB_LONG_CHAIN CNAME received in response to query for <%1>: CNAME chain length exceeded
A debug message recording that a CNAME response has been received to an upstream
@@ -39,16 +78,47 @@ the server to which the question was sent). However, receipt of this CNAME
has meant that the resolver has exceeded the CNAME chain limit (a CNAME chain
is where on CNAME points to another) and so an error is being returned.
+% RESLIB_MULTIPLE_CLASS_RESPONSE response to query for <%1> contained multiple RRsets with different classes
+A debug message reporting that the response to an upstream query for
+the specified name contained multiple RRsets in the answer and not all
+were of the same class. This is a violation of the standard and so a
+SERVFAIL will be returned.
+
% RESLIB_NO_NS_RRSET no NS RRSet in referral response received to query for <%1>
A debug message, this indicates that a response was received for the specified
query and was categorized as a referral. However, the received message did
not contain any NS RRsets. This may indicate a programming error in the
response classification code.
+% RESLIB_NOT_ONE_QNAME_RESPONSE not one question in response to query for <%1>
+A debug message, the response to the specified query from an upstream
+nameserver (as identified by the ID of the response) did not contain
+one name in the question section as required by the standard. A SERVFAIL
+will be returned to the system making the original query.
+
+% RESLIB_NOT_RESPONSE response to query for <%1> was not a response
+A debug message, the response to the specified query from an upstream
+nameserver (as identified by the ID of the response) did not have the QR
+bit set (thus indicating that the packet was a query, not a response).
+A SERVFAIL will be returned to the system making the original query.
+
+% RESLIB_NOTSINGLE_RESPONSE response to query for <%1> was not a response
+A debug message, the response to the specified query from an upstream
+nameserver was a CNAME that had mutiple RRs in the RRset. This is
+an invalid response according to the standards so a SERVFAIL will be
+returned to the system making the original query.
+
% RESLIB_NSAS_LOOKUP looking up nameserver for zone %1 in the NSAS
A debug message, the RunningQuery object is querying the NSAS for the
nameservers for the specified zone.
+% RESLIB_OPCODE_RESPONSE response to query for <%1> did not have query opcode
+A debug message, the response to the specified query from an upstream
+nameserver was a response that did not have the opcode set to that of
+a query. According to the standards, this is an invalid response to
+the query that was made, so a SERVFAIL will be returned to the system
+making the original query.
+
% RESLIB_NXDOM_NXRR NXDOMAIN/NXRRSET received in response to query for <%1>
A debug message recording that either a NXDOMAIN or an NXRRSET response has
been received to an upstream query for the specified question. Previous debug
@@ -63,7 +133,7 @@ A debug message indicating that a protocol error was received and that
the resolver is repeating the query to the same nameserver. After this
repeated query, there will be the indicated number of retries left.
-% RESLIB_RCODE_ERR RCODE indicates error in response to query for <%1>
+% RESLIB_RCODE_ERROR response to query for <%1> returns RCODE of %2
A debug message, the response to the specified query indicated an error
that is not covered by a specific code path. A SERVFAIL will be returned.
@@ -122,6 +192,11 @@ A debug message indicating that a RunningQuery's success callback has been
called because a nameserver has been found, and that a query is being sent
to the specified nameserver.
+% RESLIB_TCP_TRUNCATED TCP response to query for %1 was truncated
+This is a debug message logged when a response to the specified query to an
+upstream nameserver returned a response with the TC (truncation) bit set. This
+is treated as an error by the code.
+
% RESLIB_TEST_SERVER setting test server to %1(%2)
This is a warning message only generated in unit tests. It indicates
that all upstream queries from the resolver are being routed to the
diff --git a/src/lib/resolve/response_classifier.h b/src/lib/resolve/response_classifier.h
index 3821560..a027bd0 100644
--- a/src/lib/resolve/response_classifier.h
+++ b/src/lib/resolve/response_classifier.h
@@ -151,7 +151,7 @@ private:
size_t size);
};
-#endif // __RESPONSE_CLASSIFIER_H
-
} // namespace resolve
} // namespace isc
+
+#endif // __RESPONSE_CLASSIFIER_H
More information about the bind10-changes
mailing list