BIND 10 trac1237, updated. 4b226d86ea74b839e6066750de85c6247de691a5 [1237] Compilation fix

BIND 10 source code commits bind10-changes at lists.isc.org
Tue Dec 13 13:33:56 UTC 2011


The branch, trac1237 has been updated
       via  4b226d86ea74b839e6066750de85c6247de691a5 (commit)
       via  697a02295a8d2a7579aaa7dd6b599dcdab14bd2c (commit)
       via  d1a22e481da930ca3151ce704305d7995f9d8fcc (commit)
       via  64546f4f97bf4032f7b97f768649ba1024503fc0 (commit)
       via  d8cd199a66645341270081a7f409c557e596099b (commit)
       via  0f4dd0cf9c1ca4cc397954d639692a8946edb284 (commit)
       via  eb2e8615ae2ed35f9d70e632e970c42729853a19 (commit)
      from  0ca7dca47750297e7477223093da8159d7f48f6c (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 4b226d86ea74b839e6066750de85c6247de691a5
Author: Tomek Mrugalski <tomasz at isc.org>
Date:   Tue Dec 13 14:33:12 2011 +0100

    [1237] Compilation fix
    
    Note: this fix is also present on other branches.

commit 697a02295a8d2a7579aaa7dd6b599dcdab14bd2c
Author: Tomek Mrugalski <tomasz at isc.org>
Date:   Tue Dec 13 14:32:21 2011 +0100

    [1237] Test for interface detection on Linux implemented.

commit d1a22e481da930ca3151ce704305d7995f9d8fcc
Merge: 0ca7dca47750297e7477223093da8159d7f48f6c 64546f4f97bf4032f7b97f768649ba1024503fc0
Author: Tomek Mrugalski <tomasz at isc.org>
Date:   Mon Dec 12 20:13:19 2011 +0100

    Merge branch 'trac992' into trac1237

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

Summary of changes:
 ChangeLog                                 |    6 +
 src/bin/dhcp4/Makefile.am                 |    4 -
 src/bin/dhcp4/dhcp4_srv.cc                |   58 ++++---
 src/bin/dhcp4/dhcp4_srv.h                 |   25 ++-
 src/bin/dhcp4/main.cc                     |    6 +-
 src/bin/dhcp4/tests/dhcp4_srv_unittest.cc |  119 ++++++++++++--
 src/bin/dhcp4/tests/dhcp4_unittests.cc    |    2 +-
 src/bin/dhcp6/dhcp6_srv.cc                |    5 +-
 src/bin/dhcp6/dhcp6_srv.h                 |    4 +-
 src/lib/dhcp/iface_mgr.cc                 |   12 +-
 src/lib/dhcp/pkt4.h                       |   73 ++++++++-
 src/lib/dhcp/tests/iface_mgr_unittest.cc  |  266 ++++++++++++++++++++++++++++-
 src/lib/dhcp/tests/pkt4_unittest.cc       |   17 ++-
 13 files changed, 527 insertions(+), 70 deletions(-)

-----------------------------------------------------------------------
diff --git a/ChangeLog b/ChangeLog
index d3da7e4..a2dc66f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+3XX.	[func]		tomek
+	dhcp4: Dummy DHCPv4 component implemented. Currently it does
+	nothing useful, except providing skeleton implementation that can
+	be expanded in the future.
+	(Trac #992, git TBD)
+
 328.	[func]		jelte
 	b10-auth now passes IXFR requests on to b10-xfrout, and no longer
 	responds to them with NOTIMPL.
diff --git a/src/bin/dhcp4/Makefile.am b/src/bin/dhcp4/Makefile.am
index 1018aa2..71a4531 100644
--- a/src/bin/dhcp4/Makefile.am
+++ b/src/bin/dhcp4/Makefile.am
@@ -35,10 +35,6 @@ b10_dhcp4_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
 b10_dhcp4_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
 b10_dhcp4_LDADD += $(top_builddir)/src/lib/log/liblog.la
 
-# TODO: This is ugly hack. iface_mgr should be moved to src/lib/dhcp
-b10_dhcp4_LDADD += $(top_builddir)/src/bin/dhcp6/iface_mgr.o
-
-
 # TODO: config.h.in is wrong because doesn't honor pkgdatadir
 # and can't use @datadir@ because doesn't expand default ${prefix}
 b10_dhcp4dir = $(pkgdatadir)
diff --git a/src/bin/dhcp4/dhcp4_srv.cc b/src/bin/dhcp4/dhcp4_srv.cc
index 85b937f..780f3dc 100644
--- a/src/bin/dhcp4/dhcp4_srv.cc
+++ b/src/bin/dhcp4/dhcp4_srv.cc
@@ -23,18 +23,21 @@ using namespace isc;
 using namespace isc::dhcp;
 using namespace isc::asiolink;
 
-Dhcpv4Srv::Dhcpv4Srv() {
-    cout << "Initialization" << endl;
+Dhcpv4Srv::Dhcpv4Srv(uint16_t port) {
+    cout << "Initialization: opening sockets on port " << port << endl;
 
     // first call to instance() will create IfaceMgr (it's a singleton)
     // it may throw something if things go wrong
     IfaceMgr::instance();
 
     /// @todo: instantiate LeaseMgr here once it is imlpemented.
+    IfaceMgr::instance().printIfaces();
+
+    IfaceMgr::instance().openSockets4(port);
 
     setServerID();
 
-    shutdown = false;
+    shutdown_ = false;
 }
 
 Dhcpv4Srv::~Dhcpv4Srv() {
@@ -43,11 +46,12 @@ Dhcpv4Srv::~Dhcpv4Srv() {
 
 bool
 Dhcpv4Srv::run() {
-    while (!shutdown) {
+    while (!shutdown_) {
         boost::shared_ptr<Pkt4> query; // client's message
         boost::shared_ptr<Pkt4> rsp;   // server's response
 
 #if 0
+        // uncomment this once ticket 1239 is merged.
         query = IfaceMgr::instance().receive4();
 #endif
 
@@ -80,25 +84,26 @@ Dhcpv4Srv::run() {
             cout << "Received " << query->len() << " bytes packet type="
                  << query->getType() << endl;
 
-            /// DEBUG
+            // TODO: print out received packets only if verbose (or debug)
+            // mode is enabled
             cout << query->toText();
 
             if (rsp) {
-#if 0
-                rsp->remote_addr_ = query->remote_addr_;
-                rsp->local_addr_ = query->local_addr_;
-                rsp->remote_port_ = DHCP6_CLIENT_PORT;
-                rsp->local_port_ = DHCP6_SERVER_PORT;
-                rsp->ifindex_ = query->ifindex_;
-                rsp->iface_ = query->iface_;
-#endif
+                rsp->setRemoteAddr(query->getRemoteAddr());
+                rsp->setLocalAddr(query->getLocalAddr());
+                rsp->setRemotePort(DHCP4_CLIENT_PORT);
+                rsp->setLocalPort(DHCP4_SERVER_PORT);
+                rsp->setIface(query->getIface());
+                rsp->setIndex(query->getIndex());
+
                 cout << "Replying with:" << rsp->getType() << endl;
                 cout << rsp->toText();
                 cout << "----" << endl;
                 if (rsp->pack()) {
-                    cout << "#### pack successful." << endl;
+                    cout << "Packet assembled correctly." << endl;
                 }
 #if 0
+                // uncomment this once ticket 1240 is merged.
                 IfaceMgr::instance().send4(rsp);
 #endif
             }
@@ -113,9 +118,11 @@ Dhcpv4Srv::run() {
 
 void
 Dhcpv4Srv::setServerID() {
-    /// TODO implement this for real once interface detection is done.
-    /// Use hardcoded server-id for now
+    /// TODO implement this for real once interface detection (ticket 1237)
+    /// is done. Use hardcoded server-id for now.
+
 #if 0
+    // uncomment this once ticket 1350 is merged.
     IOAddress srvId("127.0.0.1");
     serverid_ = boost::shared_ptr<Option>(
       new Option4AddrLst(Option::V4, DHO_DHCP_SERVER_IDENTIFIER, srvId));
@@ -123,29 +130,28 @@ Dhcpv4Srv::setServerID() {
 }
 
 boost::shared_ptr<Pkt4>
-Dhcpv4Srv::processDiscover(boost::shared_ptr<Pkt4> discover) {
-    /// TODO: Echo mode. Implement this for real
+Dhcpv4Srv::processDiscover(boost::shared_ptr<Pkt4>& discover) {
+    /// TODO: Currently implemented echo mode. Implement this for real
     return (discover);
 }
 
 boost::shared_ptr<Pkt4>
-Dhcpv4Srv::processRequest(boost::shared_ptr<Pkt4> request) {
-    /// TODO: Echo mode. Implement this for real
+Dhcpv4Srv::processRequest(boost::shared_ptr<Pkt4>& request) {
+    /// TODO: Currently implemented echo mode. Implement this for real
     return (request);
 }
 
-void Dhcpv4Srv::processRelease(boost::shared_ptr<Pkt4> release) {
+void Dhcpv4Srv::processRelease(boost::shared_ptr<Pkt4>& release) {
     /// TODO: Implement this.
     cout << "Received RELEASE on " << release->getIface() << " interface." << endl;
 }
- 
-void Dhcpv4Srv::processDecline(boost::shared_ptr<Pkt4> decline) {
+
+void Dhcpv4Srv::processDecline(boost::shared_ptr<Pkt4>& decline) {
     /// TODO: Implement this.
     cout << "Received DECLINE on " << decline->getIface() << " interface." << endl;
 }
 
-boost::shared_ptr<Pkt4> processInform(boost::shared_ptr<Pkt4> inform) {
-    /// TODO: Echo mode. Implement this for real
+boost::shared_ptr<Pkt4> Dhcpv4Srv::processInform(boost::shared_ptr<Pkt4>& inform) {
+    /// TODO: Currently implemented echo mode. Implement this for real
     return (inform);
 }
-
diff --git a/src/bin/dhcp4/dhcp4_srv.h b/src/bin/dhcp4/dhcp4_srv.h
index 69392ba..033ac5a 100644
--- a/src/bin/dhcp4/dhcp4_srv.h
+++ b/src/bin/dhcp4/dhcp4_srv.h
@@ -17,6 +17,7 @@
 
 #include <boost/shared_ptr.hpp>
 #include <boost/noncopyable.hpp>
+#include <dhcp/dhcp4.h>
 #include <dhcp/pkt4.h>
 #include <dhcp/option.h>
 #include <iostream>
@@ -34,14 +35,18 @@ namespace dhcp {
 /// appropriate responses.
 class Dhcpv4Srv : public boost::noncopyable {
 
-public:
+    public:
     /// @brief Default constructor.
     ///
     /// Instantiates necessary services, required to run DHCPv6 server.
     /// In particular, creates IfaceMgr that will be responsible for
     /// network interaction. Will instantiate lease manager, and load
-    /// old or create new DUID.
-    Dhcpv4Srv();
+    /// old or create new DUID. It is possible to specify alternate
+    /// port on which DHCPv4 server will listen on. That is mostly useful
+    /// for testing purposes.
+    ///
+    /// @param port specifies port number to listen on
+    Dhcpv4Srv(uint16_t port = DHCP4_SERVER_PORT);
 
     /// @brief Destructor. Used during DHCPv6 service shutdown.
     ~Dhcpv4Srv();
@@ -67,13 +72,13 @@ protected:
     ///
     /// @return OFFER message or NULL
     boost::shared_ptr<Pkt4>
-    processDiscover(boost::shared_ptr<Pkt4> discover);
+    processDiscover(boost::shared_ptr<Pkt4>& discover);
 
     /// @brief Processes incoming REQUEST and returns REPLY response.
     ///
     /// Processes incoming REQUEST message and verifies that its sender
     /// should be served. In particular, verifies that requested lease
-    /// is valid, not expired, not reserved, not used by other client and 
+    /// is valid, not expired, not reserved, not used by other client and
     /// that requesting client is allowed to use it.
     ///
     /// Returns ACK message, NACK message, or NULL
@@ -81,7 +86,7 @@ protected:
     /// @param request a message received from client
     ///
     /// @return ACK or NACK message
-    boost::shared_ptr<Pkt4> processRequest(boost::shared_ptr<Pkt4> request);
+    boost::shared_ptr<Pkt4> processRequest(boost::shared_ptr<Pkt4>& request);
 
     /// @brief Stub function that will handle incoming RELEASE messages.
     ///
@@ -89,17 +94,17 @@ protected:
     /// this function does not return anything.
     ///
     /// @param release message received from client
-    void processRelease(boost::shared_ptr<Pkt4> release);
+    void processRelease(boost::shared_ptr<Pkt4>& release);
 
     /// @brief Stub function that will handle incoming DHCPDECLINE messages.
     ///
     /// @param decline message received from client
-    void processDecline(boost::shared_ptr<Pkt4> decline);
+    void processDecline(boost::shared_ptr<Pkt4>& decline);
 
     /// @brief Stub function that will handle incoming INFORM messages.
     ///
     /// @param infRequest message received from client
-    boost::shared_ptr<Pkt4> processInform(boost::shared_ptr<Pkt4> inform);
+    boost::shared_ptr<Pkt4> processInform(boost::shared_ptr<Pkt4>& inform);
 
     /// @brief Returns server-intentifier option
     ///
@@ -123,7 +128,7 @@ protected:
 
     /// indicates if shutdown is in progress. Setting it to true will
     /// initiate server shutdown procedure.
-    volatile bool shutdown;
+    volatile bool shutdown_;
 };
 
 }; // namespace isc::dhcp
diff --git a/src/bin/dhcp4/main.cc b/src/bin/dhcp4/main.cc
index d92f369..ee40295 100644
--- a/src/bin/dhcp4/main.cc
+++ b/src/bin/dhcp4/main.cc
@@ -81,8 +81,8 @@ main(int argc, char* argv[]) {
 
     int ret = 0;
 
-    // TODO remainder of auth to dhcp6 code copy. We need to enable this in
-    //      dhcp6 eventually
+    // TODO remainder of auth to dhcp4 code copy. We need to enable this in
+    //      dhcp4 eventually
 #if 0
     Session* cc_session = NULL;
     Session* statistics_session = NULL;
@@ -104,7 +104,7 @@ main(int argc, char* argv[]) {
         srv->run();
 
     } catch (const std::exception& ex) {
-        cerr << "[b10-dhcp6] Server failed: " << ex.what() << endl;
+        cerr << "[b10-dhcp4] Server failed: " << ex.what() << endl;
         ret = 1;
     }
 
diff --git a/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc b/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
index 1d22db8..c20e983 100644
--- a/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
+++ b/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
@@ -30,37 +30,132 @@ using namespace isc::dhcp;
 namespace {
 
 class NakedDhcpv4Srv: public Dhcpv4Srv {
-    // "naked" Interface Manager, exposes internal fields
+    // "naked" DHCPv4 server, exposes internal fields
 public:
     NakedDhcpv4Srv() { }
 
-    boost::shared_ptr<Pkt4>
-    processDiscover(boost::shared_ptr<Pkt4>& discover) {
+    boost::shared_ptr<Pkt4> processDiscover(boost::shared_ptr<Pkt4>& discover) {
         return Dhcpv4Srv::processDiscover(discover);
     }
-    boost::shared_ptr<Pkt4>
-    processRequest(boost::shared_ptr<Pkt4>& request) {
+    boost::shared_ptr<Pkt4> processRequest(boost::shared_ptr<Pkt4>& request) {
         return Dhcpv4Srv::processRequest(request);
     }
+    void processRelease(boost::shared_ptr<Pkt4>& release) {
+        return Dhcpv4Srv::processRelease(release);
+    }
+    void processDecline(boost::shared_ptr<Pkt4>& decline) {
+        Dhcpv4Srv::processDecline(decline);
+    }
+    boost::shared_ptr<Pkt4> processInform(boost::shared_ptr<Pkt4>& inform) {
+        return Dhcpv4Srv::processInform(inform);
+    }
 };
 
 class Dhcpv4SrvTest : public ::testing::Test {
 public:
     Dhcpv4SrvTest() {
     }
+
+    ~Dhcpv4SrvTest() {
+    };
 };
 
 TEST_F(Dhcpv4SrvTest, 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
+    // nothing to test. DHCPv4_srv instance is created
+    // in test fixture. It is destroyed in destructor
+
+    Dhcpv4Srv* srv = 0;
+    ASSERT_NO_THROW({
+        srv = new Dhcpv4Srv();
+    });
+
+    delete srv;
+}
+
+TEST_F(Dhcpv4SrvTest, processDiscover) {
+    NakedDhcpv4Srv* srv = new NakedDhcpv4Srv();
+
+    boost::shared_ptr<Pkt4> pkt(new Pkt4(DHCPDISCOVER, 1234));
+
+    // should not throw
+    EXPECT_NO_THROW(
+        srv->processDiscover(pkt);
+    );
+
+    // should return something
+    EXPECT_TRUE(srv->processDiscover(pkt));
+
+    // TODO: Implement more reasonable tests before starting
+    // work on processSomething() method.
+    delete srv;
+}
+
+TEST_F(Dhcpv4SrvTest, processRequest) {
+    NakedDhcpv4Srv* srv = new NakedDhcpv4Srv();
+
+    boost::shared_ptr<Pkt4> pkt(new Pkt4(DHCPREQUEST, 1234));
+
+    // should not throw
+    EXPECT_NO_THROW(
+        srv->processRequest(pkt);
+    );
+
+    // should return something
+    EXPECT_TRUE(srv->processRequest(pkt));
+
+    // TODO: Implement more reasonable tests before starting
+    // work on processSomething() method.
+    delete srv;
+}
+
+TEST_F(Dhcpv4SrvTest, processRelease) {
+    NakedDhcpv4Srv* srv = new NakedDhcpv4Srv();
+
+    boost::shared_ptr<Pkt4> pkt(new Pkt4(DHCPRELEASE, 1234));
+
+    // should not throw
+    EXPECT_NO_THROW(
+        srv->processRelease(pkt);
+    );
+
+    // TODO: Implement more reasonable tests before starting
+    // work on processSomething() method.
+
+    delete srv;
+}
+
+TEST_F(Dhcpv4SrvTest, processDecline) {
+    NakedDhcpv4Srv* srv = new NakedDhcpv4Srv();
+
+    boost::shared_ptr<Pkt4> pkt(new Pkt4(DHCPDECLINE, 1234));
+
+    // should not throw
+    EXPECT_NO_THROW(
+        srv->processDecline(pkt);
+    );
+
+    // TODO: Implement more reasonable tests before starting
+    // work on processSomething() method.
+    delete srv;
+}
+
+TEST_F(Dhcpv4SrvTest, processInform) {
+    NakedDhcpv4Srv* srv = new NakedDhcpv4Srv();
+
+    boost::shared_ptr<Pkt4> pkt(new Pkt4(DHCPINFORM, 1234));
+
+    // should not throw
+    EXPECT_NO_THROW(
+        srv->processInform(pkt);
+    );
 
-    EXPECT_NO_THROW( {
-        Dhcpv4Srv * srv = new Dhcpv4Srv();
+    // should return something
+    EXPECT_TRUE(srv->processInform(pkt));
 
-        delete srv;
-        });
+    // TODO: Implement more reasonable tests before starting
+    // work on processSomething() method.
 
+    delete srv;
 }
 
 } // end of anonymous namespace
diff --git a/src/bin/dhcp4/tests/dhcp4_unittests.cc b/src/bin/dhcp4/tests/dhcp4_unittests.cc
index ebd889d..ebc72fb 100644
--- a/src/bin/dhcp4/tests/dhcp4_unittests.cc
+++ b/src/bin/dhcp4/tests/dhcp4_unittests.cc
@@ -24,5 +24,5 @@ main(int argc, char* argv[]) {
 
     int result = RUN_ALL_TESTS();
 
-    return result;
+    return (result);
 }
diff --git a/src/bin/dhcp6/dhcp6_srv.cc b/src/bin/dhcp6/dhcp6_srv.cc
index d3d63ac..31c6afb 100644
--- a/src/bin/dhcp6/dhcp6_srv.cc
+++ b/src/bin/dhcp6/dhcp6_srv.cc
@@ -25,7 +25,7 @@ using namespace isc;
 using namespace isc::dhcp;
 using namespace isc::asiolink;
 
-Dhcpv6Srv::Dhcpv6Srv() {
+Dhcpv6Srv::Dhcpv6Srv(uint16_t port) {
     cout << "Initialization" << endl;
 
     // first call to instance() will create IfaceMgr (it's a singleton)
@@ -42,6 +42,9 @@ Dhcpv6Srv::Dhcpv6Srv() {
 	shutdown = true;
     }
 
+    // Now try to open IPv6 sockets on detected interfaces.
+    IfaceMgr::instance().openSockets6(port);
+
     /// @todo: instantiate LeaseMgr here once it is imlpemented.
 
     setServerID();
diff --git a/src/bin/dhcp6/dhcp6_srv.h b/src/bin/dhcp6/dhcp6_srv.h
index 4daef3a..72a4ed2 100644
--- a/src/bin/dhcp6/dhcp6_srv.h
+++ b/src/bin/dhcp6/dhcp6_srv.h
@@ -41,7 +41,9 @@ 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();
diff --git a/src/lib/dhcp/iface_mgr.cc b/src/lib/dhcp/iface_mgr.cc
index 69d7f85..18d2cd6 100644
--- a/src/lib/dhcp/iface_mgr.cc
+++ b/src/lib/dhcp/iface_mgr.cc
@@ -97,9 +97,6 @@ IfaceMgr::IfaceMgr()
 
         detectIfaces();
 
-        if (!openSockets6()) {
-            isc_throw(Unexpected, "Failed to open/bind sockets.");
-        }
     } catch (const std::exception& ex) {
         cout << "IfaceMgr creation failed:" << ex.what() << endl;
 
@@ -209,20 +206,21 @@ IfaceMgr::printIfaces(std::ostream& out /*= std::cout*/) {
 
         out << "Detected interface " << iface->getFullName()
              << ", hwtype=" << iface->hardware_type_ << ", maclen=" << iface->mac_len_
-             << ", mac=" << iface->getPlainMac() << endl;
-        out << "flags=" << hex << iface->flags_ << dec << "("
+             << ", 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 << "  " << iface->addrs_.size() << " addr(s):" << endl;
+        out << "  " << iface->addrs_.size() << " addr(s):";
         for (Addr6Lst::const_iterator addr=iface->addrs_.begin();
              addr != iface->addrs_.end();
              ++addr) {
-            out << "  " << addr->toText() << endl;
+            out << "  " << addr->toText();
         }
+        out << endl;
     }
 }
 
diff --git a/src/lib/dhcp/pkt4.h b/src/lib/dhcp/pkt4.h
index 43a79b3..33554c8 100644
--- a/src/lib/dhcp/pkt4.h
+++ b/src/lib/dhcp/pkt4.h
@@ -311,6 +311,73 @@ public:
     /// @return interface name
     std::string getIface() { return iface_; };
 
+    /// @brief Sets interface name.
+    ///
+    /// Sets interface name over which packet was received or is
+    /// going to be transmitted.
+    ///
+    /// @return interface name
+    void setIface(const std::string& iface ) { iface_ = iface; };
+
+    /// @brief Sets interface index.
+    ///
+    /// @param ifindex specifies interface index.
+    void setIndex(uint32_t ifindex) { ifindex_ = ifindex; };
+
+    /// @brief Returns interface index.
+    ///
+    /// @return interface index
+    uint32_t getIndex() { return (ifindex_); };
+
+    /// @brief Sets remote address.
+    ///
+    /// @params remote specifies remote address
+    void setRemoteAddr(const isc::asiolink::IOAddress& remote) {
+        remote_addr_ = remote;
+    }
+
+    /// @brief Returns remote address
+    ///
+    /// @return remote address
+    const isc::asiolink::IOAddress& getRemoteAddr() {
+        return (remote_addr_);
+    }
+
+    /// @brief Sets local address.
+    ///
+    /// @params local specifies local address
+    void setLocalAddr(const isc::asiolink::IOAddress& local) {
+        local_addr_ = local;
+    }
+
+    /// @brief Returns local address.
+    ///
+    /// @return local address
+    const isc::asiolink::IOAddress& getLocalAddr() {
+        return (local_addr_);
+    }
+
+    /// @brief Sets local port.
+    ///
+    /// @params local specifies local port
+    void setLocalPort(uint16_t local) { local_port_ = local; }
+
+    /// @brief Returns local port.
+    ///
+    /// @return local port
+    uint16_t getLocalPort() { return (local_port_); }
+
+    /// @brief Sets remote port.
+    ///
+    /// @params remote specifies remote port
+    void setRemotePort(uint16_t remote) { remote_port_ = remote; }
+
+    /// @brief Returns remote port.
+    ///
+    /// @return remote port
+    uint16_t getRemotePort() { return (remote_port_); }
+
+
 protected:
 
     /// converts DHCP message type to BOOTP op type
@@ -335,13 +402,13 @@ protected:
     /// Each network interface has assigned unique ifindex. It is functional
     /// equvalent of name, but sometimes more useful, e.g. when using crazy
     /// systems that allow spaces in interface names e.g. MS Windows)
-    int ifindex_;
+    uint32_t ifindex_;
 
     /// local UDP port
-    int local_port_;
+    uint16_t local_port_;
 
     /// remote UDP port
-    int remote_port_;
+    uint16_t remote_port_;
 
     /// @brief message operation code
     ///
diff --git a/src/lib/dhcp/tests/iface_mgr_unittest.cc b/src/lib/dhcp/tests/iface_mgr_unittest.cc
index f713ac5..3f73f51 100644
--- a/src/lib/dhcp/tests/iface_mgr_unittest.cc
+++ b/src/lib/dhcp/tests/iface_mgr_unittest.cc
@@ -211,7 +211,8 @@ TEST_F(IfaceMgrTest, getIface) {
     delete ifacemgr;
 }
 
-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
@@ -240,6 +241,7 @@ TEST_F(IfaceMgrTest, detectIfaces) {
 
     delete ifacemgr;
 }
+#endif
 
 // TODO: disabled due to other naming on various systems
 // (lo in Linux, lo0 in BSD systems)
@@ -364,4 +366,266 @@ TEST_F(IfaceMgrTest, DISABLED_sendReceive) {
     delete ifacemgr;
 }
 
+/// @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::IfaceLst& ifaces) {
+    fstream f(textFile.c_str());
+
+    bool first_line = true;
+    IfaceMgr::IfaceLst::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->addrs_.push_back(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->addrs_.push_back(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::IfaceLst& 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::IfaceLst parsedIfaces;
+
+    EXPECT_NO_THROW(
+        parse_ifconfig(textFile, parsedIfaces);
+    );
+    unlink(textFile.c_str());
+
+    cout << "------Parsed interfaces---" << endl;
+    for (IfaceMgr::IfaceLst::iterator i = parsedIfaces.begin();
+         i != parsedIfaces.end(); ++i) {
+        cout << i->name_ << ": ifindex=" << i->ifindex_ << ", 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:";
+        for (IfaceMgr::Addr6Lst::iterator a= i->addrs_.begin();
+             a != i->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::IfaceLst::iterator detected = detectedIfaces.begin();
+         detected != detectedIfaces.end(); ++detected) {
+        // let's find out if this interface is
+
+        bool found = false;
+        for (IfaceMgr::IfaceLst::iterator i = parsedIfaces.begin();
+             i != parsedIfaces.end(); ++i) {
+            if (detected->name_ != i->name_) {
+                continue;
+            }
+            found = true;
+
+            cout << "Checking interface " << detected->name_ << 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->addrs_.size(), i->addrs_.size());
+
+            // now compare addresses
+            for (IfaceMgr::Addr6Lst::iterator addr = detected->addrs_.begin();
+                 addr != detected->addrs_.end(); ++addr) {
+                bool addr_found = false;
+
+                for (IfaceMgr::Addr6Lst::iterator a = i->addrs_.begin();
+                     a != i->addrs_.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
+
 }
diff --git a/src/lib/dhcp/tests/pkt4_unittest.cc b/src/lib/dhcp/tests/pkt4_unittest.cc
index c89743f..8d1363a 100644
--- a/src/lib/dhcp/tests/pkt4_unittest.cc
+++ b/src/lib/dhcp/tests/pkt4_unittest.cc
@@ -504,7 +504,7 @@ TEST(Pkt4Test, unpackOptions) {
 
     vector<uint8_t> expectedFormat = generateTestPacket2();
 
-    for (int i=0; i < sizeof(v4Opts); i++) {
+    for (int i = 0; i < sizeof(v4Opts); i++) {
         expectedFormat.push_back(v4Opts[i]);
     }
 
@@ -559,4 +559,19 @@ TEST(Pkt4Test, unpackOptions) {
     EXPECT_EQ(0, memcmp(&x->getData()[0], v4Opts+22, 3)); // data len=3
 }
 
+TEST(Pkt4Test, metaFields) {
+
+    Pkt4* pkt = new Pkt4(DHCPOFFER, 1234);
+    pkt->setIface("loooopback");
+    pkt->setIndex(42);
+    pkt->setRemoteAddr(IOAddress("1.2.3.4"));
+    pkt->setLocalAddr(IOAddress("4.3.2.1"));
+
+    EXPECT_EQ("loooopback", pkt->getIface());
+    EXPECT_EQ(42, pkt->getIndex());
+    EXPECT_EQ("1.2.3.4", pkt->getRemoteAddr().toText());
+    EXPECT_EQ("4.3.2.1", pkt->getLocalAddr().toText());
+
+}
+
 } // end of anonymous namespace




More information about the bind10-changes mailing list