dhcp4_srv.cc 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979
  1. // Copyright (C) 2011-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 <asiolink/io_address.h>
  15. #include <dhcp/dhcp4.h>
  16. #include <dhcp/iface_mgr.h>
  17. #include <dhcp/option4_addrlst.h>
  18. #include <dhcp/option_int.h>
  19. #include <dhcp/option_int_array.h>
  20. #include <dhcp/pkt4.h>
  21. #include <dhcp/duid.h>
  22. #include <dhcp/hwaddr.h>
  23. #include <dhcp4/dhcp4_log.h>
  24. #include <dhcp4/dhcp4_srv.h>
  25. #include <dhcpsrv/utils.h>
  26. #include <dhcpsrv/cfgmgr.h>
  27. #include <dhcpsrv/lease_mgr.h>
  28. #include <dhcpsrv/lease_mgr_factory.h>
  29. #include <dhcpsrv/subnet.h>
  30. #include <dhcpsrv/utils.h>
  31. #include <dhcpsrv/addr_utilities.h>
  32. #include <hooks/hooks_manager.h>
  33. #include <hooks/callout_handle.h>
  34. #include <boost/algorithm/string/erase.hpp>
  35. #include <iomanip>
  36. #include <fstream>
  37. using namespace isc;
  38. using namespace isc::asiolink;
  39. using namespace isc::dhcp;
  40. using namespace isc::hooks;
  41. using namespace isc::log;
  42. using namespace std;
  43. /// Structure that holds registered hook indexes
  44. struct Dhcp6Hooks {
  45. int hook_index_pkt4_receive_; ///< index for "pkt4_receive" hook point
  46. int hook_index_subnet4_select_; ///< index for "subnet4_select" hook point
  47. int hook_index_pkt4_send_; ///< index for "pkt4_send" hook point
  48. /// Constructor that registers hook points for DHCPv6 engine
  49. Dhcp6Hooks() {
  50. hook_index_pkt4_receive_ = HooksManager::registerHook("pkt4_receive");
  51. hook_index_subnet4_select_ = HooksManager::registerHook("subnet4_select");
  52. hook_index_pkt4_send_ = HooksManager::registerHook("pkt4_send");
  53. }
  54. };
  55. // Declare a Hooks object. As this is outside any function or method, it
  56. // will be instantiated (and the constructor run) when the module is loaded.
  57. // As a result, the hook indexes will be defined before any method in this
  58. // module is called.
  59. Dhcp6Hooks Hooks;
  60. namespace isc {
  61. namespace dhcp {
  62. /// @brief file name of a server-id file
  63. ///
  64. /// Server must store its server identifier in persistent storage that must not
  65. /// change between restarts. This is name of the file that is created in dataDir
  66. /// (see isc::dhcp::CfgMgr::getDataDir()). It is a text file that uses
  67. /// regular IPv4 address, e.g. 192.0.2.1. Server will create it during
  68. /// first run and then use it afterwards.
  69. static const char* SERVER_ID_FILE = "b10-dhcp4-serverid";
  70. // These are hardcoded parameters. Currently this is a skeleton server that only
  71. // grants those options and a single, fixed, hardcoded lease.
  72. Dhcpv4Srv::Dhcpv4Srv(uint16_t port, const char* dbconfig, const bool use_bcast,
  73. const bool direct_response_desired)
  74. :serverid_(), shutdown_(true), alloc_engine_(), hook_index_pkt4_receive_(-1),
  75. hook_index_subnet4_select_(-1), hook_index_pkt4_send_(-1) {
  76. LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_OPEN_SOCKET).arg(port);
  77. try {
  78. // First call to instance() will create IfaceMgr (it's a singleton)
  79. // it may throw something if things go wrong.
  80. // The 'true' value of the call to setMatchingPacketFilter imposes
  81. // that IfaceMgr will try to use the mechanism to respond directly
  82. // to the client which doesn't have address assigned. This capability
  83. // may be lacking on some OSes, so there is no guarantee that server
  84. // will be able to respond directly.
  85. IfaceMgr::instance().setMatchingPacketFilter(direct_response_desired);
  86. if (port) {
  87. // open sockets only if port is non-zero. Port 0 is used
  88. // for non-socket related testing.
  89. IfaceMgr::instance().openSockets4(port, use_bcast);
  90. }
  91. string srvid_file = CfgMgr::instance().getDataDir() + "/" + string(SERVER_ID_FILE);
  92. if (loadServerID(srvid_file)) {
  93. LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_SERVERID_LOADED)
  94. .arg(srvidToString(getServerID()))
  95. .arg(srvid_file);
  96. } else {
  97. generateServerID();
  98. LOG_INFO(dhcp4_logger, DHCP4_SERVERID_GENERATED)
  99. .arg(srvidToString(getServerID()))
  100. .arg(srvid_file);
  101. if (!writeServerID(srvid_file)) {
  102. LOG_WARN(dhcp4_logger, DHCP4_SERVERID_WRITE_FAIL)
  103. .arg(srvid_file);
  104. }
  105. }
  106. // Instantiate LeaseMgr
  107. LeaseMgrFactory::create(dbconfig);
  108. LOG_INFO(dhcp4_logger, DHCP4_DB_BACKEND_STARTED)
  109. .arg(LeaseMgrFactory::instance().getType())
  110. .arg(LeaseMgrFactory::instance().getName());
  111. // Instantiate allocation engine
  112. alloc_engine_.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100));
  113. // Register hook points
  114. hook_index_pkt4_receive_ = Hooks.hook_index_pkt4_receive_;
  115. hook_index_subnet4_select_ = Hooks.hook_index_subnet4_select_;
  116. hook_index_pkt4_send_ = Hooks.hook_index_pkt4_send_;
  117. /// @todo call loadLibraries() when handling configuration changes
  118. vector<string> libraries; // no libraries at this time
  119. HooksManager::loadLibraries(libraries);
  120. } catch (const std::exception &e) {
  121. LOG_ERROR(dhcp4_logger, DHCP4_SRV_CONSTRUCT_ERROR).arg(e.what());
  122. shutdown_ = true;
  123. return;
  124. }
  125. shutdown_ = false;
  126. }
  127. Dhcpv4Srv::~Dhcpv4Srv() {
  128. IfaceMgr::instance().closeSockets();
  129. }
  130. void
  131. Dhcpv4Srv::shutdown() {
  132. LOG_DEBUG(dhcp4_logger, DBG_DHCP4_BASIC, DHCP4_SHUTDOWN_REQUEST);
  133. shutdown_ = true;
  134. }
  135. Pkt4Ptr Dhcpv4Srv::receivePacket(int timeout) {
  136. return (IfaceMgr::instance().receive4(timeout));
  137. }
  138. void Dhcpv4Srv::sendPacket(const Pkt4Ptr& packet) {
  139. IfaceMgr::instance().send(packet);
  140. }
  141. bool
  142. Dhcpv4Srv::run() {
  143. while (!shutdown_) {
  144. /// @todo: calculate actual timeout once we have lease database
  145. //cppcheck-suppress variableScope This is temporary anyway
  146. const int timeout = 1000;
  147. // client's message and server's response
  148. Pkt4Ptr query;
  149. Pkt4Ptr rsp;
  150. try {
  151. query = receivePacket(timeout);
  152. } catch (const std::exception& e) {
  153. LOG_ERROR(dhcp4_logger, DHCP4_PACKET_RECEIVE_FAIL).arg(e.what());
  154. }
  155. if (query) {
  156. try {
  157. query->unpack();
  158. } catch (const std::exception& e) {
  159. // Failed to parse the packet.
  160. LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL,
  161. DHCP4_PACKET_PARSE_FAIL).arg(e.what());
  162. continue;
  163. }
  164. LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_PACKET_RECEIVED)
  165. .arg(serverReceivedPacketName(query->getType()))
  166. .arg(query->getType())
  167. .arg(query->getIface());
  168. LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL_DATA, DHCP4_QUERY_DATA)
  169. .arg(query->toText());
  170. // Let's execute all callouts registered for packet_received
  171. if (HooksManager::getHooksManager().calloutsPresent(hook_index_pkt4_receive_)) {
  172. CalloutHandlePtr callout_handle = getCalloutHandle(query);
  173. // Delete previously set arguments
  174. callout_handle->deleteAllArguments();
  175. // Pass incoming packet as argument
  176. callout_handle->setArgument("query4", query);
  177. // Call callouts
  178. HooksManager::getHooksManager().callCallouts(hook_index_pkt4_receive_,
  179. *callout_handle);
  180. // Callouts decided to skip the next processing step. The next
  181. // processing step would to process the packet, so skip at this
  182. // stage means drop.
  183. if (callout_handle->getSkip()) {
  184. LOG_DEBUG(dhcp4_logger, DBG_DHCP4_HOOKS, DHCP4_HOOK_PACKET_RCVD_SKIP);
  185. continue;
  186. }
  187. callout_handle->getArgument("query4", query);
  188. }
  189. try {
  190. switch (query->getType()) {
  191. case DHCPDISCOVER:
  192. rsp = processDiscover(query);
  193. break;
  194. case DHCPREQUEST:
  195. rsp = processRequest(query);
  196. break;
  197. case DHCPRELEASE:
  198. processRelease(query);
  199. break;
  200. case DHCPDECLINE:
  201. processDecline(query);
  202. break;
  203. case DHCPINFORM:
  204. processInform(query);
  205. break;
  206. default:
  207. // Only action is to output a message if debug is enabled,
  208. // and that is covered by the debug statement before the
  209. // "switch" statement.
  210. ;
  211. }
  212. } catch (const isc::Exception& e) {
  213. // Catch-all exception (at least for ones based on the isc
  214. // Exception class, which covers more or less all that
  215. // are explicitly raised in the BIND 10 code). Just log
  216. // the problem and ignore the packet. (The problem is logged
  217. // as a debug message because debug is disabled by default -
  218. // it prevents a DDOS attack based on the sending of problem
  219. // packets.)
  220. if (dhcp4_logger.isDebugEnabled(DBG_DHCP4_BASIC)) {
  221. std::string source = "unknown";
  222. HWAddrPtr hwptr = query->getHWAddr();
  223. if (hwptr) {
  224. source = hwptr->toText();
  225. }
  226. LOG_DEBUG(dhcp4_logger, DBG_DHCP4_BASIC,
  227. DHCP4_PACKET_PROCESS_FAIL)
  228. .arg(source).arg(e.what());
  229. }
  230. }
  231. if (rsp) {
  232. adjustRemoteAddr(query, rsp);
  233. if (!rsp->getHops()) {
  234. rsp->setRemotePort(DHCP4_CLIENT_PORT);
  235. } else {
  236. rsp->setRemotePort(DHCP4_SERVER_PORT);
  237. }
  238. rsp->setLocalAddr(query->getLocalAddr());
  239. rsp->setLocalPort(DHCP4_SERVER_PORT);
  240. rsp->setIface(query->getIface());
  241. rsp->setIndex(query->getIndex());
  242. // Execute all callouts registered for packet6_send
  243. if (HooksManager::getHooksManager().calloutsPresent(hook_index_pkt4_send_)) {
  244. CalloutHandlePtr callout_handle = getCalloutHandle(query);
  245. // Delete all previous arguments
  246. callout_handle->deleteAllArguments();
  247. // Clear skip flag if it was set in previous callouts
  248. callout_handle->setSkip(false);
  249. // Set our response
  250. callout_handle->setArgument("response4", rsp);
  251. // Call all installed callouts
  252. HooksManager::getHooksManager().callCallouts(hook_index_pkt4_send_,
  253. *callout_handle);
  254. // Callouts decided to skip the next processing step. The next
  255. // processing step would to send the packet, so skip at this
  256. // stage means "drop response".
  257. if (callout_handle->getSkip()) {
  258. LOG_DEBUG(dhcp4_logger, DBG_DHCP4_HOOKS, DHCP4_HOOK_PACKET_SEND_SKIP);
  259. continue;
  260. }
  261. }
  262. LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL_DATA,
  263. DHCP4_RESPONSE_DATA)
  264. .arg(rsp->getType()).arg(rsp->toText());
  265. if (rsp->pack()) {
  266. try {
  267. sendPacket(rsp);
  268. } catch (const std::exception& e) {
  269. LOG_ERROR(dhcp4_logger, DHCP4_PACKET_SEND_FAIL).arg(e.what());
  270. }
  271. } else {
  272. LOG_ERROR(dhcp4_logger, DHCP4_PACK_FAIL);
  273. }
  274. }
  275. }
  276. }
  277. return (true);
  278. }
  279. bool
  280. Dhcpv4Srv::loadServerID(const std::string& file_name) {
  281. // load content of the file into a string
  282. fstream f(file_name.c_str(), ios::in);
  283. if (!f.is_open()) {
  284. return (false);
  285. }
  286. string hex_string;
  287. f >> hex_string;
  288. f.close();
  289. // remove any spaces
  290. boost::algorithm::erase_all(hex_string, " ");
  291. try {
  292. IOAddress addr(hex_string);
  293. if (!addr.isV4()) {
  294. return (false);
  295. }
  296. // Now create server-id option
  297. serverid_.reset(new Option4AddrLst(DHO_DHCP_SERVER_IDENTIFIER, addr));
  298. } catch(...) {
  299. // any kind of malformed input (empty string, IPv6 address, complete
  300. // garbate etc.)
  301. return (false);
  302. }
  303. return (true);
  304. }
  305. void
  306. Dhcpv4Srv::generateServerID() {
  307. const IfaceMgr::IfaceCollection& ifaces = IfaceMgr::instance().getIfaces();
  308. // Let's find suitable interface.
  309. for (IfaceMgr::IfaceCollection::const_iterator iface = ifaces.begin();
  310. iface != ifaces.end(); ++iface) {
  311. // Let's don't use loopback.
  312. if (iface->flag_loopback_) {
  313. continue;
  314. }
  315. // Let's skip downed interfaces. It is better to use working ones.
  316. if (!iface->flag_up_) {
  317. continue;
  318. }
  319. const Iface::AddressCollection addrs = iface->getAddresses();
  320. for (Iface::AddressCollection::const_iterator addr = addrs.begin();
  321. addr != addrs.end(); ++addr) {
  322. if (addr->getFamily() != AF_INET) {
  323. continue;
  324. }
  325. serverid_ = OptionPtr(new Option4AddrLst(DHO_DHCP_SERVER_IDENTIFIER,
  326. *addr));
  327. return;
  328. }
  329. }
  330. isc_throw(BadValue, "No suitable interfaces for server-identifier found");
  331. }
  332. bool
  333. Dhcpv4Srv::writeServerID(const std::string& file_name) {
  334. fstream f(file_name.c_str(), ios::out | ios::trunc);
  335. if (!f.good()) {
  336. return (false);
  337. }
  338. f << srvidToString(getServerID());
  339. f.close();
  340. return (true);
  341. }
  342. string
  343. Dhcpv4Srv::srvidToString(const OptionPtr& srvid) {
  344. if (!srvid) {
  345. isc_throw(BadValue, "NULL pointer passed to srvidToString()");
  346. }
  347. boost::shared_ptr<Option4AddrLst> generated =
  348. boost::dynamic_pointer_cast<Option4AddrLst>(srvid);
  349. if (!srvid) {
  350. isc_throw(BadValue, "Pointer to invalid option passed to srvidToString()");
  351. }
  352. Option4AddrLst::AddressContainer addrs = generated->getAddresses();
  353. if (addrs.size() != 1) {
  354. isc_throw(BadValue, "Malformed option passed to srvidToString(). "
  355. << "Expected to contain a single IPv4 address.");
  356. }
  357. return (addrs[0].toText());
  358. }
  359. void
  360. Dhcpv4Srv::copyDefaultFields(const Pkt4Ptr& question, Pkt4Ptr& answer) {
  361. answer->setIface(question->getIface());
  362. answer->setIndex(question->getIndex());
  363. answer->setCiaddr(question->getCiaddr());
  364. answer->setSiaddr(IOAddress("0.0.0.0")); // explicitly set this to 0
  365. answer->setHops(question->getHops());
  366. // copy MAC address
  367. answer->setHWAddr(question->getHWAddr());
  368. // relay address
  369. answer->setGiaddr(question->getGiaddr());
  370. // Let's copy client-id to response. See RFC6842.
  371. OptionPtr client_id = question->getOption(DHO_DHCP_CLIENT_IDENTIFIER);
  372. if (client_id) {
  373. answer->addOption(client_id);
  374. }
  375. // If src/dest HW addresses are used by the packet filtering class
  376. // we need to copy them as well. There is a need to check that the
  377. // address being set is not-NULL because an attempt to set the NULL
  378. // HW would result in exception. If these values are not set, the
  379. // the default HW addresses (zeroed) should be generated by the
  380. // packet filtering class when creating Ethernet header for
  381. // outgoing packet.
  382. HWAddrPtr src_hw_addr = question->getLocalHWAddr();
  383. if (src_hw_addr) {
  384. answer->setLocalHWAddr(src_hw_addr);
  385. }
  386. HWAddrPtr dst_hw_addr = question->getRemoteHWAddr();
  387. if (dst_hw_addr) {
  388. answer->setRemoteHWAddr(dst_hw_addr);
  389. }
  390. }
  391. void
  392. Dhcpv4Srv::appendDefaultOptions(Pkt4Ptr& msg, uint8_t msg_type) {
  393. OptionPtr opt;
  394. // add Message Type Option (type 53)
  395. msg->setType(msg_type);
  396. // DHCP Server Identifier (type 54)
  397. msg->addOption(getServerID());
  398. // more options will be added here later
  399. }
  400. void
  401. Dhcpv4Srv::appendRequestedOptions(const Pkt4Ptr& question, Pkt4Ptr& msg) {
  402. // Get the subnet relevant for the client. We will need it
  403. // to get the options associated with it.
  404. Subnet4Ptr subnet = selectSubnet(question);
  405. // If we can't find the subnet for the client there is no way
  406. // to get the options to be sent to a client. We don't log an
  407. // error because it will be logged by the assignLease method
  408. // anyway.
  409. if (!subnet) {
  410. return;
  411. }
  412. // try to get the 'Parameter Request List' option which holds the
  413. // codes of requested options.
  414. OptionUint8ArrayPtr option_prl = boost::dynamic_pointer_cast<
  415. OptionUint8Array>(question->getOption(DHO_DHCP_PARAMETER_REQUEST_LIST));
  416. // If there is no PRL option in the message from the client then
  417. // there is nothing to do.
  418. if (!option_prl) {
  419. return;
  420. }
  421. // Get the codes of requested options.
  422. const std::vector<uint8_t>& requested_opts = option_prl->getValues();
  423. // For each requested option code get the instance of the option
  424. // to be returned to the client.
  425. for (std::vector<uint8_t>::const_iterator opt = requested_opts.begin();
  426. opt != requested_opts.end(); ++opt) {
  427. Subnet::OptionDescriptor desc =
  428. subnet->getOptionDescriptor("dhcp4", *opt);
  429. if (desc.option) {
  430. msg->addOption(desc.option);
  431. }
  432. }
  433. }
  434. void
  435. Dhcpv4Srv::appendBasicOptions(const Pkt4Ptr& question, Pkt4Ptr& msg) {
  436. // Identify options that we always want to send to the
  437. // client (if they are configured).
  438. static const uint16_t required_options[] = {
  439. DHO_SUBNET_MASK,
  440. DHO_ROUTERS,
  441. DHO_DOMAIN_NAME_SERVERS,
  442. DHO_DOMAIN_NAME };
  443. static size_t required_options_size =
  444. sizeof(required_options) / sizeof(required_options[0]);
  445. // Get the subnet.
  446. Subnet4Ptr subnet = selectSubnet(question);
  447. if (!subnet) {
  448. return;
  449. }
  450. // Try to find all 'required' options in the outgoing
  451. // message. Those that are not present will be added.
  452. for (int i = 0; i < required_options_size; ++i) {
  453. OptionPtr opt = msg->getOption(required_options[i]);
  454. if (!opt) {
  455. // Check whether option has been configured.
  456. Subnet::OptionDescriptor desc =
  457. subnet->getOptionDescriptor("dhcp4", required_options[i]);
  458. if (desc.option) {
  459. msg->addOption(desc.option);
  460. }
  461. }
  462. }
  463. }
  464. void
  465. Dhcpv4Srv::assignLease(const Pkt4Ptr& question, Pkt4Ptr& answer) {
  466. // We need to select a subnet the client is connected in.
  467. Subnet4Ptr subnet = selectSubnet(question);
  468. if (!subnet) {
  469. // This particular client is out of luck today. We do not have
  470. // information about the subnet he is connected to. This likely means
  471. // misconfiguration of the server (or some relays). We will continue to
  472. // process this message, but our response will be almost useless: no
  473. // addresses or prefixes, no subnet specific configuration etc. The only
  474. // thing this client can get is some global information (like DNS
  475. // servers).
  476. // perhaps this should be logged on some higher level? This is most likely
  477. // configuration bug.
  478. LOG_ERROR(dhcp4_logger, DHCP4_SUBNET_SELECTION_FAILED)
  479. .arg(question->getRemoteAddr().toText())
  480. .arg(serverReceivedPacketName(question->getType()));
  481. answer->setType(DHCPNAK);
  482. answer->setYiaddr(IOAddress("0.0.0.0"));
  483. return;
  484. }
  485. LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL_DATA, DHCP4_SUBNET_SELECTED)
  486. .arg(subnet->toText());
  487. // Get client-id option
  488. ClientIdPtr client_id;
  489. OptionPtr opt = question->getOption(DHO_DHCP_CLIENT_IDENTIFIER);
  490. if (opt) {
  491. client_id = ClientIdPtr(new ClientId(opt->getData()));
  492. }
  493. // client-id is not mandatory in DHCPv4
  494. IOAddress hint = question->getYiaddr();
  495. HWAddrPtr hwaddr = question->getHWAddr();
  496. // "Fake" allocation is processing of DISCOVER message. We pretend to do an
  497. // allocation, but we do not put the lease in the database. That is ok,
  498. // because we do not guarantee that the user will get that exact lease. If
  499. // the user selects this server to do actual allocation (i.e. sends REQUEST)
  500. // it should include this hint. That will help us during the actual lease
  501. // allocation.
  502. bool fake_allocation = (question->getType() == DHCPDISCOVER);
  503. CalloutHandlePtr callout_handle = getCalloutHandle(question);
  504. // Use allocation engine to pick a lease for this client. Allocation engine
  505. // will try to honour the hint, but it is just a hint - some other address
  506. // may be used instead. If fake_allocation is set to false, the lease will
  507. // be inserted into the LeaseMgr as well.
  508. Lease4Ptr lease = alloc_engine_->allocateAddress4(subnet, client_id, hwaddr,
  509. hint, fake_allocation,
  510. callout_handle);
  511. if (lease) {
  512. // We have a lease! Let's set it in the packet and send it back to
  513. // the client.
  514. LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, fake_allocation?
  515. DHCP4_LEASE_ADVERT:DHCP4_LEASE_ALLOC)
  516. .arg(lease->addr_.toText())
  517. .arg(client_id?client_id->toText():"(no client-id)")
  518. .arg(hwaddr?hwaddr->toText():"(no hwaddr info)");
  519. answer->setYiaddr(lease->addr_);
  520. // IP Address Lease time (type 51)
  521. opt = OptionPtr(new Option(Option::V4, DHO_DHCP_LEASE_TIME));
  522. opt->setUint32(lease->valid_lft_);
  523. answer->addOption(opt);
  524. // Router (type 3)
  525. Subnet::OptionDescriptor opt_routers =
  526. subnet->getOptionDescriptor("dhcp4", DHO_ROUTERS);
  527. if (opt_routers.option) {
  528. answer->addOption(opt_routers.option);
  529. }
  530. // Subnet mask (type 1)
  531. answer->addOption(getNetmaskOption(subnet));
  532. // @todo: send renew timer option (T1, option 58)
  533. // @todo: send rebind timer option (T2, option 59)
  534. } else {
  535. // Allocation engine did not allocate a lease. The engine logged
  536. // cause of that failure. The only thing left is to insert
  537. // status code to pass the sad news to the client.
  538. LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, fake_allocation?
  539. DHCP4_LEASE_ADVERT_FAIL:DHCP4_LEASE_ALLOC_FAIL)
  540. .arg(client_id?client_id->toText():"(no client-id)")
  541. .arg(hwaddr?hwaddr->toText():"(no hwaddr info)")
  542. .arg(hint.toText());
  543. answer->setType(DHCPNAK);
  544. answer->setYiaddr(IOAddress("0.0.0.0"));
  545. }
  546. }
  547. void
  548. Dhcpv4Srv::adjustRemoteAddr(const Pkt4Ptr& question, Pkt4Ptr& msg) {
  549. // Let's create static objects representing zeroed and broadcast
  550. // addresses. We will use them further in this function to test
  551. // other addresses against them. Since they are static, they will
  552. // be created only once.
  553. static const IOAddress zero_addr("0.0.0.0");
  554. static const IOAddress bcast_addr("255.255.255.255");
  555. // If received relayed message, server responds to the relay address.
  556. if (question->getGiaddr() != zero_addr) {
  557. msg->setRemoteAddr(question->getGiaddr());
  558. // If giaddr is 0 but client set ciaddr, server should unicast the
  559. // response to ciaddr.
  560. } else if (question->getCiaddr() != zero_addr) {
  561. msg->setRemoteAddr(question->getCiaddr());
  562. // We can't unicast the response to the client when sending NAK,
  563. // because we haven't allocated address for him. Therefore,
  564. // NAK is broadcast.
  565. } else if (msg->getType() == DHCPNAK) {
  566. msg->setRemoteAddr(bcast_addr);
  567. // If yiaddr is set it means that we have created a lease for a client.
  568. } else if (msg->getYiaddr() != zero_addr) {
  569. // If the broadcast bit is set in the flags field, we have to
  570. // send the response to broadcast address. Client may have requested it
  571. // because it doesn't support reception of messages on the interface
  572. // which doesn't have an address assigned. The other case when response
  573. // must be broadcasted is when our server does not support responding
  574. // directly to a client without address assigned.
  575. const bool bcast_flag = ((question->getFlags() & Pkt4::FLAG_BROADCAST_MASK) != 0);
  576. if (!IfaceMgr::instance().isDirectResponseSupported() || bcast_flag) {
  577. msg->setRemoteAddr(bcast_addr);
  578. // Client cleared the broadcast bit and we support direct responses
  579. // so we should unicast the response to a newly allocated address -
  580. // yiaddr.
  581. } else {
  582. msg->setRemoteAddr(msg->getYiaddr());
  583. }
  584. // In most cases, we should have the remote address found already. If we
  585. // found ourselves at this point, the rational thing to do is to respond
  586. // to the address we got the query from.
  587. } else {
  588. msg->setRemoteAddr(question->getRemoteAddr());
  589. }
  590. }
  591. OptionPtr
  592. Dhcpv4Srv::getNetmaskOption(const Subnet4Ptr& subnet) {
  593. uint32_t netmask = getNetmask4(subnet->get().second);
  594. OptionPtr opt(new OptionInt<uint32_t>(Option::V4,
  595. DHO_SUBNET_MASK, netmask));
  596. return (opt);
  597. }
  598. Pkt4Ptr
  599. Dhcpv4Srv::processDiscover(Pkt4Ptr& discover) {
  600. Pkt4Ptr offer = Pkt4Ptr
  601. (new Pkt4(DHCPOFFER, discover->getTransid()));
  602. copyDefaultFields(discover, offer);
  603. appendDefaultOptions(offer, DHCPOFFER);
  604. appendRequestedOptions(discover, offer);
  605. assignLease(discover, offer);
  606. // There are a few basic options that we always want to
  607. // include in the response. If client did not request
  608. // them we append them for him.
  609. appendBasicOptions(discover, offer);
  610. return (offer);
  611. }
  612. Pkt4Ptr
  613. Dhcpv4Srv::processRequest(Pkt4Ptr& request) {
  614. Pkt4Ptr ack = Pkt4Ptr
  615. (new Pkt4(DHCPACK, request->getTransid()));
  616. copyDefaultFields(request, ack);
  617. appendDefaultOptions(ack, DHCPACK);
  618. appendRequestedOptions(request, ack);
  619. assignLease(request, ack);
  620. // There are a few basic options that we always want to
  621. // include in the response. If client did not request
  622. // them we append them for him.
  623. appendBasicOptions(request, ack);
  624. return (ack);
  625. }
  626. void
  627. Dhcpv4Srv::processRelease(Pkt4Ptr& release) {
  628. // Try to find client-id
  629. ClientIdPtr client_id;
  630. OptionPtr opt = release->getOption(DHO_DHCP_CLIENT_IDENTIFIER);
  631. if (opt) {
  632. client_id = ClientIdPtr(new ClientId(opt->getData()));
  633. }
  634. try {
  635. // Do we have a lease for that particular address?
  636. Lease4Ptr lease = LeaseMgrFactory::instance().getLease4(release->getYiaddr());
  637. if (!lease) {
  638. // No such lease - bogus release
  639. LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_RELEASE_FAIL_NO_LEASE)
  640. .arg(release->getYiaddr().toText())
  641. .arg(release->getHWAddr()->toText())
  642. .arg(client_id ? client_id->toText() : "(no client-id)");
  643. return;
  644. }
  645. // Does the hardware address match? We don't want one client releasing
  646. // second client's leases.
  647. if (lease->hwaddr_ != release->getHWAddr()->hwaddr_) {
  648. // @todo: Print hwaddr from lease as part of ticket #2589
  649. LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_RELEASE_FAIL_WRONG_HWADDR)
  650. .arg(release->getYiaddr().toText())
  651. .arg(client_id ? client_id->toText() : "(no client-id)")
  652. .arg(release->getHWAddr()->toText());
  653. return;
  654. }
  655. // Does the lease have client-id info? If it has, then check it with what
  656. // the client sent us.
  657. if (lease->client_id_ && client_id && *lease->client_id_ != *client_id) {
  658. LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_RELEASE_FAIL_WRONG_CLIENT_ID)
  659. .arg(release->getYiaddr().toText())
  660. .arg(client_id->toText())
  661. .arg(lease->client_id_->toText());
  662. return;
  663. }
  664. // Ok, hw and client-id match - let's release the lease.
  665. if (LeaseMgrFactory::instance().deleteLease(lease->addr_)) {
  666. // Release successful - we're done here
  667. LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_RELEASE)
  668. .arg(lease->addr_.toText())
  669. .arg(client_id ? client_id->toText() : "(no client-id)")
  670. .arg(release->getHWAddr()->toText());
  671. } else {
  672. // Release failed -
  673. LOG_ERROR(dhcp4_logger, DHCP4_RELEASE_FAIL)
  674. .arg(lease->addr_.toText())
  675. .arg(client_id ? client_id->toText() : "(no client-id)")
  676. .arg(release->getHWAddr()->toText());
  677. }
  678. } catch (const isc::Exception& ex) {
  679. // Rethrow the exception with a bit more data.
  680. LOG_ERROR(dhcp4_logger, DHCP4_RELEASE_EXCEPTION)
  681. .arg(ex.what())
  682. .arg(release->getYiaddr());
  683. }
  684. }
  685. void
  686. Dhcpv4Srv::processDecline(Pkt4Ptr& /* decline */) {
  687. /// TODO: Implement this.
  688. }
  689. Pkt4Ptr
  690. Dhcpv4Srv::processInform(Pkt4Ptr& inform) {
  691. /// TODO: Currently implemented echo mode. Implement this for real
  692. return (inform);
  693. }
  694. const char*
  695. Dhcpv4Srv::serverReceivedPacketName(uint8_t type) {
  696. static const char* DISCOVER = "DISCOVER";
  697. static const char* REQUEST = "REQUEST";
  698. static const char* RELEASE = "RELEASE";
  699. static const char* DECLINE = "DECLINE";
  700. static const char* INFORM = "INFORM";
  701. static const char* UNKNOWN = "UNKNOWN";
  702. switch (type) {
  703. case DHCPDISCOVER:
  704. return (DISCOVER);
  705. case DHCPREQUEST:
  706. return (REQUEST);
  707. case DHCPRELEASE:
  708. return (RELEASE);
  709. case DHCPDECLINE:
  710. return (DECLINE);
  711. case DHCPINFORM:
  712. return (INFORM);
  713. default:
  714. ;
  715. }
  716. return (UNKNOWN);
  717. }
  718. Subnet4Ptr
  719. Dhcpv4Srv::selectSubnet(const Pkt4Ptr& question) {
  720. Subnet4Ptr subnet;
  721. // Is this relayed message?
  722. IOAddress relay = question->getGiaddr();
  723. if (relay.toText() == "0.0.0.0") {
  724. // Yes: Use relay address to select subnet
  725. subnet = CfgMgr::instance().getSubnet4(relay);
  726. } else {
  727. // No: Use client's address to select subnet
  728. subnet = CfgMgr::instance().getSubnet4(question->getRemoteAddr());
  729. }
  730. // Let's execute all callouts registered for packet_received
  731. if (HooksManager::getHooksManager().calloutsPresent(hook_index_subnet4_select_)) {
  732. CalloutHandlePtr callout_handle = getCalloutHandle(question);
  733. // We're reusing callout_handle from previous calls
  734. callout_handle->deleteAllArguments();
  735. // Set new arguments
  736. callout_handle->setArgument("query4", question);
  737. callout_handle->setArgument("subnet4", subnet);
  738. callout_handle->setArgument("subnet4collection", CfgMgr::instance().getSubnets4());
  739. // Call user (and server-side) callouts
  740. HooksManager::getHooksManager().callCallouts(hook_index_subnet4_select_,
  741. *callout_handle);
  742. // Callouts decided to skip this step. This means that no subnet will be
  743. // selected. Packet processing will continue, but it will be severly limited
  744. // (i.e. only global options will be assigned)
  745. if (callout_handle->getSkip()) {
  746. LOG_DEBUG(dhcp4_logger, DBG_DHCP4_HOOKS, DHCP4_HOOK_SUBNET4_SELECT_SKIP);
  747. return (Subnet4Ptr());
  748. }
  749. // Use whatever subnet was specified by the callout
  750. callout_handle->getArgument("subnet4", subnet);
  751. }
  752. return (subnet);
  753. }
  754. void
  755. Dhcpv4Srv::sanityCheck(const Pkt4Ptr& pkt, RequirementLevel serverid) {
  756. OptionPtr server_id = pkt->getOption(DHO_DHCP_SERVER_IDENTIFIER);
  757. switch (serverid) {
  758. case FORBIDDEN:
  759. if (server_id) {
  760. isc_throw(RFCViolation, "Server-id option was not expected, but "
  761. << "received in " << serverReceivedPacketName(pkt->getType()));
  762. }
  763. break;
  764. case MANDATORY:
  765. if (!server_id) {
  766. isc_throw(RFCViolation, "Server-id option was expected, but not "
  767. " received in message "
  768. << serverReceivedPacketName(pkt->getType()));
  769. }
  770. break;
  771. case OPTIONAL:
  772. // do nothing here
  773. ;
  774. }
  775. }
  776. isc::hooks::CalloutHandlePtr Dhcpv4Srv::getCalloutHandle(const Pkt4Ptr& pkt) {
  777. // This method returns a CalloutHandle for a given packet. It is guaranteed
  778. // to return the same callout_handle (so user library contexts are
  779. // preserved). This method works well if the server processes one packet
  780. // at a time. Once the server architecture is extended to cover parallel
  781. // packets processing (e.g. delayed-ack, some form of buffering etc.), this
  782. // method has to be extended (e.g. store callouts in a map and use pkt as
  783. // a key). Additional code would be required to release the callout handle
  784. // once the server finished processing.
  785. CalloutHandlePtr callout_handle;
  786. static Pkt4Ptr old_pointer;
  787. if (!callout_handle ||
  788. old_pointer != pkt) {
  789. // This is the first packet or a different packet than previously
  790. // passed to getCalloutHandle()
  791. // Remember the pointer to this packet
  792. old_pointer = pkt;
  793. callout_handle = HooksManager::getHooksManager().createCalloutHandle();
  794. }
  795. return (callout_handle);
  796. }
  797. } // namespace dhcp
  798. } // namespace isc