dhcp4_srv.cc 48 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332
  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/duid.h>
  17. #include <dhcp/hwaddr.h>
  18. #include <dhcp/iface_mgr.h>
  19. #include <dhcp/option4_addrlst.h>
  20. #include <dhcp/option_int.h>
  21. #include <dhcp/option_int_array.h>
  22. #include <dhcp/option_vendor.h>
  23. #include <dhcp/pkt4.h>
  24. #include <dhcp/docsis3_option_defs.h>
  25. #include <dhcp4/dhcp4_log.h>
  26. #include <dhcp4/dhcp4_srv.h>
  27. #include <dhcpsrv/addr_utilities.h>
  28. #include <dhcpsrv/callout_handle_store.h>
  29. #include <dhcpsrv/cfgmgr.h>
  30. #include <dhcpsrv/lease_mgr.h>
  31. #include <dhcpsrv/lease_mgr_factory.h>
  32. #include <dhcpsrv/subnet.h>
  33. #include <dhcpsrv/utils.h>
  34. #include <dhcpsrv/utils.h>
  35. #include <hooks/callout_handle.h>
  36. #include <hooks/hooks_manager.h>
  37. #include <boost/algorithm/string/erase.hpp>
  38. #include <boost/bind.hpp>
  39. #include <boost/foreach.hpp>
  40. #include <iomanip>
  41. #include <fstream>
  42. using namespace isc;
  43. using namespace isc::asiolink;
  44. using namespace isc::dhcp;
  45. using namespace isc::hooks;
  46. using namespace isc::log;
  47. using namespace std;
  48. /// Structure that holds registered hook indexes
  49. struct Dhcp4Hooks {
  50. int hook_index_buffer4_receive_;///< index for "buffer4_receive" hook point
  51. int hook_index_pkt4_receive_; ///< index for "pkt4_receive" hook point
  52. int hook_index_subnet4_select_; ///< index for "subnet4_select" hook point
  53. int hook_index_lease4_release_; ///< index for "lease4_release" hook point
  54. int hook_index_pkt4_send_; ///< index for "pkt4_send" hook point
  55. int hook_index_buffer4_send_; ///< index for "buffer4_send" hook point
  56. /// Constructor that registers hook points for DHCPv4 engine
  57. Dhcp4Hooks() {
  58. hook_index_buffer4_receive_= HooksManager::registerHook("buffer4_receive");
  59. hook_index_pkt4_receive_ = HooksManager::registerHook("pkt4_receive");
  60. hook_index_subnet4_select_ = HooksManager::registerHook("subnet4_select");
  61. hook_index_pkt4_send_ = HooksManager::registerHook("pkt4_send");
  62. hook_index_lease4_release_ = HooksManager::registerHook("lease4_release");
  63. hook_index_buffer4_send_ = HooksManager::registerHook("buffer4_send");
  64. }
  65. };
  66. // Declare a Hooks object. As this is outside any function or method, it
  67. // will be instantiated (and the constructor run) when the module is loaded.
  68. // As a result, the hook indexes will be defined before any method in this
  69. // module is called.
  70. Dhcp4Hooks Hooks;
  71. namespace isc {
  72. namespace dhcp {
  73. /// @brief file name of a server-id file
  74. ///
  75. /// Server must store its server identifier in persistent storage that must not
  76. /// change between restarts. This is name of the file that is created in dataDir
  77. /// (see isc::dhcp::CfgMgr::getDataDir()). It is a text file that uses
  78. /// regular IPv4 address, e.g. 192.0.2.1. Server will create it during
  79. /// first run and then use it afterwards.
  80. static const char* SERVER_ID_FILE = "b10-dhcp4-serverid";
  81. // These are hardcoded parameters. Currently this is a skeleton server that only
  82. // grants those options and a single, fixed, hardcoded lease.
  83. Dhcpv4Srv::Dhcpv4Srv(uint16_t port, const char* dbconfig, const bool use_bcast,
  84. const bool direct_response_desired)
  85. : serverid_(), shutdown_(true), alloc_engine_(), port_(port),
  86. use_bcast_(use_bcast), hook_index_pkt4_receive_(-1),
  87. hook_index_subnet4_select_(-1), hook_index_pkt4_send_(-1) {
  88. LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_OPEN_SOCKET).arg(port);
  89. try {
  90. // First call to instance() will create IfaceMgr (it's a singleton)
  91. // it may throw something if things go wrong.
  92. // The 'true' value of the call to setMatchingPacketFilter imposes
  93. // that IfaceMgr will try to use the mechanism to respond directly
  94. // to the client which doesn't have address assigned. This capability
  95. // may be lacking on some OSes, so there is no guarantee that server
  96. // will be able to respond directly.
  97. IfaceMgr::instance().setMatchingPacketFilter(direct_response_desired);
  98. if (port) {
  99. // open sockets only if port is non-zero. Port 0 is used
  100. // for non-socket related testing.
  101. IfaceMgr::instance().openSockets4(port_, use_bcast_);
  102. }
  103. string srvid_file = CfgMgr::instance().getDataDir() + "/" + string(SERVER_ID_FILE);
  104. if (loadServerID(srvid_file)) {
  105. LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_SERVERID_LOADED)
  106. .arg(srvidToString(getServerID()))
  107. .arg(srvid_file);
  108. } else {
  109. generateServerID();
  110. LOG_INFO(dhcp4_logger, DHCP4_SERVERID_GENERATED)
  111. .arg(srvidToString(getServerID()))
  112. .arg(srvid_file);
  113. if (!writeServerID(srvid_file)) {
  114. LOG_WARN(dhcp4_logger, DHCP4_SERVERID_WRITE_FAIL)
  115. .arg(srvid_file);
  116. }
  117. }
  118. // Instantiate LeaseMgr
  119. LeaseMgrFactory::create(dbconfig);
  120. LOG_INFO(dhcp4_logger, DHCP4_DB_BACKEND_STARTED)
  121. .arg(LeaseMgrFactory::instance().getType())
  122. .arg(LeaseMgrFactory::instance().getName());
  123. // Instantiate allocation engine
  124. alloc_engine_.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100,
  125. false /* false = IPv4 */));
  126. // Register hook points
  127. hook_index_pkt4_receive_ = Hooks.hook_index_pkt4_receive_;
  128. hook_index_subnet4_select_ = Hooks.hook_index_subnet4_select_;
  129. hook_index_pkt4_send_ = Hooks.hook_index_pkt4_send_;
  130. /// @todo call loadLibraries() when handling configuration changes
  131. } catch (const std::exception &e) {
  132. LOG_ERROR(dhcp4_logger, DHCP4_SRV_CONSTRUCT_ERROR).arg(e.what());
  133. shutdown_ = true;
  134. return;
  135. }
  136. shutdown_ = false;
  137. }
  138. Dhcpv4Srv::~Dhcpv4Srv() {
  139. IfaceMgr::instance().closeSockets();
  140. }
  141. void
  142. Dhcpv4Srv::shutdown() {
  143. LOG_DEBUG(dhcp4_logger, DBG_DHCP4_BASIC, DHCP4_SHUTDOWN_REQUEST);
  144. shutdown_ = true;
  145. }
  146. Pkt4Ptr
  147. Dhcpv4Srv::receivePacket(int timeout) {
  148. return (IfaceMgr::instance().receive4(timeout));
  149. }
  150. void
  151. Dhcpv4Srv::sendPacket(const Pkt4Ptr& packet) {
  152. IfaceMgr::instance().send(packet);
  153. }
  154. bool
  155. Dhcpv4Srv::run() {
  156. while (!shutdown_) {
  157. /// @todo: calculate actual timeout once we have lease database
  158. //cppcheck-suppress variableScope This is temporary anyway
  159. const int timeout = 1000;
  160. // client's message and server's response
  161. Pkt4Ptr query;
  162. Pkt4Ptr rsp;
  163. try {
  164. query = receivePacket(timeout);
  165. } catch (const std::exception& e) {
  166. LOG_ERROR(dhcp4_logger, DHCP4_PACKET_RECEIVE_FAIL).arg(e.what());
  167. }
  168. // Timeout may be reached or signal received, which breaks select()
  169. // with no reception ocurred
  170. if (!query) {
  171. continue;
  172. }
  173. // In order to parse the DHCP options, the server needs to use some
  174. // configuration information such as: existing option spaces, option
  175. // definitions etc. This is the kind of information which is not
  176. // available in the libdhcp, so we need to supply our own implementation
  177. // of the option parsing function here, which would rely on the
  178. // configuration data.
  179. query->setCallback(boost::bind(&Dhcpv4Srv::unpackOptions, this,
  180. _1, _2, _3));
  181. bool skip_unpack = false;
  182. // The packet has just been received so contains the uninterpreted wire
  183. // data; execute callouts registered for buffer4_receive.
  184. if (HooksManager::calloutsPresent(Hooks.hook_index_buffer4_receive_)) {
  185. CalloutHandlePtr callout_handle = getCalloutHandle(query);
  186. // Delete previously set arguments
  187. callout_handle->deleteAllArguments();
  188. // Pass incoming packet as argument
  189. callout_handle->setArgument("query4", query);
  190. // Call callouts
  191. HooksManager::callCallouts(Hooks.hook_index_buffer4_receive_,
  192. *callout_handle);
  193. // Callouts decided to skip the next processing step. The next
  194. // processing step would to parse the packet, so skip at this
  195. // stage means that callouts did the parsing already, so server
  196. // should skip parsing.
  197. if (callout_handle->getSkip()) {
  198. LOG_DEBUG(dhcp4_logger, DBG_DHCP4_HOOKS, DHCP4_HOOK_BUFFER_RCVD_SKIP);
  199. skip_unpack = true;
  200. }
  201. callout_handle->getArgument("query4", query);
  202. }
  203. // Unpack the packet information unless the buffer4_receive callouts
  204. // indicated they did it
  205. if (!skip_unpack) {
  206. try {
  207. query->unpack();
  208. } catch (const std::exception& e) {
  209. // Failed to parse the packet.
  210. LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL,
  211. DHCP4_PACKET_PARSE_FAIL).arg(e.what());
  212. continue;
  213. }
  214. }
  215. // When receiving a packet without message type option, getType() will
  216. // throw. Let's set type to -1 as default error indicator.
  217. int type = -1;
  218. try {
  219. type = query->getType();
  220. } catch (...) {
  221. LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_PACKET_DROP_NO_TYPE)
  222. .arg(query->getIface());
  223. continue;
  224. }
  225. LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_PACKET_RECEIVED)
  226. .arg(serverReceivedPacketName(type))
  227. .arg(type)
  228. .arg(query->getIface());
  229. LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL_DATA, DHCP4_QUERY_DATA)
  230. .arg(type)
  231. .arg(query->toText());
  232. // Let's execute all callouts registered for pkt4_receive
  233. if (HooksManager::calloutsPresent(hook_index_pkt4_receive_)) {
  234. CalloutHandlePtr callout_handle = getCalloutHandle(query);
  235. // Delete previously set arguments
  236. callout_handle->deleteAllArguments();
  237. // Pass incoming packet as argument
  238. callout_handle->setArgument("query4", query);
  239. // Call callouts
  240. HooksManager::callCallouts(hook_index_pkt4_receive_,
  241. *callout_handle);
  242. // Callouts decided to skip the next processing step. The next
  243. // processing step would to process the packet, so skip at this
  244. // stage means drop.
  245. if (callout_handle->getSkip()) {
  246. LOG_DEBUG(dhcp4_logger, DBG_DHCP4_HOOKS, DHCP4_HOOK_PACKET_RCVD_SKIP);
  247. continue;
  248. }
  249. callout_handle->getArgument("query4", query);
  250. }
  251. try {
  252. switch (query->getType()) {
  253. case DHCPDISCOVER:
  254. rsp = processDiscover(query);
  255. break;
  256. case DHCPREQUEST:
  257. // Note that REQUEST is used for many things in DHCPv4: for
  258. // requesting new leases, renewing existing ones and even
  259. // for rebinding.
  260. rsp = processRequest(query);
  261. break;
  262. case DHCPRELEASE:
  263. processRelease(query);
  264. break;
  265. case DHCPDECLINE:
  266. processDecline(query);
  267. break;
  268. case DHCPINFORM:
  269. processInform(query);
  270. break;
  271. default:
  272. // Only action is to output a message if debug is enabled,
  273. // and that is covered by the debug statement before the
  274. // "switch" statement.
  275. ;
  276. }
  277. } catch (const isc::Exception& e) {
  278. // Catch-all exception (at least for ones based on the isc
  279. // Exception class, which covers more or less all that
  280. // are explicitly raised in the BIND 10 code). Just log
  281. // the problem and ignore the packet. (The problem is logged
  282. // as a debug message because debug is disabled by default -
  283. // it prevents a DDOS attack based on the sending of problem
  284. // packets.)
  285. if (dhcp4_logger.isDebugEnabled(DBG_DHCP4_BASIC)) {
  286. std::string source = "unknown";
  287. HWAddrPtr hwptr = query->getHWAddr();
  288. if (hwptr) {
  289. source = hwptr->toText();
  290. }
  291. LOG_DEBUG(dhcp4_logger, DBG_DHCP4_BASIC,
  292. DHCP4_PACKET_PROCESS_FAIL)
  293. .arg(source).arg(e.what());
  294. }
  295. }
  296. if (!rsp) {
  297. continue;
  298. }
  299. adjustRemoteAddr(query, rsp);
  300. if (!rsp->getHops()) {
  301. rsp->setRemotePort(DHCP4_CLIENT_PORT);
  302. } else {
  303. rsp->setRemotePort(DHCP4_SERVER_PORT);
  304. }
  305. rsp->setLocalAddr(query->getLocalAddr());
  306. rsp->setLocalPort(DHCP4_SERVER_PORT);
  307. rsp->setIface(query->getIface());
  308. rsp->setIndex(query->getIndex());
  309. // Specifies if server should do the packing
  310. bool skip_pack = false;
  311. // Execute all callouts registered for pkt4_send
  312. if (HooksManager::calloutsPresent(hook_index_pkt4_send_)) {
  313. CalloutHandlePtr callout_handle = getCalloutHandle(query);
  314. // Delete all previous arguments
  315. callout_handle->deleteAllArguments();
  316. // Clear skip flag if it was set in previous callouts
  317. callout_handle->setSkip(false);
  318. // Set our response
  319. callout_handle->setArgument("response4", rsp);
  320. // Call all installed callouts
  321. HooksManager::callCallouts(hook_index_pkt4_send_,
  322. *callout_handle);
  323. // Callouts decided to skip the next processing step. The next
  324. // processing step would to send the packet, so skip at this
  325. // stage means "drop response".
  326. if (callout_handle->getSkip()) {
  327. LOG_DEBUG(dhcp4_logger, DBG_DHCP4_HOOKS, DHCP4_HOOK_PACKET_SEND_SKIP);
  328. skip_pack = true;
  329. }
  330. }
  331. if (!skip_pack) {
  332. try {
  333. rsp->pack();
  334. } catch (const std::exception& e) {
  335. LOG_ERROR(dhcp4_logger, DHCP4_PACKET_SEND_FAIL)
  336. .arg(e.what());
  337. }
  338. }
  339. try {
  340. // Now all fields and options are constructed into output wire buffer.
  341. // Option objects modification does not make sense anymore. Hooks
  342. // can only manipulate wire buffer at this stage.
  343. // Let's execute all callouts registered for buffer4_send
  344. if (HooksManager::calloutsPresent(Hooks.hook_index_buffer4_send_)) {
  345. CalloutHandlePtr callout_handle = getCalloutHandle(query);
  346. // Delete previously set arguments
  347. callout_handle->deleteAllArguments();
  348. // Pass incoming packet as argument
  349. callout_handle->setArgument("response4", rsp);
  350. // Call callouts
  351. HooksManager::callCallouts(Hooks.hook_index_buffer4_send_,
  352. *callout_handle);
  353. // Callouts decided to skip the next processing step. The next
  354. // processing step would to parse the packet, so skip at this
  355. // stage means drop.
  356. if (callout_handle->getSkip()) {
  357. LOG_DEBUG(dhcp4_logger, DBG_DHCP4_HOOKS,
  358. DHCP4_HOOK_BUFFER_SEND_SKIP);
  359. continue;
  360. }
  361. callout_handle->getArgument("response4", rsp);
  362. }
  363. LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL_DATA,
  364. DHCP4_RESPONSE_DATA)
  365. .arg(static_cast<int>(rsp->getType())).arg(rsp->toText());
  366. sendPacket(rsp);
  367. } catch (const std::exception& e) {
  368. LOG_ERROR(dhcp4_logger, DHCP4_PACKET_SEND_FAIL)
  369. .arg(e.what());
  370. }
  371. }
  372. return (true);
  373. }
  374. bool
  375. Dhcpv4Srv::loadServerID(const std::string& file_name) {
  376. // load content of the file into a string
  377. fstream f(file_name.c_str(), ios::in);
  378. if (!f.is_open()) {
  379. return (false);
  380. }
  381. string hex_string;
  382. f >> hex_string;
  383. f.close();
  384. // remove any spaces
  385. boost::algorithm::erase_all(hex_string, " ");
  386. try {
  387. IOAddress addr(hex_string);
  388. if (!addr.isV4()) {
  389. return (false);
  390. }
  391. // Now create server-id option
  392. serverid_.reset(new Option4AddrLst(DHO_DHCP_SERVER_IDENTIFIER, addr));
  393. } catch(...) {
  394. // any kind of malformed input (empty string, IPv6 address, complete
  395. // garbate etc.)
  396. return (false);
  397. }
  398. return (true);
  399. }
  400. void
  401. Dhcpv4Srv::generateServerID() {
  402. const IfaceMgr::IfaceCollection& ifaces = IfaceMgr::instance().getIfaces();
  403. // Let's find suitable interface.
  404. for (IfaceMgr::IfaceCollection::const_iterator iface = ifaces.begin();
  405. iface != ifaces.end(); ++iface) {
  406. // Let's don't use loopback.
  407. if (iface->flag_loopback_) {
  408. continue;
  409. }
  410. // Let's skip downed interfaces. It is better to use working ones.
  411. if (!iface->flag_up_) {
  412. continue;
  413. }
  414. const Iface::AddressCollection addrs = iface->getAddresses();
  415. for (Iface::AddressCollection::const_iterator addr = addrs.begin();
  416. addr != addrs.end(); ++addr) {
  417. if (addr->getFamily() != AF_INET) {
  418. continue;
  419. }
  420. serverid_ = OptionPtr(new Option4AddrLst(DHO_DHCP_SERVER_IDENTIFIER,
  421. *addr));
  422. return;
  423. }
  424. }
  425. isc_throw(BadValue, "No suitable interfaces for server-identifier found");
  426. }
  427. bool
  428. Dhcpv4Srv::writeServerID(const std::string& file_name) {
  429. fstream f(file_name.c_str(), ios::out | ios::trunc);
  430. if (!f.good()) {
  431. return (false);
  432. }
  433. f << srvidToString(getServerID());
  434. f.close();
  435. return (true);
  436. }
  437. string
  438. Dhcpv4Srv::srvidToString(const OptionPtr& srvid) {
  439. if (!srvid) {
  440. isc_throw(BadValue, "NULL pointer passed to srvidToString()");
  441. }
  442. boost::shared_ptr<Option4AddrLst> generated =
  443. boost::dynamic_pointer_cast<Option4AddrLst>(srvid);
  444. if (!srvid) {
  445. isc_throw(BadValue, "Pointer to invalid option passed to srvidToString()");
  446. }
  447. Option4AddrLst::AddressContainer addrs = generated->getAddresses();
  448. if (addrs.size() != 1) {
  449. isc_throw(BadValue, "Malformed option passed to srvidToString(). "
  450. << "Expected to contain a single IPv4 address.");
  451. }
  452. return (addrs[0].toText());
  453. }
  454. void
  455. Dhcpv4Srv::copyDefaultFields(const Pkt4Ptr& question, Pkt4Ptr& answer) {
  456. answer->setIface(question->getIface());
  457. answer->setIndex(question->getIndex());
  458. answer->setCiaddr(question->getCiaddr());
  459. answer->setSiaddr(IOAddress("0.0.0.0")); // explicitly set this to 0
  460. answer->setHops(question->getHops());
  461. // copy MAC address
  462. answer->setHWAddr(question->getHWAddr());
  463. // relay address
  464. answer->setGiaddr(question->getGiaddr());
  465. // Let's copy client-id to response. See RFC6842.
  466. OptionPtr client_id = question->getOption(DHO_DHCP_CLIENT_IDENTIFIER);
  467. if (client_id) {
  468. answer->addOption(client_id);
  469. }
  470. // If src/dest HW addresses are used by the packet filtering class
  471. // we need to copy them as well. There is a need to check that the
  472. // address being set is not-NULL because an attempt to set the NULL
  473. // HW would result in exception. If these values are not set, the
  474. // the default HW addresses (zeroed) should be generated by the
  475. // packet filtering class when creating Ethernet header for
  476. // outgoing packet.
  477. HWAddrPtr src_hw_addr = question->getLocalHWAddr();
  478. if (src_hw_addr) {
  479. answer->setLocalHWAddr(src_hw_addr);
  480. }
  481. HWAddrPtr dst_hw_addr = question->getRemoteHWAddr();
  482. if (dst_hw_addr) {
  483. answer->setRemoteHWAddr(dst_hw_addr);
  484. }
  485. // If this packet is relayed, we want to copy Relay Agent Info option
  486. OptionPtr rai = question->getOption(DHO_DHCP_AGENT_OPTIONS);
  487. if (rai) {
  488. answer->addOption(rai);
  489. }
  490. }
  491. void
  492. Dhcpv4Srv::appendDefaultOptions(Pkt4Ptr& msg, uint8_t msg_type) {
  493. OptionPtr opt;
  494. // add Message Type Option (type 53)
  495. msg->setType(msg_type);
  496. // DHCP Server Identifier (type 54)
  497. msg->addOption(getServerID());
  498. // more options will be added here later
  499. }
  500. void
  501. Dhcpv4Srv::appendRequestedOptions(const Pkt4Ptr& question, Pkt4Ptr& msg) {
  502. // Get the subnet relevant for the client. We will need it
  503. // to get the options associated with it.
  504. Subnet4Ptr subnet = selectSubnet(question);
  505. // If we can't find the subnet for the client there is no way
  506. // to get the options to be sent to a client. We don't log an
  507. // error because it will be logged by the assignLease method
  508. // anyway.
  509. if (!subnet) {
  510. return;
  511. }
  512. // try to get the 'Parameter Request List' option which holds the
  513. // codes of requested options.
  514. OptionUint8ArrayPtr option_prl = boost::dynamic_pointer_cast<
  515. OptionUint8Array>(question->getOption(DHO_DHCP_PARAMETER_REQUEST_LIST));
  516. // If there is no PRL option in the message from the client then
  517. // there is nothing to do.
  518. if (!option_prl) {
  519. return;
  520. }
  521. // Get the codes of requested options.
  522. const std::vector<uint8_t>& requested_opts = option_prl->getValues();
  523. // For each requested option code get the instance of the option
  524. // to be returned to the client.
  525. for (std::vector<uint8_t>::const_iterator opt = requested_opts.begin();
  526. opt != requested_opts.end(); ++opt) {
  527. Subnet::OptionDescriptor desc =
  528. subnet->getOptionDescriptor("dhcp4", *opt);
  529. if (desc.option) {
  530. msg->addOption(desc.option);
  531. }
  532. }
  533. }
  534. void
  535. Dhcpv4Srv::appendRequestedVendorOptions(const Pkt4Ptr& question, Pkt4Ptr& answer) {
  536. // Get the configured subnet suitable for the incoming packet.
  537. Subnet4Ptr subnet = selectSubnet(question);
  538. // Leave if there is no subnet matching the incoming packet.
  539. // There is no need to log the error message here because
  540. // it will be logged in the assignLease() when it fails to
  541. // pick the suitable subnet. We don't want to duplicate
  542. // error messages in such case.
  543. if (!subnet) {
  544. return;
  545. }
  546. // Try to get the vendor option
  547. boost::shared_ptr<OptionVendor> vendor_req =
  548. boost::dynamic_pointer_cast<OptionVendor>(question->getOption(DHO_VIVSO_SUBOPTIONS));
  549. if (!vendor_req) {
  550. return;
  551. }
  552. uint32_t vendor_id = vendor_req->getVendorId();
  553. // Let's try to get ORO within that vendor-option
  554. /// @todo This is very specific to vendor-id=4491 (Cable Labs). Other vendors
  555. /// may have different policies.
  556. OptionPtr oro = vendor_req->getOption(DOCSIS3_V4_ORO);
  557. /// @todo: see OPT_UINT8_TYPE definition in OptionDefinition::optionFactory().
  558. /// I think it should be OptionUint8Array, not OptionGeneric
  559. // Option ORO not found. Don't do anything then.
  560. if (!oro) {
  561. return;
  562. }
  563. boost::shared_ptr<OptionVendor> vendor_rsp(new OptionVendor(Option::V4, vendor_id));
  564. // Get the list of options that client requested.
  565. bool added = false;
  566. const OptionBuffer& requested_opts = oro->getData();
  567. for (OptionBuffer::const_iterator code = requested_opts.begin();
  568. code != requested_opts.end(); ++code) {
  569. Subnet::OptionDescriptor desc = subnet->getVendorOptionDescriptor(vendor_id, *code);
  570. if (desc.option) {
  571. vendor_rsp->addOption(desc.option);
  572. added = true;
  573. }
  574. }
  575. if (added) {
  576. answer->addOption(vendor_rsp);
  577. }
  578. }
  579. void
  580. Dhcpv4Srv::appendBasicOptions(const Pkt4Ptr& question, Pkt4Ptr& msg) {
  581. // Identify options that we always want to send to the
  582. // client (if they are configured).
  583. static const uint16_t required_options[] = {
  584. DHO_SUBNET_MASK,
  585. DHO_ROUTERS,
  586. DHO_DOMAIN_NAME_SERVERS,
  587. DHO_DOMAIN_NAME };
  588. static size_t required_options_size =
  589. sizeof(required_options) / sizeof(required_options[0]);
  590. // Get the subnet.
  591. Subnet4Ptr subnet = selectSubnet(question);
  592. if (!subnet) {
  593. return;
  594. }
  595. // Try to find all 'required' options in the outgoing
  596. // message. Those that are not present will be added.
  597. for (int i = 0; i < required_options_size; ++i) {
  598. OptionPtr opt = msg->getOption(required_options[i]);
  599. if (!opt) {
  600. // Check whether option has been configured.
  601. Subnet::OptionDescriptor desc =
  602. subnet->getOptionDescriptor("dhcp4", required_options[i]);
  603. if (desc.option) {
  604. msg->addOption(desc.option);
  605. }
  606. }
  607. }
  608. }
  609. void
  610. Dhcpv4Srv::assignLease(const Pkt4Ptr& question, Pkt4Ptr& answer) {
  611. // We need to select a subnet the client is connected in.
  612. Subnet4Ptr subnet = selectSubnet(question);
  613. if (!subnet) {
  614. // This particular client is out of luck today. We do not have
  615. // information about the subnet he is connected to. This likely means
  616. // misconfiguration of the server (or some relays). We will continue to
  617. // process this message, but our response will be almost useless: no
  618. // addresses or prefixes, no subnet specific configuration etc. The only
  619. // thing this client can get is some global information (like DNS
  620. // servers).
  621. // perhaps this should be logged on some higher level? This is most likely
  622. // configuration bug.
  623. LOG_ERROR(dhcp4_logger, DHCP4_SUBNET_SELECTION_FAILED)
  624. .arg(question->getRemoteAddr().toText())
  625. .arg(serverReceivedPacketName(question->getType()));
  626. answer->setType(DHCPNAK);
  627. answer->setYiaddr(IOAddress("0.0.0.0"));
  628. return;
  629. }
  630. LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL_DATA, DHCP4_SUBNET_SELECTED)
  631. .arg(subnet->toText());
  632. // Get client-id option
  633. ClientIdPtr client_id;
  634. OptionPtr opt = question->getOption(DHO_DHCP_CLIENT_IDENTIFIER);
  635. if (opt) {
  636. client_id = ClientIdPtr(new ClientId(opt->getData()));
  637. }
  638. // client-id is not mandatory in DHCPv4
  639. IOAddress hint = question->getYiaddr();
  640. HWAddrPtr hwaddr = question->getHWAddr();
  641. // "Fake" allocation is processing of DISCOVER message. We pretend to do an
  642. // allocation, but we do not put the lease in the database. That is ok,
  643. // because we do not guarantee that the user will get that exact lease. If
  644. // the user selects this server to do actual allocation (i.e. sends REQUEST)
  645. // it should include this hint. That will help us during the actual lease
  646. // allocation.
  647. bool fake_allocation = (question->getType() == DHCPDISCOVER);
  648. CalloutHandlePtr callout_handle = getCalloutHandle(question);
  649. // Use allocation engine to pick a lease for this client. Allocation engine
  650. // will try to honour the hint, but it is just a hint - some other address
  651. // may be used instead. If fake_allocation is set to false, the lease will
  652. // be inserted into the LeaseMgr as well.
  653. // @todo pass the actual FQDN data.
  654. Lease4Ptr old_lease;
  655. Lease4Ptr lease = alloc_engine_->allocateLease4(subnet, client_id, hwaddr,
  656. hint, false, false, "",
  657. fake_allocation,
  658. callout_handle,
  659. old_lease);
  660. if (lease) {
  661. // We have a lease! Let's set it in the packet and send it back to
  662. // the client.
  663. LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, fake_allocation?
  664. DHCP4_LEASE_ADVERT:DHCP4_LEASE_ALLOC)
  665. .arg(lease->addr_.toText())
  666. .arg(client_id?client_id->toText():"(no client-id)")
  667. .arg(hwaddr?hwaddr->toText():"(no hwaddr info)");
  668. answer->setYiaddr(lease->addr_);
  669. // IP Address Lease time (type 51)
  670. opt = OptionPtr(new Option(Option::V4, DHO_DHCP_LEASE_TIME));
  671. opt->setUint32(lease->valid_lft_);
  672. answer->addOption(opt);
  673. // Router (type 3)
  674. Subnet::OptionDescriptor opt_routers =
  675. subnet->getOptionDescriptor("dhcp4", DHO_ROUTERS);
  676. if (opt_routers.option) {
  677. answer->addOption(opt_routers.option);
  678. }
  679. // Subnet mask (type 1)
  680. answer->addOption(getNetmaskOption(subnet));
  681. // @todo: send renew timer option (T1, option 58)
  682. // @todo: send rebind timer option (T2, option 59)
  683. } else {
  684. // Allocation engine did not allocate a lease. The engine logged
  685. // cause of that failure. The only thing left is to insert
  686. // status code to pass the sad news to the client.
  687. LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, fake_allocation?
  688. DHCP4_LEASE_ADVERT_FAIL:DHCP4_LEASE_ALLOC_FAIL)
  689. .arg(client_id?client_id->toText():"(no client-id)")
  690. .arg(hwaddr?hwaddr->toText():"(no hwaddr info)")
  691. .arg(hint.toText());
  692. answer->setType(DHCPNAK);
  693. answer->setYiaddr(IOAddress("0.0.0.0"));
  694. }
  695. }
  696. void
  697. Dhcpv4Srv::adjustRemoteAddr(const Pkt4Ptr& question, Pkt4Ptr& msg) {
  698. // Let's create static objects representing zeroed and broadcast
  699. // addresses. We will use them further in this function to test
  700. // other addresses against them. Since they are static, they will
  701. // be created only once.
  702. static const IOAddress zero_addr("0.0.0.0");
  703. static const IOAddress bcast_addr("255.255.255.255");
  704. // If received relayed message, server responds to the relay address.
  705. if (question->getGiaddr() != zero_addr) {
  706. msg->setRemoteAddr(question->getGiaddr());
  707. // If giaddr is 0 but client set ciaddr, server should unicast the
  708. // response to ciaddr.
  709. } else if (question->getCiaddr() != zero_addr) {
  710. msg->setRemoteAddr(question->getCiaddr());
  711. // We can't unicast the response to the client when sending NAK,
  712. // because we haven't allocated address for him. Therefore,
  713. // NAK is broadcast.
  714. } else if (msg->getType() == DHCPNAK) {
  715. msg->setRemoteAddr(bcast_addr);
  716. // If yiaddr is set it means that we have created a lease for a client.
  717. } else if (msg->getYiaddr() != zero_addr) {
  718. // If the broadcast bit is set in the flags field, we have to
  719. // send the response to broadcast address. Client may have requested it
  720. // because it doesn't support reception of messages on the interface
  721. // which doesn't have an address assigned. The other case when response
  722. // must be broadcasted is when our server does not support responding
  723. // directly to a client without address assigned.
  724. const bool bcast_flag = ((question->getFlags() & Pkt4::FLAG_BROADCAST_MASK) != 0);
  725. if (!IfaceMgr::instance().isDirectResponseSupported() || bcast_flag) {
  726. msg->setRemoteAddr(bcast_addr);
  727. // Client cleared the broadcast bit and we support direct responses
  728. // so we should unicast the response to a newly allocated address -
  729. // yiaddr.
  730. } else {
  731. msg->setRemoteAddr(msg->getYiaddr());
  732. }
  733. // In most cases, we should have the remote address found already. If we
  734. // found ourselves at this point, the rational thing to do is to respond
  735. // to the address we got the query from.
  736. } else {
  737. msg->setRemoteAddr(question->getRemoteAddr());
  738. }
  739. }
  740. OptionPtr
  741. Dhcpv4Srv::getNetmaskOption(const Subnet4Ptr& subnet) {
  742. uint32_t netmask = getNetmask4(subnet->get().second);
  743. OptionPtr opt(new OptionInt<uint32_t>(Option::V4,
  744. DHO_SUBNET_MASK, netmask));
  745. return (opt);
  746. }
  747. Pkt4Ptr
  748. Dhcpv4Srv::processDiscover(Pkt4Ptr& discover) {
  749. sanityCheck(discover, FORBIDDEN);
  750. Pkt4Ptr offer = Pkt4Ptr
  751. (new Pkt4(DHCPOFFER, discover->getTransid()));
  752. copyDefaultFields(discover, offer);
  753. appendDefaultOptions(offer, DHCPOFFER);
  754. appendRequestedOptions(discover, offer);
  755. appendRequestedVendorOptions(discover, offer);
  756. assignLease(discover, offer);
  757. // There are a few basic options that we always want to
  758. // include in the response. If client did not request
  759. // them we append them for him.
  760. appendBasicOptions(discover, offer);
  761. return (offer);
  762. }
  763. Pkt4Ptr
  764. Dhcpv4Srv::processRequest(Pkt4Ptr& request) {
  765. /// @todo Uncomment this (see ticket #3116)
  766. // sanityCheck(request, MANDATORY);
  767. Pkt4Ptr ack = Pkt4Ptr
  768. (new Pkt4(DHCPACK, request->getTransid()));
  769. copyDefaultFields(request, ack);
  770. appendDefaultOptions(ack, DHCPACK);
  771. appendRequestedOptions(request, ack);
  772. appendRequestedVendorOptions(request, ack);
  773. // Note that we treat REQUEST message uniformly, regardless if this is a
  774. // first request (requesting for new address), renewing existing address
  775. // or even rebinding.
  776. assignLease(request, ack);
  777. // There are a few basic options that we always want to
  778. // include in the response. If client did not request
  779. // them we append them for him.
  780. appendBasicOptions(request, ack);
  781. return (ack);
  782. }
  783. void
  784. Dhcpv4Srv::processRelease(Pkt4Ptr& release) {
  785. /// @todo Uncomment this (see ticket #3116)
  786. // sanityCheck(release, MANDATORY);
  787. // Try to find client-id
  788. ClientIdPtr client_id;
  789. OptionPtr opt = release->getOption(DHO_DHCP_CLIENT_IDENTIFIER);
  790. if (opt) {
  791. client_id = ClientIdPtr(new ClientId(opt->getData()));
  792. }
  793. try {
  794. // Do we have a lease for that particular address?
  795. Lease4Ptr lease = LeaseMgrFactory::instance().getLease4(release->getYiaddr());
  796. if (!lease) {
  797. // No such lease - bogus release
  798. LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_RELEASE_FAIL_NO_LEASE)
  799. .arg(release->getYiaddr().toText())
  800. .arg(release->getHWAddr()->toText())
  801. .arg(client_id ? client_id->toText() : "(no client-id)");
  802. return;
  803. }
  804. // Does the hardware address match? We don't want one client releasing
  805. // second client's leases.
  806. if (lease->hwaddr_ != release->getHWAddr()->hwaddr_) {
  807. // @todo: Print hwaddr from lease as part of ticket #2589
  808. LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_RELEASE_FAIL_WRONG_HWADDR)
  809. .arg(release->getYiaddr().toText())
  810. .arg(client_id ? client_id->toText() : "(no client-id)")
  811. .arg(release->getHWAddr()->toText());
  812. return;
  813. }
  814. // Does the lease have client-id info? If it has, then check it with what
  815. // the client sent us.
  816. if (lease->client_id_ && client_id && *lease->client_id_ != *client_id) {
  817. LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_RELEASE_FAIL_WRONG_CLIENT_ID)
  818. .arg(release->getYiaddr().toText())
  819. .arg(client_id->toText())
  820. .arg(lease->client_id_->toText());
  821. return;
  822. }
  823. bool skip = false;
  824. // Execute all callouts registered for lease4_release
  825. if (HooksManager::calloutsPresent(Hooks.hook_index_lease4_release_)) {
  826. CalloutHandlePtr callout_handle = getCalloutHandle(release);
  827. // Delete all previous arguments
  828. callout_handle->deleteAllArguments();
  829. // Pass the original packet
  830. callout_handle->setArgument("query4", release);
  831. // Pass the lease to be updated
  832. callout_handle->setArgument("lease4", lease);
  833. // Call all installed callouts
  834. HooksManager::callCallouts(Hooks.hook_index_lease4_release_,
  835. *callout_handle);
  836. // Callouts decided to skip the next processing step. The next
  837. // processing step would to send the packet, so skip at this
  838. // stage means "drop response".
  839. if (callout_handle->getSkip()) {
  840. skip = true;
  841. LOG_DEBUG(dhcp4_logger, DBG_DHCP4_HOOKS,
  842. DHCP4_HOOK_LEASE4_RELEASE_SKIP);
  843. }
  844. }
  845. // Ok, hw and client-id match - let's release the lease.
  846. if (!skip) {
  847. bool success = LeaseMgrFactory::instance().deleteLease(lease->addr_);
  848. if (success) {
  849. // Release successful - we're done here
  850. LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_RELEASE)
  851. .arg(lease->addr_.toText())
  852. .arg(client_id ? client_id->toText() : "(no client-id)")
  853. .arg(release->getHWAddr()->toText());
  854. } else {
  855. // Release failed -
  856. LOG_ERROR(dhcp4_logger, DHCP4_RELEASE_FAIL)
  857. .arg(lease->addr_.toText())
  858. .arg(client_id ? client_id->toText() : "(no client-id)")
  859. .arg(release->getHWAddr()->toText());
  860. }
  861. }
  862. } catch (const isc::Exception& ex) {
  863. // Rethrow the exception with a bit more data.
  864. LOG_ERROR(dhcp4_logger, DHCP4_RELEASE_EXCEPTION)
  865. .arg(ex.what())
  866. .arg(release->getYiaddr());
  867. }
  868. }
  869. void
  870. Dhcpv4Srv::processDecline(Pkt4Ptr& /* decline */) {
  871. /// @todo Implement this (also see ticket #3116)
  872. }
  873. Pkt4Ptr
  874. Dhcpv4Srv::processInform(Pkt4Ptr& inform) {
  875. /// @todo Implement this for real. (also see ticket #3116)
  876. return (inform);
  877. }
  878. const char*
  879. Dhcpv4Srv::serverReceivedPacketName(uint8_t type) {
  880. static const char* DISCOVER = "DISCOVER";
  881. static const char* REQUEST = "REQUEST";
  882. static const char* RELEASE = "RELEASE";
  883. static const char* DECLINE = "DECLINE";
  884. static const char* INFORM = "INFORM";
  885. static const char* UNKNOWN = "UNKNOWN";
  886. switch (type) {
  887. case DHCPDISCOVER:
  888. return (DISCOVER);
  889. case DHCPREQUEST:
  890. return (REQUEST);
  891. case DHCPRELEASE:
  892. return (RELEASE);
  893. case DHCPDECLINE:
  894. return (DECLINE);
  895. case DHCPINFORM:
  896. return (INFORM);
  897. default:
  898. ;
  899. }
  900. return (UNKNOWN);
  901. }
  902. Subnet4Ptr
  903. Dhcpv4Srv::selectSubnet(const Pkt4Ptr& question) {
  904. Subnet4Ptr subnet;
  905. // Is this relayed message?
  906. IOAddress relay = question->getGiaddr();
  907. static const IOAddress notset("0.0.0.0");
  908. if (relay != notset) {
  909. // Yes: Use relay address to select subnet
  910. subnet = CfgMgr::instance().getSubnet4(relay);
  911. } else {
  912. // No: Use client's address to select subnet
  913. subnet = CfgMgr::instance().getSubnet4(question->getRemoteAddr());
  914. }
  915. /// @todo Implement getSubnet4(interface-name)
  916. // Let's execute all callouts registered for subnet4_select
  917. if (HooksManager::calloutsPresent(hook_index_subnet4_select_)) {
  918. CalloutHandlePtr callout_handle = getCalloutHandle(question);
  919. // We're reusing callout_handle from previous calls
  920. callout_handle->deleteAllArguments();
  921. // Set new arguments
  922. callout_handle->setArgument("query4", question);
  923. callout_handle->setArgument("subnet4", subnet);
  924. callout_handle->setArgument("subnet4collection",
  925. CfgMgr::instance().getSubnets4());
  926. // Call user (and server-side) callouts
  927. HooksManager::callCallouts(hook_index_subnet4_select_,
  928. *callout_handle);
  929. // Callouts decided to skip this step. This means that no subnet will be
  930. // selected. Packet processing will continue, but it will be severly
  931. // limited (i.e. only global options will be assigned)
  932. if (callout_handle->getSkip()) {
  933. LOG_DEBUG(dhcp4_logger, DBG_DHCP4_HOOKS,
  934. DHCP4_HOOK_SUBNET4_SELECT_SKIP);
  935. return (Subnet4Ptr());
  936. }
  937. // Use whatever subnet was specified by the callout
  938. callout_handle->getArgument("subnet4", subnet);
  939. }
  940. return (subnet);
  941. }
  942. void
  943. Dhcpv4Srv::sanityCheck(const Pkt4Ptr& pkt, RequirementLevel serverid) {
  944. OptionPtr server_id = pkt->getOption(DHO_DHCP_SERVER_IDENTIFIER);
  945. switch (serverid) {
  946. case FORBIDDEN:
  947. if (server_id) {
  948. isc_throw(RFCViolation, "Server-id option was not expected, but "
  949. << "received in " << serverReceivedPacketName(pkt->getType()));
  950. }
  951. break;
  952. case MANDATORY:
  953. if (!server_id) {
  954. isc_throw(RFCViolation, "Server-id option was expected, but not "
  955. " received in message "
  956. << serverReceivedPacketName(pkt->getType()));
  957. }
  958. break;
  959. case OPTIONAL:
  960. // do nothing here
  961. ;
  962. }
  963. // If there is HWAddress set and it is non-empty, then we're good
  964. if (pkt->getHWAddr() && !pkt->getHWAddr()->hwaddr_.empty()) {
  965. return;
  966. }
  967. // There has to be something to uniquely identify the client:
  968. // either non-zero MAC address or client-id option present (or both)
  969. OptionPtr client_id = pkt->getOption(DHO_DHCP_CLIENT_IDENTIFIER);
  970. // If there's no client-id (or a useless one is provided, i.e. 0 length)
  971. if (!client_id || client_id->len() == client_id->getHeaderLen()) {
  972. isc_throw(RFCViolation, "Missing or useless client-id and no HW address "
  973. " provided in message "
  974. << serverReceivedPacketName(pkt->getType()));
  975. }
  976. }
  977. void
  978. Dhcpv4Srv::openActiveSockets(const uint16_t port,
  979. const bool use_bcast) {
  980. IfaceMgr::instance().closeSockets();
  981. // Get the reference to the collection of interfaces. This reference should
  982. // be valid as long as the program is run because IfaceMgr is a singleton.
  983. // Therefore we can safely iterate over instances of all interfaces and
  984. // modify their flags. Here we modify flags which indicate whether socket
  985. // should be open for a particular interface or not.
  986. const IfaceMgr::IfaceCollection& ifaces = IfaceMgr::instance().getIfaces();
  987. for (IfaceMgr::IfaceCollection::const_iterator iface = ifaces.begin();
  988. iface != ifaces.end(); ++iface) {
  989. Iface* iface_ptr = IfaceMgr::instance().getIface(iface->getName());
  990. if (iface_ptr == NULL) {
  991. isc_throw(isc::Unexpected, "Interface Manager returned NULL"
  992. << " instance of the interface when DHCPv4 server was"
  993. << " trying to reopen sockets after reconfiguration");
  994. }
  995. if (CfgMgr::instance().isActiveIface(iface->getName())) {
  996. iface_ptr->inactive4_ = false;
  997. LOG_INFO(dhcp4_logger, DHCP4_ACTIVATE_INTERFACE)
  998. .arg(iface->getFullName());
  999. } else {
  1000. // For deactivating interface, it should be sufficient to log it
  1001. // on the debug level because it is more useful to know what
  1002. // interface is activated which is logged on the info level.
  1003. LOG_DEBUG(dhcp4_logger, DBG_DHCP4_BASIC,
  1004. DHCP4_DEACTIVATE_INTERFACE).arg(iface->getName());
  1005. iface_ptr->inactive4_ = true;
  1006. }
  1007. }
  1008. // Let's reopen active sockets. openSockets4 will check internally whether
  1009. // sockets are marked active or inactive.
  1010. // @todo Optimization: we should not reopen all sockets but rather select
  1011. // those that have been affected by the new configuration.
  1012. if (!IfaceMgr::instance().openSockets4(port, use_bcast)) {
  1013. LOG_WARN(dhcp4_logger, DHCP4_NO_SOCKETS_OPEN);
  1014. }
  1015. }
  1016. size_t
  1017. Dhcpv4Srv::unpackOptions(const OptionBuffer& buf,
  1018. const std::string& option_space,
  1019. isc::dhcp::OptionCollection& options) {
  1020. size_t offset = 0;
  1021. OptionDefContainer option_defs;
  1022. if (option_space == "dhcp4") {
  1023. // Get the list of stdandard option definitions.
  1024. option_defs = LibDHCP::getOptionDefs(Option::V4);
  1025. } else if (!option_space.empty()) {
  1026. OptionDefContainerPtr option_defs_ptr =
  1027. CfgMgr::instance().getOptionDefs(option_space);
  1028. if (option_defs_ptr != NULL) {
  1029. option_defs = *option_defs_ptr;
  1030. }
  1031. }
  1032. // Get the search index #1. It allows to search for option definitions
  1033. // using option code.
  1034. const OptionDefContainerTypeIndex& idx = option_defs.get<1>();
  1035. // The buffer being read comprises a set of options, each starting with
  1036. // a one-byte type code and a one-byte length field.
  1037. while (offset + 1 <= buf.size()) {
  1038. uint8_t opt_type = buf[offset++];
  1039. // DHO_END is a special, one octet long option
  1040. if (opt_type == DHO_END)
  1041. return (offset); // just return. Don't need to add DHO_END option
  1042. // DHO_PAD is just a padding after DHO_END. Let's continue parsing
  1043. // in case we receive a message without DHO_END.
  1044. if (opt_type == DHO_PAD)
  1045. continue;
  1046. if (offset + 1 >= buf.size()) {
  1047. // opt_type must be cast to integer so as it is not treated as
  1048. // unsigned char value (a number is presented in error message).
  1049. isc_throw(OutOfRange, "Attempt to parse truncated option "
  1050. << static_cast<int>(opt_type));
  1051. }
  1052. uint8_t opt_len = buf[offset++];
  1053. if (offset + opt_len > buf.size()) {
  1054. isc_throw(OutOfRange, "Option parse failed. Tried to parse "
  1055. << offset + opt_len << " bytes from " << buf.size()
  1056. << "-byte long buffer.");
  1057. }
  1058. // Get all definitions with the particular option code. Note that option code
  1059. // is non-unique within this container however at this point we expect
  1060. // to get one option definition with the particular code. If more are
  1061. // returned we report an error.
  1062. const OptionDefContainerTypeRange& range = idx.equal_range(opt_type);
  1063. // Get the number of returned option definitions for the option code.
  1064. size_t num_defs = distance(range.first, range.second);
  1065. OptionPtr opt;
  1066. if (num_defs > 1) {
  1067. // Multiple options of the same code are not supported right now!
  1068. isc_throw(isc::Unexpected, "Internal error: multiple option definitions"
  1069. " for option type " << static_cast<int>(opt_type)
  1070. << " returned. Currently it is not supported to initialize"
  1071. << " multiple option definitions for the same option code."
  1072. << " This will be supported once support for option spaces"
  1073. << " is implemented");
  1074. } else if (num_defs == 0) {
  1075. opt = OptionPtr(new Option(Option::V4, opt_type,
  1076. buf.begin() + offset,
  1077. buf.begin() + offset + opt_len));
  1078. opt->setEncapsulatedSpace("dhcp4");
  1079. } else {
  1080. // The option definition has been found. Use it to create
  1081. // the option instance from the provided buffer chunk.
  1082. const OptionDefinitionPtr& def = *(range.first);
  1083. assert(def);
  1084. opt = def->optionFactory(Option::V4, opt_type,
  1085. buf.begin() + offset,
  1086. buf.begin() + offset + opt_len,
  1087. boost::bind(&Dhcpv4Srv::unpackOptions,
  1088. this, _1, _2, _3));
  1089. }
  1090. options.insert(std::make_pair(opt_type, opt));
  1091. offset += opt_len;
  1092. }
  1093. return (offset);
  1094. }
  1095. } // namespace dhcp
  1096. } // namespace isc