host.cc 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590
  1. // Copyright (C) 2014-2017 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this
  5. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
  6. #include <config.h>
  7. #include <dhcp/pkt4.h>
  8. #include <dhcpsrv/host.h>
  9. #include <util/encode/hex.h>
  10. #include <util/strutil.h>
  11. #include <asiolink/io_address.h>
  12. #include <exceptions/exceptions.h>
  13. #include <sstream>
  14. using namespace isc::data;
  15. using namespace isc::asiolink;
  16. namespace isc {
  17. namespace dhcp {
  18. IPv6Resrv::IPv6Resrv(const Type& type,
  19. const asiolink::IOAddress& prefix,
  20. const uint8_t prefix_len)
  21. : type_(type), prefix_(asiolink::IOAddress("::")), prefix_len_(128) {
  22. // Validate and set the actual values.
  23. set(type, prefix, prefix_len);
  24. }
  25. void
  26. IPv6Resrv::set(const Type& type, const asiolink::IOAddress& prefix,
  27. const uint8_t prefix_len) {
  28. if (!prefix.isV6() || prefix.isV6Multicast()) {
  29. isc_throw(isc::BadValue, "invalid prefix '" << prefix
  30. << "' for new IPv6 reservation");
  31. } else if (prefix_len > 128) {
  32. isc_throw(isc::BadValue, "invalid prefix length '"
  33. << static_cast<int>(prefix_len)
  34. << "' for new IPv6 reservation");
  35. } else if ((type == TYPE_NA) && (prefix_len != 128)) {
  36. isc_throw(isc::BadValue, "invalid prefix length '"
  37. << static_cast<int>(prefix_len)
  38. << "' for reserved IPv6 address, expected 128");
  39. }
  40. type_ = type;
  41. prefix_ = prefix;
  42. prefix_len_ = prefix_len;
  43. }
  44. std::string
  45. IPv6Resrv::toText() const {
  46. std::ostringstream s;
  47. s << prefix_;
  48. // For PD, append prefix length.
  49. if (getType() == TYPE_PD) {
  50. s << "/" << static_cast<int>(prefix_len_);
  51. }
  52. return (s.str());
  53. }
  54. bool
  55. IPv6Resrv::operator==(const IPv6Resrv& other) const {
  56. return (type_ == other.type_ &&
  57. prefix_ == other.prefix_ &&
  58. prefix_len_ == other.prefix_len_);
  59. }
  60. bool
  61. IPv6Resrv::operator!=(const IPv6Resrv& other) const {
  62. return (!operator==(other));
  63. }
  64. Host::Host(const uint8_t* identifier, const size_t identifier_len,
  65. const IdentifierType& identifier_type,
  66. const SubnetID ipv4_subnet_id, const SubnetID ipv6_subnet_id,
  67. const asiolink::IOAddress& ipv4_reservation,
  68. const std::string& hostname,
  69. const std::string& dhcp4_client_classes,
  70. const std::string& dhcp6_client_classes,
  71. const asiolink::IOAddress& next_server,
  72. const std::string& server_host_name,
  73. const std::string& boot_file_name)
  74. : identifier_type_(identifier_type),
  75. identifier_value_(), ipv4_subnet_id_(ipv4_subnet_id),
  76. ipv6_subnet_id_(ipv6_subnet_id),
  77. ipv4_reservation_(asiolink::IOAddress::IPV4_ZERO_ADDRESS()),
  78. hostname_(hostname), dhcp4_client_classes_(dhcp4_client_classes),
  79. dhcp6_client_classes_(dhcp6_client_classes),
  80. next_server_(asiolink::IOAddress::IPV4_ZERO_ADDRESS()),
  81. server_host_name_(server_host_name), boot_file_name_(boot_file_name),
  82. host_id_(0), cfg_option4_(new CfgOption()), cfg_option6_(new CfgOption()) {
  83. // Initialize host identifier.
  84. setIdentifier(identifier, identifier_len, identifier_type);
  85. if (!ipv4_reservation.isV4Zero()) {
  86. // Validate and set IPv4 address reservation.
  87. setIPv4Reservation(ipv4_reservation);
  88. }
  89. if (!next_server.isV4Zero()) {
  90. // Validate and set next server address.
  91. setNextServer(next_server);
  92. }
  93. }
  94. Host::Host(const std::string& identifier, const std::string& identifier_name,
  95. const SubnetID ipv4_subnet_id, const SubnetID ipv6_subnet_id,
  96. const asiolink::IOAddress& ipv4_reservation,
  97. const std::string& hostname,
  98. const std::string& dhcp4_client_classes,
  99. const std::string& dhcp6_client_classes,
  100. const asiolink::IOAddress& next_server,
  101. const std::string& server_host_name,
  102. const std::string& boot_file_name)
  103. : identifier_type_(IDENT_HWADDR),
  104. identifier_value_(), ipv4_subnet_id_(ipv4_subnet_id),
  105. ipv6_subnet_id_(ipv6_subnet_id),
  106. ipv4_reservation_(asiolink::IOAddress::IPV4_ZERO_ADDRESS()),
  107. hostname_(hostname), dhcp4_client_classes_(dhcp4_client_classes),
  108. dhcp6_client_classes_(dhcp6_client_classes),
  109. next_server_(asiolink::IOAddress::IPV4_ZERO_ADDRESS()),
  110. server_host_name_(server_host_name), boot_file_name_(boot_file_name),
  111. host_id_(0), cfg_option4_(new CfgOption()), cfg_option6_(new CfgOption()) {
  112. // Initialize host identifier.
  113. setIdentifier(identifier, identifier_name);
  114. if (!ipv4_reservation.isV4Zero()) {
  115. // Validate and set IPv4 address reservation.
  116. setIPv4Reservation(ipv4_reservation);
  117. }
  118. if (!next_server.isV4Zero()) {
  119. // Validate and set next server address.
  120. setNextServer(next_server);
  121. }
  122. }
  123. const std::vector<uint8_t>&
  124. Host::getIdentifier() const {
  125. return (identifier_value_);
  126. }
  127. Host::IdentifierType
  128. Host::getIdentifierType() const {
  129. return (identifier_type_);
  130. }
  131. Host::IdentifierType
  132. Host::getIdentifierType(const std::string& identifier_name) {
  133. if (identifier_name == "hw-address") {
  134. return (IDENT_HWADDR);
  135. } else if (identifier_name == "duid") {
  136. return (IDENT_DUID);
  137. } else if (identifier_name == "circuit-id") {
  138. return (IDENT_CIRCUIT_ID);
  139. } else if (identifier_name == "client-id") {
  140. return (IDENT_CLIENT_ID);
  141. } else if (identifier_name == "flex-id") {
  142. return (IDENT_FLEX);
  143. } else {
  144. isc_throw(isc::BadValue, "invalid client identifier type '"
  145. << identifier_name << "'");
  146. }
  147. }
  148. HWAddrPtr
  149. Host::getHWAddress() const {
  150. return ((identifier_type_ == IDENT_HWADDR) ?
  151. HWAddrPtr(new HWAddr(identifier_value_, HTYPE_ETHER)) : HWAddrPtr());
  152. }
  153. DuidPtr
  154. Host::getDuid() const {
  155. return ((identifier_type_ == IDENT_DUID) ?
  156. DuidPtr(new DUID(identifier_value_)) : DuidPtr());
  157. }
  158. std::string
  159. Host::getIdentifierAsText() const {
  160. return (getIdentifierAsText(identifier_type_, &identifier_value_[0],
  161. identifier_value_.size()));
  162. }
  163. std::string
  164. Host::getIdentifierAsText(const IdentifierType& type, const uint8_t* value,
  165. const size_t length) {
  166. // Convert identifier into <type>=<value> form.
  167. std::ostringstream s;
  168. switch (type) {
  169. case IDENT_HWADDR:
  170. s << "hwaddr";
  171. break;
  172. case IDENT_DUID:
  173. s << "duid";
  174. break;
  175. case IDENT_CIRCUIT_ID:
  176. s << "circuit-id";
  177. break;
  178. case IDENT_CLIENT_ID:
  179. s << "client-id";
  180. break;
  181. case IDENT_FLEX:
  182. s << "flex-id";
  183. break;
  184. default:
  185. // This should never happen actually, unless we add new identifier
  186. // and forget to add a case for it above.
  187. s << "(invalid-type)";
  188. }
  189. std::vector<uint8_t> vec(value, value + length);
  190. s << "=" << (length > 0 ? util::encode::encodeHex(vec) : "(null)");
  191. return (s.str());
  192. }
  193. std::string
  194. Host::getIdentifierName(const IdentifierType& type) {
  195. switch (type) {
  196. case Host::IDENT_HWADDR:
  197. return ("hw-address");
  198. case Host::IDENT_DUID:
  199. return ("duid");
  200. case Host::IDENT_CIRCUIT_ID:
  201. return ("circuit-id");
  202. case Host::IDENT_CLIENT_ID:
  203. return ("client-id");
  204. case Host::IDENT_FLEX:
  205. return ("flex-id");
  206. default:
  207. ;
  208. }
  209. return ("(unknown)");
  210. }
  211. void
  212. Host::setIdentifier(const uint8_t* identifier, const size_t len,
  213. const IdentifierType& type) {
  214. if (len < 1) {
  215. isc_throw(BadValue, "invalid client identifier length 0");
  216. }
  217. identifier_type_ = type;
  218. identifier_value_.assign(identifier, identifier + len);
  219. }
  220. void
  221. Host::setIdentifier(const std::string& identifier, const std::string& name) {
  222. // Empty identifier is not allowed.
  223. if (identifier.empty()) {
  224. isc_throw(isc::BadValue, "empty host identifier used");
  225. }
  226. // Set identifier type.
  227. identifier_type_ = getIdentifierType(name);
  228. // Identifier value can either be specified as string of hexadecimal
  229. // digits or a string in quotes. The latter is copied to a vector excluding
  230. // quote characters.
  231. // Try to convert the values in quotes into a vector of ASCII codes.
  232. // If the identifier lacks opening and closing quote, this will return
  233. // an empty value, in which case we'll try to decode it as a string of
  234. // hexadecimal digits.
  235. try {
  236. std::vector<uint8_t> binary = util::str::quotedStringToBinary(identifier);
  237. if (binary.empty()) {
  238. util::str::decodeFormattedHexString(identifier, binary);
  239. }
  240. // Successfully decoded the identifier, so let's use it.
  241. identifier_value_.swap(binary);
  242. } catch (...) {
  243. // The string doesn't match any known pattern, so we have to
  244. // report an error at this point.
  245. isc_throw(isc::BadValue, "invalid host identifier value '"
  246. << identifier << "'");
  247. }
  248. }
  249. void
  250. Host::setIPv4Reservation(const asiolink::IOAddress& address) {
  251. if (!address.isV4()) {
  252. isc_throw(isc::BadValue, "address '" << address << "' is not a valid"
  253. " IPv4 address");
  254. } else if (address.isV4Zero() || address.isV4Bcast()) {
  255. isc_throw(isc::BadValue, "must not make reservation for the '"
  256. << address << "' address");
  257. }
  258. ipv4_reservation_ = address;
  259. }
  260. void
  261. Host::removeIPv4Reservation() {
  262. ipv4_reservation_ = asiolink::IOAddress::IPV4_ZERO_ADDRESS();
  263. }
  264. void
  265. Host::addReservation(const IPv6Resrv& reservation) {
  266. // Check if it is not duplicating existing reservation.
  267. if (hasReservation(reservation)) {
  268. isc_throw(isc::InvalidOperation, "failed on attempt to add a duplicated"
  269. " host reservation for " << reservation.toText());
  270. }
  271. // Add it.
  272. ipv6_reservations_.insert(IPv6ResrvTuple(reservation.getType(),
  273. reservation));
  274. }
  275. IPv6ResrvRange
  276. Host::getIPv6Reservations(const IPv6Resrv::Type& type) const {
  277. return (ipv6_reservations_.equal_range(type));
  278. }
  279. IPv6ResrvRange
  280. Host::getIPv6Reservations() const {
  281. return (IPv6ResrvRange(ipv6_reservations_.begin(),
  282. ipv6_reservations_.end()));
  283. }
  284. bool
  285. Host::hasIPv6Reservation() const {
  286. return (!ipv6_reservations_.empty());
  287. }
  288. bool
  289. Host::hasReservation(const IPv6Resrv& reservation) const {
  290. IPv6ResrvRange reservations = getIPv6Reservations(reservation.getType());
  291. if (std::distance(reservations.first, reservations.second) > 0) {
  292. for (IPv6ResrvIterator it = reservations.first;
  293. it != reservations.second; ++it) {
  294. if (it->second == reservation) {
  295. return (true);
  296. }
  297. }
  298. }
  299. // No matching reservations found.
  300. return (false);
  301. }
  302. void
  303. Host::addClientClass4(const std::string& class_name) {
  304. addClientClassInternal(dhcp4_client_classes_, class_name);
  305. }
  306. void
  307. Host::addClientClass6(const std::string& class_name) {
  308. addClientClassInternal(dhcp6_client_classes_, class_name);
  309. }
  310. void
  311. Host::addClientClassInternal(ClientClasses& classes,
  312. const std::string& class_name) {
  313. std::string trimmed = util::str::trim(class_name);
  314. if (!trimmed.empty()) {
  315. classes.insert(ClientClass(trimmed));
  316. }
  317. }
  318. void
  319. Host::setNextServer(const asiolink::IOAddress& next_server) {
  320. if (!next_server.isV4()) {
  321. isc_throw(isc::BadValue, "next server address '" << next_server
  322. << "' is not a valid IPv4 address");
  323. } else if (next_server.isV4Bcast()) {
  324. isc_throw(isc::BadValue, "invalid next server address '"
  325. << next_server << "'");
  326. }
  327. next_server_ = next_server;
  328. }
  329. void
  330. Host::setServerHostname(const std::string& server_host_name) {
  331. if (server_host_name.size() > Pkt4::MAX_SNAME_LEN - 1) {
  332. isc_throw(isc::BadValue, "server hostname length must not exceed "
  333. << (Pkt4::MAX_SNAME_LEN - 1));
  334. }
  335. server_host_name_ = server_host_name;
  336. }
  337. void
  338. Host::setBootFileName(const std::string& boot_file_name) {
  339. if (boot_file_name.size() > Pkt4::MAX_FILE_LEN - 1) {
  340. isc_throw(isc::BadValue, "boot file length must not exceed "
  341. << (Pkt4::MAX_FILE_LEN - 1));
  342. }
  343. boot_file_name_ = boot_file_name;
  344. }
  345. ElementPtr
  346. Host::toElement4() const {
  347. // Prepare the map
  348. ElementPtr map = Element::createMap();
  349. // Set the identifier
  350. Host::IdentifierType id_type = getIdentifierType();
  351. if (id_type == Host::IDENT_HWADDR) {
  352. HWAddrPtr hwaddr = getHWAddress();
  353. map->set("hw-address", Element::create(hwaddr->toText(false)));
  354. } else if (id_type == Host::IDENT_DUID) {
  355. DuidPtr duid = getDuid();
  356. map->set("duid", Element::create(duid->toText()));
  357. } else if (id_type == Host::IDENT_CIRCUIT_ID) {
  358. const std::vector<uint8_t>& bin = getIdentifier();
  359. std::string circuit_id = util::encode::encodeHex(bin);
  360. map->set("circuit-id", Element::create(circuit_id));
  361. } else if (id_type == Host::IDENT_CLIENT_ID) {
  362. const std::vector<uint8_t>& bin = getIdentifier();
  363. std::string client_id = util::encode::encodeHex(bin);
  364. map->set("client-id", Element::create(client_id));
  365. } else if (id_type == Host::IDENT_FLEX) {
  366. const std::vector<uint8_t>& bin = getIdentifier();
  367. std::string flex = util::encode::encodeHex(bin);
  368. map->set("flex-id", Element::create(flex));
  369. } else {
  370. isc_throw(ToElementError, "invalid identifier type: " << id_type);
  371. }
  372. // Set the reservation
  373. const IOAddress& address = getIPv4Reservation();
  374. map->set("ip-address", Element::create(address.toText()));
  375. // Set the hostname
  376. const std::string& hostname = getHostname();
  377. map->set("hostname", Element::create(hostname));
  378. // Set next-server
  379. const IOAddress& next_server = getNextServer();
  380. map->set("next-server", Element::create(next_server.toText()));
  381. // Set server-hostname
  382. const std::string& server_hostname = getServerHostname();
  383. map->set("server-hostname", Element::create(server_hostname));
  384. // Set boot-file-name
  385. const std::string& boot_file_name = getBootFileName();
  386. map->set("boot-file-name", Element::create(boot_file_name));
  387. // Set client-classes
  388. const ClientClasses& cclasses = getClientClasses4();
  389. ElementPtr classes = Element::createList();
  390. for (ClientClasses::const_iterator cclass = cclasses.cbegin();
  391. cclass != cclasses.end(); ++cclass) {
  392. classes->add(Element::create(*cclass));
  393. }
  394. map->set("client-classes", classes);
  395. // Set option-data
  396. ConstCfgOptionPtr opts = getCfgOption4();
  397. map->set("option-data", opts->toElement());
  398. return (map);
  399. }
  400. ElementPtr
  401. Host::toElement6() const {
  402. // Prepare the map
  403. ElementPtr map = Element::createMap();
  404. // Set the identifier
  405. Host::IdentifierType id_type = getIdentifierType();
  406. if (id_type == Host::IDENT_HWADDR) {
  407. HWAddrPtr hwaddr = getHWAddress();
  408. map->set("hw-address", Element::create(hwaddr->toText(false)));
  409. } else if (id_type == Host::IDENT_DUID) {
  410. DuidPtr duid = getDuid();
  411. map->set("duid", Element::create(duid->toText()));
  412. } else if (id_type == Host::IDENT_CIRCUIT_ID) {
  413. isc_throw(ToElementError, "unexpected circuit-id DUID type");
  414. } else if (id_type == Host::IDENT_CLIENT_ID) {
  415. isc_throw(ToElementError, "unexpected client-id DUID type");
  416. } else if (id_type == Host::IDENT_FLEX) {
  417. const std::vector<uint8_t>& bin = getIdentifier();
  418. std::string flex = util::encode::encodeHex(bin);
  419. map->set("flex-id", Element::create(flex));
  420. } else {
  421. isc_throw(ToElementError, "invalid DUID type: " << id_type);
  422. }
  423. // Set reservations (ip-addresses)
  424. IPv6ResrvRange na_resv = getIPv6Reservations(IPv6Resrv::TYPE_NA);
  425. ElementPtr resvs = Element::createList();
  426. for (IPv6ResrvIterator resv = na_resv.first;
  427. resv != na_resv.second; ++resv) {
  428. resvs->add(Element::create(resv->second.toText()));
  429. }
  430. map->set("ip-addresses", resvs);
  431. // Set reservations (prefixes)
  432. IPv6ResrvRange pd_resv = getIPv6Reservations(IPv6Resrv::TYPE_PD);
  433. resvs = Element::createList();
  434. for (IPv6ResrvIterator resv = pd_resv.first;
  435. resv != pd_resv.second; ++resv) {
  436. resvs->add(Element::create(resv->second.toText()));
  437. }
  438. map->set("prefixes", resvs);
  439. // Set the hostname
  440. const std::string& hostname = getHostname();
  441. map->set("hostname", Element::create(hostname));
  442. // Set client-classes
  443. const ClientClasses& cclasses = getClientClasses6();
  444. ElementPtr classes = Element::createList();
  445. for (ClientClasses::const_iterator cclass = cclasses.cbegin();
  446. cclass != cclasses.end(); ++cclass) {
  447. classes->add(Element::create(*cclass));
  448. }
  449. map->set("client-classes", classes);
  450. // Set option-data
  451. ConstCfgOptionPtr opts = getCfgOption6();
  452. map->set("option-data", opts->toElement());
  453. return (map);
  454. }
  455. std::string
  456. Host::toText() const {
  457. std::ostringstream s;
  458. // Add HW address or DUID.
  459. s << getIdentifierAsText();
  460. // Add IPv4 subnet id if exists (non-zero).
  461. if (ipv4_subnet_id_) {
  462. s << " ipv4_subnet_id=" << ipv4_subnet_id_;
  463. }
  464. // Add IPv6 subnet id if exists (non-zero).
  465. if (ipv6_subnet_id_) {
  466. s << " ipv6_subnet_id=" << ipv6_subnet_id_;
  467. }
  468. // Add hostname.
  469. s << " hostname=" << (hostname_.empty() ? "(empty)" : hostname_);
  470. // Add IPv4 reservation.
  471. s << " ipv4_reservation=" << (ipv4_reservation_.isV4Zero() ? "(no)" :
  472. ipv4_reservation_.toText());
  473. // Add next server.
  474. s << " siaddr=" << (next_server_.isV4Zero() ? "(no)" :
  475. next_server_.toText());
  476. // Add server host name.
  477. s << " sname=" << (server_host_name_.empty() ? "(empty)" : server_host_name_);
  478. // Add boot file name.
  479. s << " file=" << (boot_file_name_.empty() ? "(empty)" : boot_file_name_);
  480. if (ipv6_reservations_.empty()) {
  481. s << " ipv6_reservations=(none)";
  482. } else {
  483. // Add all IPv6 reservations.
  484. for (IPv6ResrvIterator resrv = ipv6_reservations_.begin();
  485. resrv != ipv6_reservations_.end(); ++resrv) {
  486. s << " ipv6_reservation"
  487. << std::distance(ipv6_reservations_.begin(), resrv)
  488. << "=" << resrv->second.toText();
  489. }
  490. }
  491. // Add DHCPv4 client classes.
  492. for (ClientClasses::const_iterator cclass = dhcp4_client_classes_.begin();
  493. cclass != dhcp4_client_classes_.end(); ++cclass) {
  494. s << " dhcp4_class"
  495. << std::distance(dhcp4_client_classes_.begin(), cclass)
  496. << "=" << *cclass;
  497. }
  498. // Add DHCPv6 client classes.
  499. for (ClientClasses::const_iterator cclass = dhcp6_client_classes_.begin();
  500. cclass != dhcp6_client_classes_.end(); ++cclass) {
  501. s << " dhcp6_class"
  502. << std::distance(dhcp6_client_classes_.begin(), cclass)
  503. << "=" << *cclass;
  504. }
  505. return (s.str());
  506. }
  507. } // end of namespace isc::dhcp
  508. } // end of namespace isc