dhcp4_srv.cc 42 KB

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