BIND 10 master, updated. d82df10e192238851799070520e23f753db6c2ee Merge branch 'trac1237'

BIND 10 source code commits bind10-changes at lists.isc.org
Fri Dec 23 13:00:11 UTC 2011


The branch, master has been updated
       via  d82df10e192238851799070520e23f753db6c2ee (commit)
       via  8a040737426aece7cc92a795f2b712d7c3407513 (commit)
       via  dc2ae81fef04a1d3ec94ea5e75009393266107fa (commit)
       via  f4e31621d8dbfe3742cc62c8d5435147f7f1b96a (commit)
       via  1cd357e3b4d3940d7d0f7b40fc33b03d0bb58eee (commit)
       via  e1252b9d0a5a8f23dad207911923326f05d0db4c (commit)
       via  73601f240d4b3aa72b3498b71235b949f645a326 (commit)
       via  5ec569cd8c5e56ec6e4f4f8df05a44485489c38a (commit)
       via  aaea8925c243f6ead71f368de54ad27cfd19ef6b (commit)
       via  0b13e5293e643ab9098ab32579b5435beeaff9a1 (commit)
       via  6bbffedb003004dbbbc1b05b7c909e5cf03c529c (commit)
       via  4b226d86ea74b839e6066750de85c6247de691a5 (commit)
       via  697a02295a8d2a7579aaa7dd6b599dcdab14bd2c (commit)
       via  d1a22e481da930ca3151ce704305d7995f9d8fcc (commit)
       via  0ca7dca47750297e7477223093da8159d7f48f6c (commit)
      from  b362cbe3a2eaeabbeebaa720bb4aaab04f667044 (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 d82df10e192238851799070520e23f753db6c2ee
Merge: b362cbe3a2eaeabbeebaa720bb4aaab04f667044 8a040737426aece7cc92a795f2b712d7c3407513
Author: Tomek Mrugalski <tomasz at isc.org>
Date:   Fri Dec 23 13:52:12 2011 +0100

    Merge branch 'trac1237'
    
    Conflicts:
    	ChangeLog
    	src/bin/dhcp4/dhcp4_srv.cc
    	src/bin/dhcp6/dhcp6_srv.cc
    	src/lib/dhcp/iface_mgr.cc
    	src/lib/dhcp/iface_mgr.h
    	src/lib/dhcp/tests/iface_mgr_unittest.cc

-----------------------------------------------------------------------

Summary of changes:
 ChangeLog                                          |    6 +
 configure.ac                                       |   31 ++
 src/bin/dhcp6/Makefile.am                          |    4 +-
 src/bin/dhcp6/dhcp6_srv.cc                         |   18 +-
 src/lib/dhcp/Makefile.am                           |    2 +
 src/lib/dhcp/iface_mgr.cc                          |   53 ++-
 src/lib/dhcp/iface_mgr.h                           |   50 +++-
 .../resolver_callback.cc => dhcp/iface_mgr_bsd.cc} |   30 +-
 src/lib/dhcp/iface_mgr_linux.cc                    |  381 ++++++++++++++++++++
 src/lib/dhcp/tests/Makefile.am                     |    3 +
 src/lib/dhcp/tests/iface_mgr_unittest.cc           |  271 ++++++++++++++-
 11 files changed, 802 insertions(+), 47 deletions(-)
 copy src/lib/{resolve/resolver_callback.cc => dhcp/iface_mgr_bsd.cc} (69%)
 create mode 100644 src/lib/dhcp/iface_mgr_linux.cc

