dhcp4_srv.cc 28 KB

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