* only), as support for transmission to hosts without IPv4 address
* assigned is not implemented in IfaceMgr yet.
*
- * DHCPv4 server component does not listen to BIND10 message queue.
- *
* DHCPv4 server component does not use BIND10 logging yet.
*
- * DHCPv4 server component is not integrated with boss yet.
+ * @section dhcpv4Session BIND10 message queue integration
+ *
+ * DHCPv4 server component is now integrated with BIND10 message queue.
+ * The integration is performed by establish_session() and disconnect_session()
+ * functions in src/bin/dhcp4/main.cc file. isc::cc::Session cc_session
+ * object uses ASIO for establishing connection. It registers its socket
+ * in isc::asiolink::IOService io_service object. Typically, other components that
+ * use ASIO for their communication, register their other sockets in the
+ * same io_service and then just call io_service.run() method that does
+ * not return, until one of the callback decides that it is time to shut down
+ * the whole component cal calls io_service.stop(). DHCPv4 works in a
+ * different way. It does receive messages using select()
+ * (see isc::dhcp::IfaceMgr::receive4()), which is incompatible with ASIO.
+ * To solve this problem, socket descriptor is extracted from cc_session
+ * object and is passed to IfaceMgr by using isc::dhcp::IfaceMgr::set_session_socket().
+ * IfaceMgr then uses this socket in its select() call. If there is some
+ * data to be read, it calls registered callback that is supposed to
+ * read and process incoming data.
+ *
+ * This somewhat complicated approach is needed for a simple reason. In
+ * embedded deployments there will be no message queue. Not referring directly
+ * to anything related to message queue in isc::dhcp::Dhcpv4Srv and
+ * isc::dhcp::IfaceMgr classes brings in two benefits. First, the can
+ * be used with and without message queue. Second benefit is related to the
+ * first one: \ref libdhcp is supposed to be simple and robust and not require
+ * many dependencies. One notable example of a use case that benefits from
+ * this approach is a perfdhcp tool.
*
* @page dhcpv6 DHCPv6 Server Component
*
*
* DHCPv6 server component is not integrated with boss yet.
*
- * @page libdhcp libdhcp++ library
+ * @page libdhcp libdhcp++
*
- * @section libdhcpIntro Libdhcp++ Introduction
+ * @section libdhcpIntro Libdhcp++ Library Introduction
*
* libdhcp++ is an all-purpose DHCP-manipulation library, written in
* C++. It offers packet parsing and assembly, DHCPv4 and DHCPv6
* isc::dhcp::Option::delOption(), isc::dhcp::Option::getOption() can be used
* for that purpose.
*
- * @section lidhcpIfaceMgr Interface Manager
+ * @section libdhcpIfaceMgr Interface Manager
*
* Interface Manager (or IfaceMgr) is an abstraction layer about low-level
* network operations. In particlar, it provides information about existing
*
* @section DHCP
* - @subpage dhcpv4
+ * - @subpage dhcpv4Session
* - @subpage dhcpv6
* - @subpage libdhcp
+ * - @subpage libdhcpIntro
+ * - @subpage libdhcpIfaceMgr
*
* @section misc Miscellaneous topics
* - @subpage LoggingApi
}
} // end of anonymous namespace
+/// @brief DHCPv4 context (provides access to essential objects)
+///
+/// This is a structure that provides access to essential objects
+/// used during DHCPv4 operation: Dhcpv4Srv object itself and
+/// also objects required for msgq session management.
+struct DHCPv4Context {
+ IOService io_service;
+ Session* cc_session;
+ ModuleCCSession* config_session;
+ Dhcpv4Srv* server;
+ DHCPv4Context(): cc_session(NULL), config_session(NULL), server(NULL) { };
+};
+
+DHCPv4Context dhcp4;
+
// Global objects are ugly, but that is the most convenient way of
// having it accessible from handlers.
-IOService io_service;
// The same applies to global pointers. Ugly, but useful.
-Dhcpv4Srv* server = NULL;
ConstElementPtr
dhcp4_config_handler(ConstElementPtr new_config) {
cout << "b10-dhcp4: Received new config:" << new_config->str() << endl;
ConstElementPtr answer = isc::config::createAnswer(0,
- "Thanks for sending config.");
+ "Thank you for sending config.");
return (answer);
}
cout << "b10-dhcp4: Received new command: [" << command << "], args="
<< args->str() << endl;
if (command == "shutdown") {
- if (server) {
- server->shutdown();
+ if (dhcp4.server) {
+ dhcp4.server->shutdown();
}
- io_service.stop();
+ dhcp4.io_service.stop();
ConstElementPtr answer = isc::config::createAnswer(0,
"Shutting down.");
return (answer);
return (answer);
}
+/// @brief callback that will be called from iface_mgr when command/config arrives
void session_reader(void) {
- io_service.run_one();
+ // Process one asio event. If there are more events, iface_mgr will call
+ // this callback more than once.
+ dhcp4.io_service.run_one();
}
+/// @brief Establishes msgq session.
+///
+/// Creates session that will be used to receive commands and updated
+/// configuration from boss (or indirectly from user via bindctl).
void establish_session() {
- Session* cc_session = NULL;
- ModuleCCSession* config_session = NULL;
-
string specfile;
if (getenv("B10_FROM_BUILD")) {
specfile = string(getenv("B10_FROM_BUILD")) +
cout << "b10-dhcp4: my specfile is " << specfile << endl;
- cc_session = new Session(io_service.get_io_service());
-
- config_session = new ModuleCCSession(specfile, *cc_session,
- dhcp4_config_handler,
- dhcp4_command_handler, false);
+ dhcp4.cc_session = new Session(dhcp4.io_service.get_io_service());
- config_session->start();
+ dhcp4.config_session = new ModuleCCSession(specfile, *dhcp4.cc_session,
+ dhcp4_config_handler,
+ dhcp4_command_handler, false);
+ dhcp4.config_session->start();
- int ctrl_socket = cc_session->getSocketDesc();
+ int ctrl_socket = dhcp4.cc_session->getSocketDesc();
cout << "b10-dhcp4: Control session started, socket="
<< ctrl_socket << endl;
IfaceMgr::instance().set_session_socket(ctrl_socket, session_reader);
+}
- // cout << "b10-dhcp4: About to call io_service.run()" << endl;
- // io_service.run();
- // cout << "b10-dhcp4: Returned from io_service.run()" << endl;
+void disconnect_session() {
+ dhcp4.cc_session->disconnect();
+ delete dhcp4.config_session;
+ dhcp4.config_session = NULL;
+ delete dhcp4.cc_session;
+ dhcp4.cc_session = NULL;
}
int
(verbose_mode ? isc::log::DEBUG : isc::log::INFO),
isc::log::MAX_DEBUG_LEVEL, NULL);
-
- cout << "My pid=" << getpid() << endl;
+ cout << "b10-dhcp4: My pid is " << getpid() << endl;
if (argc - optind > 0) {
usage();
establish_session();
cout << "[b10-dhcp4] Initiating DHCPv4 server operation." << endl;
+ dhcp4.server = new Dhcpv4Srv();
+ dhcp4.server->run();
- server = new Dhcpv4Srv();
-
- server->run();
+ disconnect_session();
- delete server;
+ delete dhcp4.server;
+ dhcp4.server = NULL;
} catch (const std::exception& ex) {
cerr << "[b10-dhcp4] Server failed: " << ex.what() << endl;
#include <fstream>
#include <sstream>
+#include <unistd.h>
#include <arpa/inet.h>
#include <gtest/gtest.h>
// checks that IfaceManager can be instantiated
createLoInterfacesTxt();
- createLoInterfacesTxt();
-
IfaceMgr & ifacemgr = IfaceMgr::instance();
ASSERT_TRUE(&ifacemgr != 0);
}
}
#endif
+volatile bool callback_ok;
+
+void my_callback(void) {
+ cout << "Callback triggered." << endl;
+ callback_ok = true;
+}
+
+TEST_F(IfaceMgrTest, controlSession) {
+ // tests if extra control socket and its callback can be passed and
+ // it is supported properly by receive4() method.
+
+ callback_ok = false;
+
+ NakedIfaceMgr* ifacemgr = new NakedIfaceMgr();
+
+ // create pipe and register it as extra socket
+ int pipefd[2];
+ EXPECT_TRUE(pipe(pipefd) == 0);
+ EXPECT_NO_THROW(ifacemgr->set_session_socket(pipefd[0], my_callback));
+
+ Pkt4Ptr pkt4;
+ pkt4 = ifacemgr->receive4(1);
+
+ // Our callback should not be called this time (there was no data)
+ EXPECT_FALSE(callback_ok);
+
+ // IfaceMgr should not process control socket data as incoming packets
+ EXPECT_FALSE(pkt4);
+
+ // Now, send some data over pipe (38 bytes)
+ EXPECT_EQ(38, write(pipefd[1], "Hi, this is a message sent over a pipe", 38));
+
+ // ... and repeat
+ pkt4 = ifacemgr->receive4(1);
+
+ // IfaceMgr should not process control socket data as incoming packets
+ EXPECT_FALSE(pkt4);
+
+ // There was some data, so this time callback should be called
+ EXPECT_TRUE(callback_ok);
+
+ delete ifacemgr;
+
+ // close both pipe ends
+ close(pipefd[1]);
+ close(pipefd[0]);
+}
+
}