ctrl_dhcp4_srv.cc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. // Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. // PERFORMANCE OF THIS SOFTWARE.
  14. #include <config.h>
  15. #include <asiolink/asiolink.h>
  16. #include <cc/data.h>
  17. #include <cc/session.h>
  18. #include <config/ccsession.h>
  19. #include <dhcp/iface_mgr.h>
  20. #include <dhcp4/config_parser.h>
  21. #include <dhcp4/ctrl_dhcp4_srv.h>
  22. #include <dhcp4/dhcp4_log.h>
  23. #include <dhcp4/spec_config.h>
  24. #include <dhcpsrv/cfgmgr.h>
  25. #include <dhcpsrv/dhcp_config_parser.h>
  26. #include <exceptions/exceptions.h>
  27. #include <hooks/hooks_manager.h>
  28. #include <util/buffer.h>
  29. #include <cassert>
  30. #include <iostream>
  31. #include <sstream>
  32. #include <string>
  33. #include <vector>
  34. using namespace isc::asiolink;
  35. using namespace isc::cc;
  36. using namespace isc::config;
  37. using namespace isc::data;
  38. using namespace isc::dhcp;
  39. using namespace isc::hooks;
  40. using namespace isc::log;
  41. using namespace isc::util;
  42. using namespace std;
  43. namespace isc {
  44. namespace dhcp {
  45. ControlledDhcpv4Srv* ControlledDhcpv4Srv::server_ = NULL;
  46. ConstElementPtr
  47. ControlledDhcpv4Srv::dhcp4StubConfigHandler(ConstElementPtr) {
  48. // This configuration handler is intended to be used only
  49. // when the initial configuration comes in. To receive this
  50. // configuration a pointer to this handler must be passed
  51. // using ModuleCCSession constructor. This constructor will
  52. // invoke the handler and will store the configuration for
  53. // the configuration session when the handler returns success.
  54. // Since this configuration is partial we just pretend to
  55. // parse it and always return success. The function that
  56. // initiates the session must get the configuration on its
  57. // own using getFullConfig.
  58. return (isc::config::createAnswer(0, "Configuration accepted."));
  59. }
  60. ConstElementPtr
  61. ControlledDhcpv4Srv::dhcp4ConfigHandler(ConstElementPtr new_config) {
  62. if (!server_ || !server_->config_session_) {
  63. // That should never happen as we install config_handler
  64. // after we instantiate the server.
  65. ConstElementPtr answer =
  66. isc::config::createAnswer(1, "Configuration rejected,"
  67. " server is during startup/shutdown phase.");
  68. return (answer);
  69. }
  70. // The configuration passed to this handler function is partial.
  71. // In other words, it just includes the values being modified.
  72. // In the same time, there are dependencies between various
  73. // DHCP configuration parsers. For example: the option value can
  74. // be set if the definition of this option is set. If someone removes
  75. // an existing option definition then the partial configuration that
  76. // removes that definition is triggered while a relevant option value
  77. // may remain configured. This eventually results in the DHCP server
  78. // configuration being in the inconsistent state.
  79. // In order to work around this problem we need to merge the new
  80. // configuration with the existing (full) configuration.
  81. // Let's create a new object that will hold the merged configuration.
  82. boost::shared_ptr<MapElement> merged_config(new MapElement());
  83. // Let's get the existing configuration.
  84. ConstElementPtr full_config = server_->config_session_->getFullConfig();
  85. // The full_config and merged_config should be always non-NULL
  86. // but to provide some level of exception safety we check that they
  87. // really are (in case we go out of memory).
  88. if (full_config && merged_config) {
  89. merged_config->setValue(full_config->mapValue());
  90. // Merge an existing and new configuration.
  91. isc::data::merge(merged_config, new_config);
  92. LOG_DEBUG(dhcp4_logger, DBG_DHCP4_COMMAND, DHCP4_CONFIG_UPDATE)
  93. .arg(full_config->str());
  94. }
  95. // Configure the server.
  96. ConstElementPtr answer = configureDhcp4Server(*server_, merged_config);
  97. // Check that configuration was successful. If not, do not reopen sockets.
  98. int rcode = 0;
  99. parseAnswer(rcode, answer);
  100. if (rcode != 0) {
  101. return (answer);
  102. }
  103. // Configuration may change active interfaces. Therefore, we have to reopen
  104. // sockets according to new configuration. This operation is not exception
  105. // safe and we really don't want to emit exceptions to the callback caller.
  106. // Instead, catch an exception and create appropriate answer.
  107. try {
  108. server_->openActiveSockets(server_->getPort(), server_->useBroadcast());
  109. } catch (std::exception& ex) {
  110. std::ostringstream err;
  111. err << "failed to open sockets after server reconfiguration: " << ex.what();
  112. answer = isc::config::createAnswer(1, err.str());
  113. }
  114. return (answer);
  115. }
  116. ConstElementPtr
  117. ControlledDhcpv4Srv::dhcp4CommandHandler(const string& command, ConstElementPtr args) {
  118. LOG_DEBUG(dhcp4_logger, DBG_DHCP4_COMMAND, DHCP4_COMMAND_RECEIVED)
  119. .arg(command).arg(args->str());
  120. if (command == "shutdown") {
  121. if (ControlledDhcpv4Srv::server_) {
  122. ControlledDhcpv4Srv::server_->shutdown();
  123. } else {
  124. LOG_WARN(dhcp4_logger, DHCP4_NOT_RUNNING);
  125. ConstElementPtr answer = isc::config::createAnswer(1,
  126. "Shutdown failure.");
  127. return (answer);
  128. }
  129. ConstElementPtr answer = isc::config::createAnswer(0,
  130. "Shutting down.");
  131. return (answer);
  132. } else if (command == "libreload") {
  133. // TODO delete any stored CalloutHandles referring to the old libraries
  134. // Get list of currently loaded libraries and reload them.
  135. vector<string> loaded = HooksManager::getLibraryNames();
  136. bool status = HooksManager::loadLibraries(loaded);
  137. if (!status) {
  138. LOG_ERROR(dhcp4_logger, DHCP4_HOOKS_LIBS_RELOAD_FAIL);
  139. ConstElementPtr answer = isc::config::createAnswer(1,
  140. "Failed to reload hooks libraries.");
  141. return (answer);
  142. }
  143. ConstElementPtr answer = isc::config::createAnswer(0,
  144. "Hooks libraries successfully reloaded.");
  145. return (answer);
  146. }
  147. ConstElementPtr answer = isc::config::createAnswer(1,
  148. "Unrecognized command.");
  149. return (answer);
  150. }
  151. void ControlledDhcpv4Srv::sessionReader(void) {
  152. // Process one asio event. If there are more events, iface_mgr will call
  153. // this callback more than once.
  154. if (server_) {
  155. server_->io_service_.run_one();
  156. }
  157. }
  158. void ControlledDhcpv4Srv::establishSession() {
  159. string specfile;
  160. if (getenv("B10_FROM_BUILD")) {
  161. specfile = string(getenv("B10_FROM_BUILD")) +
  162. "/src/bin/dhcp4/dhcp4.spec";
  163. } else {
  164. specfile = string(DHCP4_SPECFILE_LOCATION);
  165. }
  166. /// @todo: Check if session is not established already. Throw, if it is.
  167. LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_CCSESSION_STARTING)
  168. .arg(specfile);
  169. cc_session_ = new Session(io_service_.get_io_service());
  170. // Create a session with the dummy configuration handler.
  171. // Dumy configuration handler is internally invoked by the
  172. // constructor and on success the constructor updates
  173. // the current session with the configuration that had been
  174. // committed in the previous session. If we did not install
  175. // the dummy handler, the previous configuration would have
  176. // been lost.
  177. config_session_ = new ModuleCCSession(specfile, *cc_session_,
  178. dhcp4StubConfigHandler,
  179. dhcp4CommandHandler, false);
  180. config_session_->start();
  181. // We initially create ModuleCCSession() without configHandler, as
  182. // the session module is too eager to send partial configuration.
  183. // We want to get the full configuration, so we explicitly call
  184. // getFullConfig() and then pass it to our configHandler.
  185. config_session_->setConfigHandler(dhcp4ConfigHandler);
  186. try {
  187. configureDhcp4Server(*this, config_session_->getFullConfig());
  188. // Configuration may disable or enable interfaces so we have to
  189. // reopen sockets according to new configuration.
  190. openActiveSockets(getPort(), useBroadcast());
  191. } catch (const DhcpConfigError& ex) {
  192. LOG_ERROR(dhcp4_logger, DHCP4_CONFIG_LOAD_FAIL).arg(ex.what());
  193. }
  194. /// Integrate the asynchronous I/O model of BIND 10 configuration
  195. /// control with the "select" model of the DHCP server. This is
  196. /// fully explained in \ref dhcpv4Session.
  197. int ctrl_socket = cc_session_->getSocketDesc();
  198. LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_CCSESSION_STARTED)
  199. .arg(ctrl_socket);
  200. IfaceMgr::instance().set_session_socket(ctrl_socket, sessionReader);
  201. }
  202. void ControlledDhcpv4Srv::disconnectSession() {
  203. if (config_session_) {
  204. delete config_session_;
  205. config_session_ = NULL;
  206. }
  207. if (cc_session_) {
  208. cc_session_->disconnect();
  209. delete cc_session_;
  210. cc_session_ = NULL;
  211. }
  212. // deregister session socket
  213. IfaceMgr::instance().set_session_socket(IfaceMgr::INVALID_SOCKET, NULL);
  214. }
  215. ControlledDhcpv4Srv::ControlledDhcpv4Srv(uint16_t port /*= DHCP4_SERVER_PORT*/)
  216. :Dhcpv4Srv(port), cc_session_(NULL), config_session_(NULL) {
  217. server_ = this; // remember this instance for use in callback
  218. }
  219. void ControlledDhcpv4Srv::shutdown() {
  220. io_service_.stop(); // Stop ASIO transmissions
  221. Dhcpv4Srv::shutdown(); // Initiate DHCPv4 shutdown procedure.
  222. }
  223. ControlledDhcpv4Srv::~ControlledDhcpv4Srv() {
  224. disconnectSession();
  225. server_ = NULL; // forget this instance. There should be no callback anymore
  226. // at this stage anyway.
  227. }
  228. isc::data::ConstElementPtr
  229. ControlledDhcpv4Srv::execDhcpv4ServerCommand(const std::string& command_id,
  230. isc::data::ConstElementPtr args) {
  231. try {
  232. return (dhcp4CommandHandler(command_id, args));
  233. } catch (const Exception& ex) {
  234. ConstElementPtr answer = isc::config::createAnswer(1, ex.what());
  235. return (answer);
  236. }
  237. }
  238. };
  239. };