-----------------------------------------------------------------------
diff --git a/ChangeLog b/ChangeLog
index a88f9e1..ff15bf7 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+362.	[func]		tomek
+	libdhcp++: Interface detection in Linux implemented. libdhcp++
+	if now able to detect available network interfaces, its link-layer
+	addresses, flags and configured IPv4 and IPv6 addresses.
+	(Trac #1237, git 8a040737426aece7cc92a795f2b712d7c3407513)
+
 361.	[func]		tomek
 	libdhcp++: Transmission and reception of DHCPv4 packets is now
 	implemented. Low-level hacks are not implemented for transmission
diff --git a/configure.ac b/configure.ac
index cfce842..3ab7beb 100644
--- a/configure.ac
+++ b/configure.ac
@@ -369,6 +369,36 @@ AC_HEADER_STDBOOL
 AC_TYPE_SIZE_T
 
 
+# Detect OS type (it may be used to do OS-specific things, e.g.
+# interface detection in DHCP)
+AC_MSG_CHECKING(OS family)
+system=`uname -s`
+case $system in
+    Linux)
+      OS_TYPE="Linux"
+      CPPFLAGS="$CPPFLAGS -DOS_LINUX"
+      ;;
+    Darwin | FreeBSD | NetBSD | OpenBSD)
+      OS_TYPE="BSD"
+      CPPFLAGS="$CPPFLAGS -DOS_BSD"
+      ;;
+    Solaris)
+      OS_TYPE="Solaris"
+      CPPFLAGS="$CPPFLAGS -DOS_SOLARIS"
+      ;;
+    *)
+      OS_TYPE="Unknown"
+      AC_MSG_WARN("Unsupported OS: uname returned $system")
+      ;;
+esac
+AC_MSG_RESULT($OS_TYPE)
+
+AM_CONDITIONAL(OS_LINUX, test $OS_TYPE = Linux)
+AM_COND_IF([OS_LINUX], [AC_DEFINE([OS_LINUX], [1], [Running on Linux?])])
+AM_CONDITIONAL(OS_BSD, test $OS_TYPE = BSD)
+AM_COND_IF([OS_BSD], [AC_DEFINE([OS_BSD], [1], [Running on BSD?])])
+AM_CONDITIONAL(OS_SOLARIS, test $OS_TYPE = Solaris)
+AM_COND_IF([OS_SOLARIS], [AC_DEFINE([OS_SOLARIS], [1], [Running on Solaris?])])
 
 AC_MSG_CHECKING(for sa_len in struct sockaddr)
 AC_TRY_COMPILE([
@@ -1144,6 +1174,7 @@ Flags:
   CXXFLAGS:      $CXXFLAGS
   LDFLAGS:       $LDFLAGS
   B10_CXXFLAGS:  $B10_CXXFLAGS
+  OS Family:     $OS_TYPE
 dnl includes too
   Python:        ${PYTHON_INCLUDES}
                  ${PYTHON_CXXFLAGS}
diff --git a/src/bin/dhcp6/Makefile.am b/src/bin/dhcp6/Makefile.am
index 0e93924..b1b0798 100644
--- a/src/bin/dhcp6/Makefile.am
+++ b/src/bin/dhcp6/Makefile.am
@@ -34,10 +34,10 @@ pkglibexec_PROGRAMS = b10-dhcp6
 
 b10_dhcp6_SOURCES = main.cc dhcp6_srv.cc dhcp6_srv.h
 
-b10_dhcp6_LDADD = $(top_builddir)/src/lib/dhcp/libdhcp++.la
-b10_dhcp6_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+b10_dhcp6_LDADD = $(top_builddir)/src/lib/exceptions/libexceptions.la
 b10_dhcp6_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
 b10_dhcp6_LDADD += $(top_builddir)/src/lib/log/liblog.la
+b10_dhcp6_LDADD += $(top_builddir)/src/lib/dhcp/libdhcp++.la
 
 # TODO: config.h.in is wrong because doesn't honor pkgdatadir
 # and can't use @datadir@ because doesn't expand default ${prefix}
diff --git a/src/bin/dhcp6/dhcp6_srv.cc b/src/bin/dhcp6/dhcp6_srv.cc
index c559d8c..7d1b549 100644
--- a/src/bin/dhcp6/dhcp6_srv.cc
+++ b/src/bin/dhcp6/dhcp6_srv.cc
@@ -27,13 +27,21 @@ using namespace isc::dhcp;
 using namespace isc::asiolink;
 
 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.
-    IfaceMgr::instance();
+    // first call to instance() will create IfaceMgr (it's a singleton)
+    // it may throw something if things go wrong
+    try {
+	IfaceMgr::instance();
+    } catch (const std::exception &e) {
+	cout << "Failed to instantiate InterfaceManager:" << e.what() << ". Aborting." << endl;
+	shutdown = true;
+    }
+
+    if (IfaceMgr::instance().countIfaces() == 0) {
+	cout << "Failed to detect any network interfaces. Aborting." << endl;
+	shutdown = true;
+    }
 
     // Now try to open IPv6 sockets on detected interfaces.
     IfaceMgr::instance().openSockets6(port);
diff --git a/src/lib/dhcp/Makefile.am b/src/lib/dhcp/Makefile.am
index 99ee112..5eada15 100644
--- a/src/lib/dhcp/Makefile.am
+++ b/src/lib/dhcp/Makefile.am
@@ -11,6 +11,8 @@ lib_LTLIBRARIES = libdhcp++.la
 libdhcp___la_SOURCES  =
 libdhcp___la_SOURCES += libdhcp++.cc libdhcp++.h
 libdhcp___la_SOURCES += iface_mgr.cc iface_mgr.h
+libdhcp___la_SOURCES += iface_mgr_linux.cc
+libdhcp___la_SOURCES += iface_mgr_bsd.cc
 libdhcp___la_SOURCES += option.cc option.h
 libdhcp___la_SOURCES += option6_ia.cc option6_ia.h
 libdhcp___la_SOURCES += option6_iaaddr.cc option6_iaaddr.h
diff --git a/src/lib/dhcp/iface_mgr.cc b/src/lib/dhcp/iface_mgr.cc
index bb89813..c086e46 100644
--- a/src/lib/dhcp/iface_mgr.cc
+++ b/src/lib/dhcp/iface_mgr.cc
@@ -12,6 +12,7 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#include <config.h>
 #include <sstream>
 #include <fstream>
 #include <string.h>
@@ -53,7 +54,9 @@ IfaceMgr::instance() {
 }
 
 IfaceMgr::Iface::Iface(const std::string& name, int ifindex)
-    :name_(name), ifindex_(ifindex), mac_len_(0)
+    :name_(name), ifindex_(ifindex), mac_len_(0), flag_loopback_(false),
+     flag_up_(false), flag_running_(false), flag_multicast_(false),
+     flag_broadcast_(false), flags_(0), hardware_type_(0)
 {
     memset(mac_, 0, sizeof(mac_));
 }
@@ -72,7 +75,7 @@ IfaceMgr::Iface::getPlainMac() const {
     tmp << hex;
     for (int i = 0; i < mac_len_; i++) {
         tmp.width(2);
-        tmp <<  int(mac_[i]);
+        tmp <<  static_cast<int>(mac_[i]);
         if (i < mac_len_-1) {
             tmp << ":";
         }
@@ -153,7 +156,7 @@ IfaceMgr::~IfaceMgr() {
 }
 
 void
-IfaceMgr::detectIfaces() {
+IfaceMgr::stubDetectIfaces() {
     string ifaceName, linkLocal;
 
     // TODO do the actual detection. Currently interface detection is faked
@@ -167,8 +170,8 @@ IfaceMgr::detectIfaces() {
         ifstream interfaces("interfaces.txt");
 
         if (!interfaces.good()) {
-            cout << "Failed to read interfaces.txt file." << endl;
-            isc_throw(Unexpected, "Failed to read interfaces.txt");
+            cout << "interfaces.txt file is not available. Stub interface detection skipped." << endl;
+            return;
         }
         interfaces >> ifaceName;
         interfaces >> linkLocal;
@@ -192,6 +195,12 @@ IfaceMgr::detectIfaces() {
     }
 }
 
+#if !defined(OS_LINUX) && !defined(OS_BSD)
+void IfaceMgr::detectIfaces() {
+    stubDetectIfaces();
+}
+#endif
+
 bool IfaceMgr::openSockets4(uint16_t port) {
     int sock;
     int count = 0;
@@ -259,8 +268,8 @@ bool IfaceMgr::openSockets6(uint16_t port) {
                 return (false);
             }
 
-            if ( !joinMcast(sock, iface->getName(),
-                             string(ALL_DHCP_RELAY_AGENTS_AND_SERVERS) ) ) {
+            if ( !joinMulticast(sock, iface->getName(),
+                                string(ALL_DHCP_RELAY_AGENTS_AND_SERVERS) ) ) {
                 close(sock);
                 isc_throw(Unexpected, "Failed to join " << ALL_DHCP_RELAY_AGENTS_AND_SERVERS
                           << " multicast group.");
@@ -289,15 +298,25 @@ IfaceMgr::printIfaces(std::ostream& out /*= std::cout*/) {
          iface!=ifaces_.end();
          ++iface) {
 
+        const AddressCollection& addrs = iface->getAddresses();
+
         out << "Detected interface " << iface->getFullName()
-             << ", mac=" << iface->getPlainMac() << endl;
-        out << "flags=" <<  endl;
-        out << "  " << iface->getAddresses().size() << " addr(s):" << endl;
-        for (AddressCollection::const_iterator addr=iface->getAddresses().begin();
-             addr != iface->getAddresses().end();
-             ++addr) {
-            out << "  " << addr->toText() << endl;
+             << ", hwtype=" << iface->hardware_type_ << ", maclen=" << iface->mac_len_
+             << ", mac=" << iface->getPlainMac();
+        out << ", flags=" << hex << iface->flags_ << dec << "("
+            << (iface->flag_loopback_?"LOOPBACK ":"")
+            << (iface->flag_up_?"UP ":"")
+            << (iface->flag_running_?"RUNNING ":"")
+            << (iface->flag_multicast_?"MULTICAST ":"")
+            << (iface->flag_broadcast_?"BROADCAST ":"")
+            << ")" << endl;
+        out << "  " << addrs.size() << " addr(s):";
+
+        for (AddressCollection::const_iterator addr = addrs.begin();
+             addr != addrs.end(); ++addr) {
+            out << "  " << addr->toText();
         }
+        out << endl;
     }
 }
 
@@ -403,7 +422,7 @@ int IfaceMgr::openSocket6(Iface& iface, const IOAddress& addr, int port) {
            addr.getAddress().to_v6().to_bytes().data(),
            sizeof(addr6.sin6_addr));
 #ifdef HAVE_SA_LEN
-    addr6->sin6_len = sizeof(addr6);
+    addr6.sin6_len = sizeof(addr6);
 #endif
 
     // TODO: use sockcreator once it becomes available
@@ -450,7 +469,7 @@ int IfaceMgr::openSocket6(Iface& iface, const IOAddress& addr, int port) {
         // are link and site-scoped, so there is no sense to join those groups
         // with global addresses.
 
-        if ( !joinMcast( sock, iface.getName(),
+        if ( !joinMulticast( sock, iface.getName(),
                          string(ALL_DHCP_RELAY_AGENTS_AND_SERVERS) ) ) {
             close(sock);
             isc_throw(Unexpected, "Failed to join " << ALL_DHCP_RELAY_AGENTS_AND_SERVERS
@@ -468,7 +487,7 @@ int IfaceMgr::openSocket6(Iface& iface, const IOAddress& addr, int port) {
 }
 
 bool
-IfaceMgr::joinMcast(int sock, const std::string& ifname,
+IfaceMgr::joinMulticast(int sock, const std::string& ifname,
 const std::string & mcast) {
 
     struct ipv6_mreq mreq;
diff --git a/src/lib/dhcp/iface_mgr.h b/src/lib/dhcp/iface_mgr.h
index e937967..2cfd1ee 100644
--- a/src/lib/dhcp/iface_mgr.h
+++ b/src/lib/dhcp/iface_mgr.h
@@ -87,6 +87,14 @@ public:
         /// @return MAC address as a plain text (string)
         std::string getPlainMac() const;
 
+        /// @brief Sets flag_*_ fields based on bitmask value returned by OS
+        ///
+        /// Note: Implementation of this method is OS-dependent as bits have
+        /// different meaning on each OS.
+        ///
+        /// @param flags bitmask value returned by OS in interface detection
+        void setFlags(uint32_t flags);
+
         /// @brief Returns interface index.
         ///
         /// @return interface index
@@ -159,11 +167,35 @@ public:
         /// list of assigned addresses
         AddressCollection addrs_;
 
+    public:
         /// link-layer address
         uint8_t mac_[MAX_MAC_LEN];
 
         /// length of link-layer address (usually 6)
         int mac_len_;
+
+        /// specifies if selected interface is loopback
+        bool flag_loopback_;
+
+        /// specifies if selected interface is up
+        bool flag_up_;
+
+        /// flag specifies if selected interface is running
+        /// (e.g. cable plugged in, wifi associated)
+        bool flag_running_;
+
+        /// flag specifies if selected interface is multicast capable
+        bool flag_multicast_;
+
+        /// flag specifies if selected interface is broadcast capable
+        bool flag_broadcast_;
+
+        /// interface flags (this value is as is returned by OS,
+        /// it may mean different things on different OSes)
+        uint32_t flags_;
+
+        /// hardware type
+        uint16_t hardware_type_;
     };
 
     // TODO performance improvement: we may change this into
@@ -316,6 +348,11 @@ public:
     /// @return true if any sockets were open
     bool openSockets4(uint16_t port = DHCP4_SERVER_PORT);
 
+    /// @brief returns number of detected interfaces
+    ///
+    /// @return number of detected interfaces
+    uint16_t countIfaces() { return ifaces_.size(); }
+
     // don't use private, we need derived classes in tests
 protected:
 
@@ -368,6 +405,15 @@ protected:
     void
     detectIfaces();
 
+    /// @brief Stub implementation of network interface detection.
+    ///
+    /// This implementations reads a single line from interfaces.txt file
+    /// and pretends to detect such interface. First interface name and
+    /// link-local IPv6 address or IPv4 address is read from the
+    /// intefaces.txt file.
+    void
+    stubDetectIfaces();
+
     // TODO: having 2 maps (ifindex->iface and ifname->iface would)
     //      probably be better for performance reasons
 
@@ -413,8 +459,8 @@ private:
     /// @return true if multicast join was successful
     ///
     bool
-    joinMcast(int sock, const std::string& ifname,
-              const std::string& mcast);
+    joinMulticast(int sock, const std::string& ifname,
+                  const std::string& mcast);
 
 };
 
diff --git a/src/lib/dhcp/iface_mgr_bsd.cc b/src/lib/dhcp/iface_mgr_bsd.cc
new file mode 100644
index 0000000..7786b92
--- /dev/null
+++ b/src/lib/dhcp/iface_mgr_bsd.cc
@@ -0,0 +1,38 @@
+// Copyright (C) 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
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+
+#if defined(OS_BSD)
+
+#include <dhcp/iface_mgr.h>
+#include <exceptions/exceptions.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::asiolink;
+using namespace isc::dhcp;
+
+namespace isc {
+
+void
+IfaceMgr::detectIfaces() {
+    // TODO do the actual detection on BSDs. Currently just calling
+    // stub implementation.
+    stubDetectIfaces();
+}
+
+}
+
+#endif
diff --git a/src/lib/dhcp/iface_mgr_linux.cc b/src/lib/dhcp/iface_mgr_linux.cc
new file mode 100644
index 0000000..657c943
--- /dev/null
+++ b/src/lib/dhcp/iface_mgr_linux.cc
@@ -0,0 +1,381 @@
+// Copyright (C) 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
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+
+#include <config.h>
+
+#if defined(OS_LINUX)
+
+#include <dhcp/iface_mgr.h>
+#include <exceptions/exceptions.h>
+
+#include <net/if.h>
+#include <linux/if_link.h>
+#include <linux/rtnetlink.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::asiolink;
+using namespace isc::dhcp;
+
+/// This is a structure that defines context for netlink connection.
+struct rtnl_handle
+{
+    int                fd;
+    struct sockaddr_nl local;
+    struct sockaddr_nl peer;
+    __u32              seq;
+    __u32              dump;
+};
+
+struct nlmsg_list
+{
+    struct nlmsg_list *next;
+    struct nlmsghdr h;
+};
+
+const int sndbuf = 32768;
+const int rcvbuf = 32768;
+
+namespace isc {
+
+
+/// @brief Opens netlink socket and initializes handle structure.
+///
+/// @exception Unexpected Thrown if socket configuration fails.
+///
+/// @param handle Context will be stored in this structure.
+void rtnl_open_socket(struct rtnl_handle& handle) {
+    // equivalent of rtnl_open
+    handle.fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+    if (handle.fd < 0) {
+        isc_throw(Unexpected, "Failed to create NETLINK socket.");
+    }
+
+    if (setsockopt(handle.fd, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf)) < 0) {
+        isc_throw(Unexpected, "Failed to set send buffer in NETLINK socket.");
+    }
+
+    if (setsockopt(handle.fd, SOL_SOCKET, SO_RCVBUF, &sndbuf, sizeof(rcvbuf)) < 0) {
+        isc_throw(Unexpected, "Failed to set receive buffer in NETLINK socket.");
+    }
+
+    memset(&handle.local, 0, sizeof(handle.local));
+    handle.local.nl_family = AF_NETLINK;
+    handle.local.nl_groups = 0;
+
+    if (bind(handle.fd, (struct sockaddr*)&handle.local, sizeof(handle.local)) < 0) {
+        isc_throw(Unexpected, "Failed to bind netlink socket.");
+    }
+
+    socklen_t addr_len = sizeof(handle.local);
+    if (getsockname(handle.fd, (struct sockaddr*)&handle.local, &addr_len) < 0) {
+        isc_throw(Unexpected, "Getsockname for netlink socket failed.");
+    }
+
+    // just 2 sanity checks and we are done
+    if ( (addr_len != sizeof(handle.local)) ||
+         (handle.local.nl_family != AF_NETLINK) ) {
+        isc_throw(Unexpected, "getsockname() returned unexpected data for netlink socket.");
+    }
+}
+
+/// @brief Sends request over NETLINK socket.
+///
+/// @param handle context structure
+/// @param family requested information family
+/// @param type request type (RTM_GETLINK or RTM_GETADDR)
+void rtnl_send_request(struct rtnl_handle& handle, int family, int type) {
+    struct {
+        struct nlmsghdr nlh;
+        struct rtgenmsg g;
+    } req;
+    struct sockaddr_nl nladdr;
+
+    memset(&nladdr, 0, sizeof(nladdr));
+    nladdr.nl_family = AF_NETLINK;
+
+    memset(&req, 0, sizeof(req));
+    req.nlh.nlmsg_len = sizeof(req);
+    req.nlh.nlmsg_type = type;
+    req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
+    req.nlh.nlmsg_pid = 0;
+    req.nlh.nlmsg_seq = handle.dump = ++handle.seq;
+    req.g.rtgen_family = family;
+
+    int status =  sendto(handle.fd, (void*)&req, sizeof(req), 0,
+                         (struct sockaddr*)&nladdr, sizeof(nladdr));
+
+    if (status<0) {
+        isc_throw(Unexpected, "Failed to send " << sizeof(nladdr) << " bytes over netlink socket.");
+    }
+}
+
+/// @brief Appends nlmsg to a list
+///
+/// @param n a message to be added
+/// @param link_info a list
+void rtnl_store_reply(struct nlmsghdr *n, struct nlmsg_list** link_info)
+{
+    struct nlmsg_list *h;
+    struct nlmsg_list **lp;
+
+    h = (nlmsg_list*)malloc(n->nlmsg_len+sizeof(void*));
+    if (h == NULL) {
+        isc_throw(Unexpected, "Failed to allocate " << n->nlmsg_len+sizeof(void*)
+                  << " bytes.");
+    }
+
+    memcpy(&h->h, n, n->nlmsg_len);
+    h->next = NULL;
+
+    for (lp = link_info; *lp; lp = &(*lp)->next) /* NOTHING */;
+    *lp = h;
+}
+
+void parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len)
+{
+    memset(tb, 0, sizeof(struct rtattr *) * (max + 1));
+    while (RTA_OK(rta, len)) {
+        if (rta->rta_type <= max)
+            tb[rta->rta_type] = rta;
+        rta = RTA_NEXT(rta,len);
+    }
+    if (len) {
+        isc_throw(Unexpected, "Failed to parse RTATTR in netlink message.");
+    }
+}
+
+void ipaddrs_get(IfaceMgr::Iface& iface, struct nlmsg_list *addr_info) {
+    uint8_t addr[16];
+    struct rtattr * rta_tb[IFA_MAX+1];
+
+    for ( ;addr_info ;  addr_info = addr_info->next) {
+        struct nlmsghdr *n = &addr_info->h;
+        struct ifaddrmsg *ifa = (ifaddrmsg*)NLMSG_DATA(n);
+
+        // these are not the addresses you are looking for
+        if ( ifa->ifa_index != iface.getIndex()) {
+            continue;
+        }
+
+        if ( ifa->ifa_family == AF_INET6 ) {
+            memset(rta_tb, 0, sizeof(rta_tb));
+            parse_rtattr(rta_tb, IFA_MAX, IFA_RTA(ifa), n->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa)));
+            if (!rta_tb[IFA_LOCAL])
+                rta_tb[IFA_LOCAL]   = rta_tb[IFA_ADDRESS];
+            if (!rta_tb[IFA_ADDRESS])
+                rta_tb[IFA_ADDRESS] = rta_tb[IFA_LOCAL];
+
+            memcpy(addr,(char*)RTA_DATA(rta_tb[IFLA_ADDRESS]),16);
+            IOAddress a = IOAddress::from_bytes(AF_INET6, addr);
+            iface.addAddress(a);
+
+            /// TODO: Read lifetimes of configured addresses
+        }
+
+        if ( ifa->ifa_family == AF_INET ) {
+            memset(rta_tb, 0, sizeof(rta_tb));
+            parse_rtattr(rta_tb, IFA_MAX, IFA_RTA(ifa), n->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa)));
+            if (!rta_tb[IFA_LOCAL])
+                rta_tb[IFA_LOCAL]   = rta_tb[IFA_ADDRESS];
+            if (!rta_tb[IFA_ADDRESS])
+                rta_tb[IFA_ADDRESS] = rta_tb[IFA_LOCAL];
+
+            memcpy(addr,(char*)RTA_DATA(rta_tb[IFLA_ADDRESS]),4);
+            IOAddress a = IOAddress::from_bytes(AF_INET, addr);
+            iface.addAddress(a);
+        }
+    }
+}
+
+
+void rtnl_process_reply(struct rtnl_handle &rth, struct nlmsg_list *&info) {
+
+    struct sockaddr_nl nladdr;
+    struct iovec iov;
+    struct msghdr msg;
+    memset(&msg, 0, sizeof(struct msghdr));
+    msg.msg_name = &nladdr;
+    msg.msg_namelen = sizeof(nladdr);
+    msg.msg_iov = &iov;
+    msg.msg_iovlen = 1;
+
+    char buf[rcvbuf];
+
+    iov.iov_base = buf;
+    while (1) {
+        int status;
+        struct nlmsghdr *h;
+
+        iov.iov_len = sizeof(buf);
+        status = recvmsg(rth.fd, &msg, 0);
+
+        if (status < 0) {
+            if (errno == EINTR)
+                continue;
+            isc_throw(Unexpected, "Overrun while processing reply from netlink socket.");
+        }
+
+        if (status == 0) {
+            isc_throw(Unexpected, "EOF while reading netlink socket.");
+        }
+
+        h = (struct nlmsghdr*)buf;
+        while (NLMSG_OK(h, status)) {
+
+            // why we received this anyway?
+            if (nladdr.nl_pid != 0 ||
+                h->nlmsg_pid != rth.local.nl_pid ||
+                h->nlmsg_seq != rth.dump) {
+                h = NLMSG_NEXT(h, status);
+                continue;
+            }
+
+            if (h->nlmsg_type == NLMSG_DONE) {
+                // end of message
+                return;
+            }
+
+            if (h->nlmsg_type == NLMSG_ERROR) {
+                struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
+                if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) {
+                    // we are really out of luck here. We can't even say what is
+                    // wrong as error message is truncated. D'oh.
+                    isc_throw(Unexpected, "Netlink reply read failed.");
+                } else {
+                    isc_throw(Unexpected, "Netlink reply read error " << -err->error);
+                }
+                // never happens we throw before we reach here
+                return;
+            }
+
+            // store the data
+            rtnl_store_reply(h, &info);
+
+            h = NLMSG_NEXT(h, status);
+        }
+        if (msg.msg_flags & MSG_TRUNC) {
+            isc_throw(Unexpected, "Message received over netlink truncated.");
+        }
+        if (status) {
+            isc_throw(Unexpected, "Trailing garbage of " << status << " bytes received over netlink.");
+        }
+    }
+}
+
+/// @brief releases nlmsg list
+///
+/// @param head first element of the list to be released
+void release_list(struct nlmsg_list *head) {
+    struct nlmsg_list *tmp;
+    while (head) {
+        tmp = head->next;
+        free(head);
+        head = tmp;
+    }
+}
+
+void IfaceMgr::detectIfaces() {
+    cout << "Linux: detecting interfaces." << endl;
+
+    struct nlmsg_list* link_info = NULL; // link info list
+    struct nlmsg_list* addr_info = NULL; // address info list
+    struct nlmsg_list* l = NULL;
+    struct rtnl_handle rth;
+
+    // required to display information about interface
+    struct ifinfomsg* ifi = NULL;
+    struct rtattr* tb[IFLA_MAX+1];
+    int len = 0;
+    memset(tb, 0, sizeof(tb));
+    memset(&rth,0, sizeof(rth));
+
+    // open socket
+    rtnl_open_socket(rth);
+
+    // now we have open functional socket, let's use it!
+    // ask for list of interface...
+    rtnl_send_request(rth, AF_PACKET, RTM_GETLINK);
+
+    // Get reply and store it in link_info list.
+    rtnl_process_reply(rth, link_info);
+
+    // Now ask for list of addresses (AF_UNSPEC = of any family)
+    rtnl_send_request(rth, AF_UNSPEC, RTM_GETADDR);
+
+    // Get reply and store it in addr_info list.
+    rtnl_process_reply(rth, addr_info);
+
+    // Now build list with interface names
+    for (l=link_info; l; l = l->next) {
+        ifi = (ifinfomsg*)NLMSG_DATA(&l->h);
+        len = (&l->h)->nlmsg_len;
+        len -= NLMSG_LENGTH(sizeof(*ifi));
+        parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len);
+
+        Iface* iface = new Iface(string( (char*)RTA_DATA(tb[IFLA_IFNAME])), ifi->ifi_index);
+
+        iface->hardware_type_ = ifi->ifi_type;
+        iface->setFlags(ifi->ifi_flags);
+
+        iface->mac_len_ = 0;
+        memset(iface->mac_, 0, IfaceMgr::MAX_MAC_LEN);
+        // Does inetface has LL_ADDR?
+        if (tb[IFLA_ADDRESS]) {
+            iface->mac_len_ = RTA_PAYLOAD(tb[IFLA_ADDRESS]);
+            if (iface->mac_len_ > IfaceMgr::MAX_MAC_LEN) {
+                iface->mac_len_ = 0;
+                isc_throw(Unexpected, "Interface " << iface->getFullName()
+                          << " was detected to have link address of length " << RTA_PAYLOAD(tb[IFLA_ADDRESS])
+                          << ", but maximum supported length is " << IfaceMgr::MAX_MAC_LEN);
+            }
+            memcpy(iface->mac_, RTA_DATA(tb[IFLA_ADDRESS]), iface->mac_len_);
+        }
+        else {
+            // Tunnels can have no LL_ADDR. RTA_PAYLOAD doesn't check it and try to
+            // dereference it in this manner
+            // #define RTA_PAYLOAD(rta) ((int)((rta)->rta_len) - RTA_LENGTH(0))
+            iface->mac_len_ = 0;
+        }
+
+        ipaddrs_get(*iface, addr_info);
+        ifaces_.push_back(*iface);
+    }
+
+    release_list(link_info);
+    release_list(addr_info);
+
+    printIfaces();
+}
+
+/// @brief sets flag_*_ fields.
+///
+/// This implementation is OS-specific as bits have different meaning
+/// on different OSes.
+///
+/// @param flags flags bitfield read from OS
+void IfaceMgr::Iface::setFlags(uint32_t flags) {
+    flags_ = flags;
+
+    flag_loopback_ = flags & IFF_LOOPBACK;
+    flag_up_ = flags & IFF_UP;
+    flag_running_ = flags & IFF_RUNNING;
+    flag_multicast_ = flags & IFF_MULTICAST;
+    flag_broadcast_ = flags & IFF_BROADCAST;
+}
+
+}
+
+#endif // if defined(LINUX)
diff --git a/src/lib/dhcp/tests/Makefile.am b/src/lib/dhcp/tests/Makefile.am
index ef849f1..e86fa24 100644
--- a/src/lib/dhcp/tests/Makefile.am
+++ b/src/lib/dhcp/tests/Makefile.am
@@ -21,6 +21,8 @@ libdhcp___unittests_SOURCES  = run_unittests.cc
 libdhcp___unittests_SOURCES += ../libdhcp++.h ../libdhcp++.cc
 libdhcp___unittests_SOURCES += libdhcp++_unittest.cc
 libdhcp___unittests_SOURCES += ../iface_mgr.cc ../iface_mgr.h iface_mgr_unittest.cc
+libdhcp___unittests_SOURCES += ../iface_mgr_linux.cc
+libdhcp___unittests_SOURCES += ../iface_mgr_bsd.cc
 libdhcp___unittests_SOURCES += ../option6_iaaddr.h ../option6_iaaddr.cc option6_iaaddr_unittest.cc
 libdhcp___unittests_SOURCES += ../option6_ia.h ../option6_ia.cc option6_ia_unittest.cc
 libdhcp___unittests_SOURCES += ../option6_addrlst.h ../option6_addrlst.cc option6_addrlst_unittest.cc
@@ -33,6 +35,7 @@ libdhcp___unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) $(LOG4CPLUS_INCL
 libdhcp___unittests_LDFLAGS  = $(AM_LDFLAGS)  $(GTEST_LDFLAGS)
 
 libdhcp___unittests_CXXFLAGS = $(AM_CXXFLAGS)
+
 if USE_CLANGPP
 # This is to workaround unused variables tcout and tcerr in
 # log4cplus's streams.h.
diff --git a/src/lib/dhcp/tests/iface_mgr_unittest.cc b/src/lib/dhcp/tests/iface_mgr_unittest.cc
index 9ccc55d..f2d8016 100644
--- a/src/lib/dhcp/tests/iface_mgr_unittest.cc
+++ b/src/lib/dhcp/tests/iface_mgr_unittest.cc
@@ -58,7 +58,7 @@ public:
     }
 
     ~IfaceMgrTest() {
-            unlink(INTERFACE_FILE);
+        unlink(INTERFACE_FILE);
     }
 };
 
@@ -151,6 +151,7 @@ TEST_F(IfaceMgrTest, dhcp6Sniffer) {
 
 TEST_F(IfaceMgrTest, basic) {
     // checks that IfaceManager can be instantiated
+    createLoInterfacesTxt();
 
     createLoInterfacesTxt();
 
@@ -220,7 +221,8 @@ TEST_F(IfaceMgrTest, getIface) {
 
 }
 
-TEST_F(IfaceMgrTest, detectIfaces) {
+#if !defined(OS_LINUX)
+TEST_F(IfaceMgrTest, detectIfaces_stub) {
 
     // test detects that interfaces can be detected
     // there is no code for that now, but interfaces are
@@ -248,8 +250,8 @@ TEST_F(IfaceMgrTest, detectIfaces) {
     EXPECT_STREQ( "fe80::1234", addr.toText().c_str() );
 
     delete ifacemgr;
-    unlink(INTERFACE_FILE);
 }
+#endif
 
 TEST_F(IfaceMgrTest, sockets6) {
     // testing socket operation in a portable way is tricky
@@ -281,7 +283,6 @@ TEST_F(IfaceMgrTest, sockets6) {
     close(socket2);
 
     delete ifacemgr;
-    unlink(INTERFACE_FILE);
 }
 
 // TODO: disabled due to other naming on various systems
@@ -368,7 +369,6 @@ TEST_F(IfaceMgrTest, sendReceive6) {
     EXPECT_TRUE( (rcvPkt->remote_port_ == 10546) || (rcvPkt->remote_port_ == 10547) );
 
     delete ifacemgr;
-    unlink(INTERFACE_FILE);
 }
 
 TEST_F(IfaceMgrTest, sendReceive4) {
@@ -480,7 +480,6 @@ TEST_F(IfaceMgrTest, socket4) {
     close(socket1);
 
     delete ifacemgr;
-    unlink(INTERFACE_FILE);
 }
 
 // Test the Iface structure itself
@@ -610,7 +609,265 @@ TEST_F(IfaceMgrTest, socketInfo) {
     );
 
     delete ifacemgr;
-    unlink(INTERFACE_FILE);
 }
 
+/// @brief parses text representation of MAC address
+///
+/// This function parses text representation of a MAC address and stores
+/// it in binary format. Text format is expecte to be separate with
+/// semicolons, e.g. f4:6d:04:96:58:f2
+///
+/// TODO: IfaceMgr::Iface::mac_ uses uint8_t* type, should be vector<uint8_t>
+///
+/// @param textMac string with MAC address to parse
+/// @param mac pointer to output buffer
+/// @param macLen length of output buffer
+///
+/// @return number of bytes filled in output buffer
+size_t parse_mac(const std::string& textMac, uint8_t* mac, size_t macLen) {
+    stringstream tmp(textMac);
+    tmp.flags(ios::hex);
+    int i = 0;
+    uint8_t octet = 0; // output octet
+    uint8_t byte;  // parsed charater from text representation
+    while (!tmp.eof()) {
+
+        tmp >> byte; // hex value
+        if (byte == ':') {
+            mac[i++] = octet;
+
+            if (i == macLen) {
+                // parsing aborted. We hit output buffer size
+                return(i);
+            }
+            octet = 0;
+            continue;
+        }
+        if (isalpha(byte)) {
+            byte = toupper(byte) - 'A' + 10;
+        } else if (isdigit(byte)) {
+            byte -= '0';
+        } else {
+            // parse error. Let's return what we were able to parse so far
+            break;
+        }
+        octet <<= 4;
+        octet += byte;
+    }
+    mac[i++] = octet;
+
+    return (i);
+}
+
+#if defined(OS_LINUX)
+
+/// @brief Parses 'ifconfig -a' output and creates list of interfaces
+///
+/// This method tries to parse ifconfig output. Note that there are some
+/// oddities in recent versions of ifconfig, like putting extra spaces
+/// after MAC address, inconsistent naming and spacing between inet and inet6.
+/// This is an attempt to find a balance between tight parsing of every piece
+/// of text that ifconfig prints and robustness to handle slight differences
+/// in ifconfig output.
+///
+/// @param textFile name of a text file that holds output of ifconfig -a
+/// @param ifaces empty list of interfaces to be filled
+void parse_ifconfig(const std::string textFile, IfaceMgr::IfaceCollection& ifaces) {
+    fstream f(textFile.c_str());
+
+    bool first_line = true;
+    IfaceMgr::IfaceCollection::iterator iface;
+    while (!f.eof()) {
+        string line;
+        getline(f, line);
+
+        // interfaces are separated by empty line
+        if (line.length() == 0) {
+            first_line = true;
+            continue;
+        }
+
+        // uncomment this for ifconfig output debug
+        // cout << "line[" << line << "]" << endl;
+
+        // this is first line of a new interface
+        if (first_line) {
+            first_line = false;
+
+            size_t offset;
+            offset = line.find_first_of(" ");
+            if (offset == string::npos) {
+                isc_throw(BadValue, "Malformed output of ifconfig");
+            }
+            string name = line.substr(0, offset);
+
+            // sadly, ifconfig does not return ifindex
+            ifaces.push_back(IfaceMgr::Iface(name, 0));
+            iface = ifaces.end();
+            --iface; // points to the last element
+
+            offset = line.find(string("HWaddr"));
+
+            string mac = "";
+            if (offset != string::npos) { // some interfaces don't have MAC (e.g. lo)
+                offset += 7;
+                mac = line.substr(offset, string::npos);
+                mac = mac.substr(0, mac.find_first_of(" "));
+
+                iface->mac_len_ = parse_mac(mac, iface->mac_, IfaceMgr::MAX_MAC_LEN);
+            }
+        }
+
+        if (line.find("inet6") != string::npos) {
+            // IPv6 address
+            string addr = line.substr(line.find("inet6")+12, string::npos);
+            addr = addr.substr(0, addr.find("/"));
+            IOAddress a(addr);
+            iface->addAddress(a);
+        } else if(line.find("inet") != string::npos) {
+            // IPv4 address
+            string addr = line.substr(line.find("inet")+10, string::npos);
+            addr = addr.substr(0, addr.find_first_of(" "));
+            IOAddress a(addr);
+            iface->addAddress(a);
+        } else if(line.find("Metric")) {
+            // flags
+            if (line.find("UP") != string::npos) {
+                iface->flag_up_ = true;
+            }
+            if (line.find("LOOPBACK") != string::npos) {
+                iface->flag_loopback_ = true;
+            }
+            if (line.find("RUNNING") != string::npos) {
+                iface->flag_running_ = true;
+            }
+            if (line.find("BROADCAST") != string::npos) {
+                iface->flag_broadcast_ = true;
+            }
+            if (line.find("MULTICAST") != string::npos) {
+                iface->flag_multicast_ = true;
+            }
+        }
+    }
+}
+
+
+// This test compares implemented detection routines to output of "ifconfig -a" command.
+// It is far from perfect, but it is able to verify that interface names, flags,
+// MAC address, IPv4 and IPv6 addresses are detected properly. Interface list completeness
+// (check that each interface is reported, i.e. no missing or extra interfaces) and
+// address completeness is verified.
+//
+// Things that are not tested: 
+// - ifindex (ifconfig does not print it out)
+// - address scopes and lifetimes (we don't need it, so it is not implemented in IfaceMgr)
+TEST_F(IfaceMgrTest, detectIfaces_linux) {
+
+    NakedIfaceMgr* ifacemgr = new NakedIfaceMgr();
+    IfaceMgr::IfaceCollection& detectedIfaces = ifacemgr->getIfacesLst();
+
+    const std::string textFile = "ifconfig.txt";
+
+    unlink(textFile.c_str());
+    int result = system( ("/sbin/ifconfig -a > " + textFile).c_str());
+
+    ASSERT_EQ(0, result);
+
+    // list of interfaces parsed from ifconfig
+    IfaceMgr::IfaceCollection parsedIfaces;
+
+    EXPECT_NO_THROW(
+        parse_ifconfig(textFile, parsedIfaces);
+    );
+    unlink(textFile.c_str());
+
+    cout << "------Parsed interfaces---" << endl;
+    for (IfaceMgr::IfaceCollection::iterator i = parsedIfaces.begin();
+         i != parsedIfaces.end(); ++i) {
+        cout << i->getName() << ": ifindex=" << i->getIndex() << ", mac=" << i->getPlainMac();
+        cout << ", flags:";
+        if (i->flag_up_) {
+            cout << " UP";
+        }
+        if (i->flag_running_) {
+            cout << " RUNNING";
+        }
+        if (i->flag_multicast_) {
+            cout << " MULTICAST";
+        }
+        if (i->flag_broadcast_) {
+            cout << " BROADCAST";
+        }
+        cout << ", addrs:";
+        const IfaceMgr::AddressCollection& addrs = i->getAddresses();
+        for (IfaceMgr::AddressCollection::const_iterator a= addrs.begin();
+             a != addrs.end(); ++a) {
+            cout << a->toText() << " ";
+        }
+        cout << endl;
+    }
+
+    // Ok, now we have 2 lists of interfaces. Need to compare them
+    ASSERT_EQ(detectedIfaces.size(), parsedIfaces.size());
+
+    // TODO: This could could probably be written simple with find()
+    for (IfaceMgr::IfaceCollection::iterator detected = detectedIfaces.begin();
+         detected != detectedIfaces.end(); ++detected) {
+        // let's find out if this interface is
+
+        bool found = false;
+        for (IfaceMgr::IfaceCollection::iterator i = parsedIfaces.begin();
+             i != parsedIfaces.end(); ++i) {
+            if (detected->getName() != i->getName()) {
+                continue;
+            }
+            found = true;
+
+            cout << "Checking interface " << detected->getName() << endl;
+
+            // start with checking flags
+            EXPECT_EQ(detected->flag_loopback_, i->flag_loopback_);
+            EXPECT_EQ(detected->flag_up_, i->flag_up_);
+            EXPECT_EQ(detected->flag_running_, i->flag_running_);
+            EXPECT_EQ(detected->flag_multicast_, i->flag_multicast_);
+            EXPECT_EQ(detected->flag_broadcast_, i->flag_broadcast_);
+
+            // skip MAC comparison for loopback as netlink returns MAC
+            // 00:00:00:00:00:00 for lo
+            if (!detected->flag_loopback_) {
+                ASSERT_EQ(detected->mac_len_, i->mac_len_);
+                EXPECT_EQ(0, memcmp(detected->mac_, i->mac_, i->mac_len_));
+            }
+
+            EXPECT_EQ(detected->getAddresses().size(), i->getAddresses().size());
+
+            // now compare addresses
+            const IfaceMgr::AddressCollection& addrs = detected->getAddresses();
+            for (IfaceMgr::AddressCollection::const_iterator addr = addrs.begin();
+                 addr != addrs.end(); ++addr) {
+                bool addr_found = false;
+
+                const IfaceMgr::AddressCollection& addrs2 = detected->getAddresses();
+                for (IfaceMgr::AddressCollection::const_iterator a = addrs2.begin();
+                     a != addrs2.end(); ++a) {
+                    if (*addr != *a) {
+                        continue;
+                    }
+                    addr_found = true;
+                }
+                if (!addr_found) {
+                    cout << "ifconfig does not seem to report " << addr->toText()
+                         << " address on " << detected->getFullName() << " interface." << endl;
+                    FAIL();
+                }
+                cout << "Address " << addr->toText() << " on iterface " << detected->getFullName()
+                     << " matched with 'ifconfig -a' output." << endl;
+            }
+        }
+    }
+
+    delete ifacemgr;
+}
+#endif
+
 }




More information about the bind10-changes mailing list