BIND 10 trac1708, updated. f973e64243cb9b616201247f86caffa8965a6928 [1708] Fixed python test for skip msgq connection.

BIND 10 source code commits bind10-changes at lists.isc.org
Mon Jul 23 13:11:08 UTC 2012


The branch, trac1708 has been updated
       via  f973e64243cb9b616201247f86caffa8965a6928 (commit)
       via  b524d5e024eda8dd37d51c0eb87f6b2ff2af9196 (commit)
       via  ed478d2878ef583a1f37da8a588f70f4bf47a9b9 (commit)
      from  f788c425a50d504d7d23d3945b67f2ad25513495 (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 f973e64243cb9b616201247f86caffa8965a6928
Author: Tomek Mrugalski <tomasz at isc.org>
Date:   Mon Jul 23 15:10:40 2012 +0200

    [1708] Fixed python test for skip msgq connection.

commit b524d5e024eda8dd37d51c0eb87f6b2ff2af9196
Author: Tomek Mrugalski <tomasz at isc.org>
Date:   Fri Jun 22 16:14:23 2012 +0200

    [1708] msgq connection in dhcp{4,6} is now optional.

commit ed478d2878ef583a1f37da8a588f70f4bf47a9b9
Author: Tomek Mrugalski <tomasz at isc.org>
Date:   Fri Jun 22 15:36:27 2012 +0200

    [1708] Data reception over many interfaces (IPv6) added

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

Summary of changes:
 ChangeLog                                |    9 ++
 src/bin/dhcp4/main.cc                    |   26 +++++-
 src/bin/dhcp4/tests/dhcp4_test.py        |   13 +++
 src/bin/dhcp6/dhcp6.spec                 |   16 +++-
 src/bin/dhcp6/dhcp6_srv.cc               |    4 +-
 src/bin/dhcp6/main.cc                    |   32 +++++--
 src/bin/dhcp6/tests/dhcp6_test.py        |   18 +++-
 src/lib/dhcp/iface_mgr.cc                |  146 +++++++++++++++++++++---------
 src/lib/dhcp/iface_mgr.h                 |    4 +-
 src/lib/dhcp/tests/iface_mgr_unittest.cc |    2 +-
 10 files changed, 209 insertions(+), 61 deletions(-)

-----------------------------------------------------------------------
diff --git a/ChangeLog b/ChangeLog
index 8fb267f..a8d0f49 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+451.	[func]		tomek
+	b10-dhcp6: DHCPv6 server component is now integrated into
+	BIND10 framework. It can be started from BIND10 (using bindctl)
+	and can receive commands. The only supported command for now
+	is 'Dhcp6 shutdown'.
+	b10-dhcp4: Command line-switch '-s' to disable msgq was added.
+	b10-dhcp6: Command line-switch '-s' to disable msgq was added.
+	(Trac #1708, git tbd)
+
 450.	[func]*		tomek
 	b10-dhcp4: DHCPv4 server component is now integrated into
 	BIND10 framework. It can be started from BIND10 (using bindctl)
diff --git a/src/bin/dhcp4/main.cc b/src/bin/dhcp4/main.cc
index ab3ae1b..472f46f 100644
--- a/src/bin/dhcp4/main.cc
+++ b/src/bin/dhcp4/main.cc
@@ -39,6 +39,7 @@ usage() {
     cerr << "Usage:  b10-dhcp4 [-v]"
          << endl;
     cerr << "\t-v: verbose output" << endl;
+    cerr << "\t-s: stand-alone mode (don't connect to BIND10)" << endl;
     cerr << "\t-p number: specify non-standard port number 1-65535 "
          << "(useful for testing only)" << endl;
     exit(EXIT_FAILURE);
@@ -51,13 +52,17 @@ main(int argc, char* argv[]) {
     bool verbose_mode = false; // should server be verbose?
     int port_number = DHCP4_SERVER_PORT; // The default. any other values are
                                          // useful for testing only.
+    bool stand_alone = false; // should be connect to BIND10 msgq?
 
-    while ((ch = getopt(argc, argv, "vp:")) != -1) {
+    while ((ch = getopt(argc, argv, "vsp:")) != -1) {
         switch (ch) {
         case 'v':
             verbose_mode = true;
             isc::log::denabled = true;
             break;
+        case 's':
+            stand_alone = true;
+            break;
         case 'p':
             port_number = strtol(optarg, NULL, 10);
             if (port_number == 0) {
@@ -77,8 +82,9 @@ main(int argc, char* argv[]) {
                          (verbose_mode ? isc::log::DEBUG : isc::log::INFO),
                          isc::log::MAX_DEBUG_LEVEL, NULL);
 
-    cout << "b10-dhcp4: My pid=" << getpid() << ", binding to port " 
-         << port_number << ", verbose " << (verbose_mode?"yes":"no") << endl;
+    cout << "b10-dhcp4: My pid=" << getpid() << ", binding to port "
+         << port_number << ", verbose " << (verbose_mode?"yes":"no")
+         << ", stand-alone=" << (stand_alone?"yes":"no") << endl;
 
     if (argc - optind > 0) {
         usage();
@@ -92,6 +98,20 @@ main(int argc, char* argv[]) {
 
         /// @todo: pass verbose to the actul server once logging is implemented
         ControlledDhcpv4Srv* server = new ControlledDhcpv4Srv(port_number);
+
+        if (!stand_alone) {
+            try {
+                server->establishSession();
+            } catch (const std::exception& ex) {
+                cerr << "Failed to establish BIND10 session. "
+                    "Running in stand-alone mode:" << ex.what() << endl;
+                // Let's continue. It is useful to have the ability to run
+                // DHCP server in stand-alone mode, e.g. for testing
+            }
+        } else {
+            cout << "Skipping connection to the BIND10 msgq." << endl;
+        }
+
         server->run();
         delete server;
         server = NULL;
diff --git a/src/bin/dhcp4/tests/dhcp4_test.py b/src/bin/dhcp4/tests/dhcp4_test.py
index 18d23ff..065c80c 100644
--- a/src/bin/dhcp4/tests/dhcp4_test.py
+++ b/src/bin/dhcp4/tests/dhcp4_test.py
@@ -166,5 +166,18 @@ class TestDhcpv4Daemon(unittest.TestCase):
         # Check that there is an error message about invalid port number printed on stderr
         self.assertEqual( str(output).count("opening sockets on port 10057"), 1)
 
+    def test_skip_msgq(self):
+        print("Check that connection to BIND10 msgq can be disabled.")
+
+        (returncode, output, error) = self.runDhcp4(['../b10-dhcp4', '-s', '-p', '10057'])
+
+        # When invalid port number is specified, return code must not be success
+        # TODO: Temporarily commented out as socket binding on systems that do not have
+        #       interface detection implemented currently fails.
+        # self.assertTrue(returncode == 0)
+
+        # Check that there is an error message about invalid port number printed on stderr
+        self.assertEqual( str(output).count("Skipping connection to the BIND10 msgq."), 1)
+
 if __name__ == '__main__':
     unittest.main()
diff --git a/src/bin/dhcp6/dhcp6.spec b/src/bin/dhcp6/dhcp6.spec
index 05c3529..2a82a2d 100644
--- a/src/bin/dhcp6/dhcp6.spec
+++ b/src/bin/dhcp6/dhcp6.spec
@@ -1,6 +1,6 @@
 {
   "module_spec": {
-    "module_name": "dhcp6",
+    "module_name": "Dhcp6",
     "module_description": "DHCPv6 server daemon",
     "config_data": [
       { "item_name": "interface",
@@ -9,6 +9,18 @@
         "item_default": "eth0"
       }
     ],
-    "commands": []
+    "commands": [
+        {
+            "command_name": "shutdown",
+            "command_description": "Shuts down DHCPv6 server.",
+            "command_args": [
+                {
+                    "item_name": "pid",
+                    "item_type": "integer",
+                    "item_optional": true
+                }
+            ]
+        }
+    ]
   }
 }
diff --git a/src/bin/dhcp6/dhcp6_srv.cc b/src/bin/dhcp6/dhcp6_srv.cc
index 55a925c..d28d9b7 100644
--- a/src/bin/dhcp6/dhcp6_srv.cc
+++ b/src/bin/dhcp6/dhcp6_srv.cc
@@ -79,9 +79,11 @@ void Dhcpv6Srv::shutdown() {
 
 bool Dhcpv6Srv::run() {
     while (!shutdown_) {
+        /// @todo: calculate actual timeout once we have lease database
+        int timeout = 1000;
 
         // client's message and server's response
-        Pkt6Ptr query = IfaceMgr::instance().receive6();
+        Pkt6Ptr query = IfaceMgr::instance().receive6(timeout);
         Pkt6Ptr rsp;
 
         if (query) {
diff --git a/src/bin/dhcp6/main.cc b/src/bin/dhcp6/main.cc
index 1cd5291..96e2dd1 100644
--- a/src/bin/dhcp6/main.cc
+++ b/src/bin/dhcp6/main.cc
@@ -39,6 +39,7 @@ usage() {
     cerr << "Usage: b10-dhcp6 [-v]"
          << endl;
     cerr << "\t-v: verbose output" << endl;
+    cerr << "\t-s: stand-alone mode (don't connect to BIND10)" << endl;
     cerr << "\t-p number: specify non-standard port number 1-65535 "
          << "(useful for testing only)" << endl;
     exit(EXIT_FAILURE);
@@ -51,13 +52,17 @@ main(int argc, char* argv[]) {
     int port_number = DHCP6_SERVER_PORT; // The default. Any other values are
                                          // useful for testing only.
     bool verbose_mode = false; // Should server be verbose?
+    bool stand_alone = false; // should be connect to BIND10 msgq?
 
-    while ((ch = getopt(argc, argv, "vp:")) != -1) {
+    while ((ch = getopt(argc, argv, "vsp:")) != -1) {
         switch (ch) {
         case 'v':
             verbose_mode = true;
             isc::log::denabled = true;
             break;
+        case 's':
+            stand_alone = true;
+            break;
         case 'p':
             port_number = strtol(optarg, NULL, 10);
             if (port_number == 0) {
@@ -78,7 +83,8 @@ main(int argc, char* argv[]) {
                          isc::log::MAX_DEBUG_LEVEL, NULL);
 
     cout << "b10-dhcp6: My pid=" << getpid() << ", binding to port "
-         << port_number << ", verbose " << (verbose_mode?"yes":"no") << endl;
+         << port_number << ", verbose " << (verbose_mode?"yes":"no")
+         << ", stand-alone=" << (stand_alone?"yes":"no") << endl;
 
     if (argc - optind > 0) {
         usage();
@@ -90,18 +96,26 @@ main(int argc, char* argv[]) {
 
         cout << "b10-dhcp6: Initiating DHCPv6 server operation." << endl;
 
+        /// @todo: pass verbose to the actual server once logging is implemented
         ControlledDhcpv6Srv* server = new ControlledDhcpv6Srv(port_number);
+
+        if (!stand_alone) {
+            try {
+                server->establishSession();
+            } catch (const std::exception& ex) {
+                cerr << "Failed to establish BIND10 session. "
+                    "Running in stand-alone mode:" << ex.what() << endl;
+                // Let's continue. It is useful to have the ability to run 
+                // DHCP server in stand-alone mode, e.g. for testing
+            }
+        } else {
+            cout << "Skipping connection to the BIND10 msgq." << endl;
+        }
+
         server->run();
         delete server;
         server = NULL;
 
-        cout << "[b10-dhcp6] Initiating DHCPv6 operation." << endl;
-
-        /// @todo: pass verbose to the actual server once logging is implemented
-        Dhcpv6Srv* srv = new Dhcpv6Srv(port_number);
-
-        srv->run();
-
     } catch (const std::exception& ex) {
         cerr << "[b10-dhcp6] Server failed: " << ex.what() << endl;
         ret = EXIT_FAILURE;
diff --git a/src/bin/dhcp6/tests/dhcp6_test.py b/src/bin/dhcp6/tests/dhcp6_test.py
index 230588f..123b105 100644
--- a/src/bin/dhcp6/tests/dhcp6_test.py
+++ b/src/bin/dhcp6/tests/dhcp6_test.py
@@ -158,7 +158,7 @@ class TestDhcpv6Daemon(unittest.TestCase):
     def test_portnumber_nonroot(self):
         print("Check that specifying unprivileged port number will work.")
 
-        (returncode, output, error) = self.runCommand(['../b10-dhcp6', '-p', '10057'])
+        (returncode, output, error) = self.runCommand(['../b10-dhcp6', '-p', '10547'])
 
         # When invalid port number is specified, return code must not be success
         # TODO: Temporarily commented out as socket binding on systems that do not have
@@ -166,7 +166,21 @@ class TestDhcpv6Daemon(unittest.TestCase):
         # self.assertTrue(returncode == 0)
 
         # Check that there is a message on stdout about opening proper port
-        self.assertEqual( str(output).count("opening sockets on port 10057"), 1)
+        self.assertEqual( str(output).count("opening sockets on port 10547"), 1)
+
+    def test_skip_msgq(self):
+        print("Check that connection to BIND10 msgq can be disabled.")
+
+        (returncode, output, error) = self.runCommand(['../b10-dhcp6', '-s', '-p', '10547'])
+
+        # When invalid port number is specified, return code must not be success
+        # TODO: Temporarily commented out as socket binding on systems that do not have
+        #       interface detection implemented currently fails.
+        # self.assertTrue(returncode == 0)
+
+        # Check that there is an error message about invalid port number printed on stderr
+        self.assertEqual( str(output).count("Skipping connection to the BIND10 msgq."), 1)
+
 
 if __name__ == '__main__':
     unittest.main()
diff --git a/src/lib/dhcp/iface_mgr.cc b/src/lib/dhcp/iface_mgr.cc
index 508413d..3167ae0 100644
--- a/src/lib/dhcp/iface_mgr.cc
+++ b/src/lib/dhcp/iface_mgr.cc
@@ -693,13 +693,12 @@ IfaceMgr::receive4(uint32_t timeout) {
 
     const SocketInfo* candidate = 0;
     IfaceCollection::const_iterator iface;
-
     fd_set sockets;
-    FD_ZERO(&sockets);
     int maxfd = 0;
-
     stringstream names;
 
+    FD_ZERO(&sockets);
+
     /// @todo: marginal performance optimization. We could create the set once
     /// and then use its copy for select(). Please note that select() modifies
     /// provided set to indicated which sockets have something to read.
@@ -858,9 +857,108 @@ IfaceMgr::receive4(uint32_t timeout) {
     return (pkt);
 }
 
-Pkt6Ptr IfaceMgr::receive6() {
-    uint8_t buf[RCVBUFSIZE];
+Pkt6Ptr IfaceMgr::receive6(uint32_t timeout) {
+
+    const SocketInfo* candidate = 0;
+    fd_set sockets;
+    int maxfd = 0;
+    stringstream names;
+
+    FD_ZERO(&sockets);
+
+    /// @todo: marginal performance optimization. We could create the set once
+    /// and then use its copy for select(). Please note that select() modifies
+    /// provided set to indicated which sockets have something to read.
+    IfaceCollection::const_iterator iface;
+    for (iface = ifaces_.begin(); iface != ifaces_.end(); ++iface) {
+
+        for (SocketCollection::const_iterator s = iface->sockets_.begin();
+             s != iface->sockets_.end(); ++s) {
+
+            // Only deal with IPv4 addresses.
+            if (s->addr_.getFamily() == AF_INET6) {
+                names << s->sockfd_ << "(" << iface->getName() << ") ";
+
+                // Add this socket to listening set
+                FD_SET(s->sockfd_, &sockets);
+                if (maxfd < s->sockfd_) {
+                    maxfd = s->sockfd_;
+                }
+            }
+        }
+    }
+
+    // if there is session socket registered...
+    if (session_socket_ != INVALID_SOCKET) {
+        // at it to the set as well
+        FD_SET(session_socket_, &sockets);
+        if (maxfd < session_socket_)
+            maxfd = session_socket_;
+        names << session_socket_ << "(session)";
+    }
+
+    cout << "Trying to receive data on sockets:" << names.str()
+         << ".Timeout is " << timeout << " seconds." << endl;
+
+    /// @todo: implement sub-second precision one day
+    struct timeval select_timeout;
+    select_timeout.tv_sec = timeout;
+    select_timeout.tv_usec = 0;
+
+    int result = select(maxfd + 1, &sockets, NULL, NULL, &select_timeout);
+
+    if (result == 0) {
+        // nothing received and timeout has been reached
+        return (Pkt6Ptr()); // NULL
+    } else if (result < 0) {
+        cout << "Socket read error: " << strerror(errno) << endl;
+
+        /// @todo: perhaps throw here?
+        return (Pkt6Ptr()); // NULL
+    }
+
+    // Let's find out which socket has the data
+    if ((session_socket_ != INVALID_SOCKET) && (FD_ISSET(session_socket_, &sockets))) {
+        // something received over session socket
+        cout << "BIND10 command or config available over session socket." << endl;
+
+        if (session_callback_) {
+            // in theory we could call io_service.run_one() here, instead of
+            // implementing callback mechanism, but that would introduce
+            // asiolink dependency to libdhcp++ and that is something we want
+            // to avoid (see CPE market and out long term plans for minimalistic
+            // implementations.
+            session_callback_();
+        }
+
+        return (Pkt6Ptr()); // NULL
+    }
+
+    // Let's find out which interface/socket has the data
+    for (iface = ifaces_.begin(); iface != ifaces_.end(); ++iface) {
+        for (SocketCollection::const_iterator s = iface->sockets_.begin();
+             s != iface->sockets_.end(); ++s) {
+            if (FD_ISSET(s->sockfd_, &sockets)) {
+                candidate = &(*s);
+                break;
+            }
+        }
+        if (candidate) {
+            break;
+        }
+    }
 
+    if (!candidate) {
+        cout << "Received data over unknown socket." << endl;
+        return (Pkt6Ptr()); // NULL
+    }
+
+    cout << "Trying to receive over UDP6 socket " << candidate->sockfd_ << " bound to "
+         << candidate->addr_.toText() << "/port=" << candidate->port_ << " on "
+         << iface->getFullName() << endl;
+
+    // Now we have a socket, let's get some data from it!
+    uint8_t buf[RCVBUFSIZE];
     memset(&control_buf_[0], 0, control_buf_len_);
     struct sockaddr_in6 from;
     memset(&from, 0, sizeof(from));
@@ -892,43 +990,7 @@ Pkt6Ptr IfaceMgr::receive6() {
     m.msg_control = &control_buf_[0];
     m.msg_controllen = control_buf_len_;
 
-    /// 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();
-    const SocketInfo* candidate = 0;
-    while (iface != ifaces_.end()) {
-        for (SocketCollection::const_iterator s = iface->sockets_.begin();
-             s != iface->sockets_.end(); ++s) {
-            if (s->addr_.getFamily() != AF_INET6) {
-                continue;
-            }
-            if (s->addr_.getAddress().to_v6().is_multicast()) {
-                candidate = &(*s);
-                break;
-            }
-            if (!candidate) {
-                candidate = &(*s); // it's not multicast, but it's better than nothing
-            }
-        }
-        if (candidate) {
-            break;
-        }
-        ++iface;
-    }
-    if (iface == ifaces_.end()) {
-        isc_throw(Unexpected, "No suitable IPv6 interfaces detected. Can't receive anything.");
-    }
-
-    if (!candidate) {
-        isc_throw(Unexpected, "Interface " << iface->getFullName()
-                  << " does not have any sockets open.");
-    }
-
-    cout << "Trying to receive over UDP6 socket " << candidate->sockfd_ << " bound to "
-         << candidate->addr_.toText() << "/port=" << candidate->port_ << " on "
-         << iface->getFullName() << endl;
-    int result = recvmsg(candidate->sockfd_, &m, 0);
+    result = recvmsg(candidate->sockfd_, &m, 0);
 
     struct in6_addr to_addr;
     memset(&to_addr, 0, sizeof(to_addr));
diff --git a/src/lib/dhcp/iface_mgr.h b/src/lib/dhcp/iface_mgr.h
index 7fa2e85..48f43a6 100644
--- a/src/lib/dhcp/iface_mgr.h
+++ b/src/lib/dhcp/iface_mgr.h
@@ -345,8 +345,10 @@ public:
     /// to not wait infinitely, but rather do something useful
     /// (e.g. remove expired leases)
     ///
+    /// @param timeout specifies timeout (in seconds)
+    ///
     /// @return Pkt6 object representing received packet (or NULL)
-    Pkt6Ptr receive6();
+    Pkt6Ptr receive6(uint32_t timeout);
 
     /// @brief Tries to receive IPv4 packet over open IPv4 sockets.
     ///
diff --git a/src/lib/dhcp/tests/iface_mgr_unittest.cc b/src/lib/dhcp/tests/iface_mgr_unittest.cc
index e7ccb68..df74d93 100644
--- a/src/lib/dhcp/tests/iface_mgr_unittest.cc
+++ b/src/lib/dhcp/tests/iface_mgr_unittest.cc
@@ -309,7 +309,7 @@ TEST_F(IfaceMgrTest, sendReceive6) {
 
     EXPECT_EQ(true, ifacemgr->send(sendPkt));
 
-    rcvPkt = ifacemgr->receive6();
+    rcvPkt = ifacemgr->receive6(10);
 
     ASSERT_TRUE(rcvPkt); // received our own packet
 



More information about the bind10-changes mailing list