host.cc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463
  1. // Copyright (C) 2014-2016 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 <exceptions/exceptions.h>
  12. #include <sstream>
  13. namespace isc {
  14. namespace dhcp {
  15. IPv6Resrv::IPv6Resrv(const Type& type,
  16. const asiolink::IOAddress& prefix,
  17. const uint8_t prefix_len)
  18. : type_(type), prefix_(asiolink::IOAddress("::")), prefix_len_(128) {
  19. // Validate and set the actual values.
  20. set(type, prefix, prefix_len);
  21. }
  22. void
  23. IPv6Resrv::set(const Type& type, const asiolink::IOAddress& prefix,
  24. const uint8_t prefix_len) {
  25. if (!prefix.isV6() || prefix.isV6Multicast()) {
  26. isc_throw(isc::BadValue, "invalid prefix '" << prefix
  27. << "' for new IPv6 reservation");
  28. } else if (prefix_len > 128) {
  29. isc_throw(isc::BadValue, "invalid prefix length '"
  30. << static_cast<int>(prefix_len)
  31. << "' for new IPv6 reservation");
  32. } else if ((type == TYPE_NA) && (prefix_len != 128)) {
  33. isc_throw(isc::BadValue, "invalid prefix length '"
  34. << static_cast<int>(prefix_len)
  35. << "' for reserved IPv6 address, expected 128");
  36. }
  37. type_ = type;
  38. prefix_ = prefix;
  39. prefix_len_ = prefix_len;
  40. }
  41. std::string
  42. IPv6Resrv::toText() const {
  43. std::ostringstream s;
  44. s << prefix_;
  45. // For PD, append prefix length.
  46. if (getType() == TYPE_PD) {
  47. s << "/" << static_cast<int>(prefix_len_);
  48. }
  49. return (s.str());
  50. }
  51. bool
  52. IPv6Resrv::operator==(const IPv6Resrv& other) const {
  53. return (type_ == other.type_ &&
  54. prefix_ == other.prefix_ &&
  55. prefix_len_ == other.prefix_len_);
  56. }
  57. bool
  58. IPv6Resrv::operator!=(const IPv6Resrv& other) const {
  59. return (!operator==(other));
  60. }
  61. Host::Host(const uint8_t* identifier, const size_t identifier_len,
  62. const IdentifierType& identifier_type,
  63. const SubnetID ipv4_subnet_id, const SubnetID ipv6_subnet_id,
  64. const asiolink::IOAddress& ipv4_reservation,
  65. const std::string& hostname,
  66. const std::string& dhcp4_client_classes,
  67. const std::string& dhcp6_client_classes,
  68. const asiolink::IOAddress& next_server,
  69. const std::string& server_host_name,
  70. const std::string& boot_file_name)
  71. : identifier_type_(identifier_type),
  72. identifier_value_(), ipv4_subnet_id_(ipv4_subnet_id),
  73. ipv6_subnet_id_(ipv6_subnet_id),
  74. ipv4_reservation_(asiolink::IOAddress::IPV4_ZERO_ADDRESS()),
  75. hostname_(hostname), dhcp4_client_classes_(dhcp4_client_classes),
  76. dhcp6_client_classes_(dhcp6_client_classes),
  77. next_server_(asiolink::IOAddress::IPV4_ZERO_ADDRESS()),
  78. server_host_name_(server_host_name), boot_file_name_(boot_file_name),
  79. host_id_(0), cfg_option4_(new CfgOption()), cfg_option6_(new CfgOption()) {
  80. // Initialize host identifier.
  81. setIdentifier(identifier, identifier_len, identifier_type);
  82. if (!ipv4_reservation.isV4Zero()) {
  83. // Validate and set IPv4 address reservation.
  84. setIPv4Reservation(ipv4_reservation);
  85. }
  86. if (!next_server.isV4Zero()) {
  87. // Validate and set next server address.
  88. setNextServer(next_server);
  89. }
  90. }
  91. Host::Host(const std::string& identifier, const std::string& identifier_name,
  92. const SubnetID ipv4_subnet_id, const SubnetID ipv6_subnet_id,
  93. const asiolink::IOAddress& ipv4_reservation,
  94. const std::string& hostname,
  95. const std::string& dhcp4_client_classes,
  96. const std::string& dhcp6_client_classes,
  97. const asiolink::IOAddress& next_server,
  98. const std::string& server_host_name,
  99. const std::string& boot_file_name)
  100. : identifier_type_(IDENT_HWADDR),
  101. identifier_value_(), ipv4_subnet_id_(ipv4_subnet_id),
  102. ipv6_subnet_id_(ipv6_subnet_id),
  103. ipv4_reservation_(asiolink::IOAddress::IPV4_ZERO_ADDRESS()),
  104. hostname_(hostname), dhcp4_client_classes_(dhcp4_client_classes),
  105. dhcp6_client_classes_(dhcp6_client_classes),
  106. next_server_(asiolink::IOAddress::IPV4_ZERO_ADDRESS()),
  107. server_host_name_(server_host_name), boot_file_name_(boot_file_name),
  108. host_id_(0), cfg_option4_(new CfgOption()), cfg_option6_(new CfgOption()) {
  109. // Initialize host identifier.
  110. setIdentifier(identifier, identifier_name);
  111. if (!ipv4_reservation.isV4Zero()) {
  112. // Validate and set IPv4 address reservation.
  113. setIPv4Reservation(ipv4_reservation);
  114. }
  115. if (!next_server.isV4Zero()) {
  116. // Validate and set next server address.
  117. setNextServer(next_server);
  118. }
  119. }
  120. const std::vector<uint8_t>&
  121. Host::getIdentifier() const {
  122. return (identifier_value_);
  123. }
  124. Host::IdentifierType
  125. Host::getIdentifierType() const {
  126. return (identifier_type_);
  127. }
  128. Host::IdentifierType
  129. Host::getIdentifierType(const std::string& identifier_name) {
  130. if (identifier_name == "hw-address") {
  131. return (IDENT_HWADDR);
  132. } else if (identifier_name == "duid") {
  133. return (IDENT_DUID);
  134. } else if (identifier_name == "circuit-id") {
  135. return (IDENT_CIRCUIT_ID);
  136. } else if (identifier_name == "client-id") {
  137. return (IDENT_CLIENT_ID);
  138. } else {
  139. isc_throw(isc::BadValue, "invalid client identifier type '"
  140. << identifier_name << "'");
  141. }
  142. }
  143. HWAddrPtr
  144. Host::getHWAddress() const {
  145. return ((identifier_type_ == IDENT_HWADDR) ?
  146. HWAddrPtr(new HWAddr(identifier_value_, HTYPE_ETHER)) : HWAddrPtr());
  147. }
  148. DuidPtr
  149. Host::getDuid() const {
  150. return ((identifier_type_ == IDENT_DUID) ?
  151. DuidPtr(new DUID(identifier_value_)) : DuidPtr());
  152. }
  153. std::string
  154. Host::getIdentifierAsText() const {
  155. return (getIdentifierAsText(identifier_type_, &identifier_value_[0],
  156. identifier_value_.size()));
  157. }
  158. std::string
  159. Host::getIdentifierAsText(const IdentifierType& type, const uint8_t* value,
  160. const size_t length) {
  161. // Convert identifier into <type>=<value> form.
  162. std::ostringstream s;
  163. switch (type) {
  164. case IDENT_HWADDR:
  165. s << "hwaddr";
  166. break;
  167. case IDENT_DUID:
  168. s << "duid";
  169. break;
  170. case IDENT_CIRCUIT_ID:
  171. s << "circuit-id";
  172. break;
  173. case IDENT_CLIENT_ID:
  174. s << "client-id";
  175. break;
  176. default:
  177. // This should never happen actually, unless we add new identifier
  178. // and forget to add a case for it above.
  179. s << "(invalid-type)";
  180. }
  181. std::vector<uint8_t> vec(value, value + length);
  182. s << "=" << (length > 0 ? util::encode::encodeHex(vec) : "(null)");
  183. return (s.str());
  184. }
  185. std::string
  186. Host::getIdentifierName(const IdentifierType& type) {
  187. switch (type) {
  188. case Host::IDENT_HWADDR:
  189. return ("hw-address");
  190. case Host::IDENT_DUID:
  191. return ("duid");
  192. case Host::IDENT_CIRCUIT_ID:
  193. return ("circuit-id");
  194. case Host::IDENT_CLIENT_ID:
  195. return ("client-id");
  196. default:
  197. ;
  198. }
  199. return ("(unknown)");
  200. }
  201. void
  202. Host::setIdentifier(const uint8_t* identifier, const size_t len,
  203. const IdentifierType& type) {
  204. if (len < 1) {
  205. isc_throw(BadValue, "invalid client identifier length 0");
  206. }
  207. identifier_type_ = type;
  208. identifier_value_.assign(identifier, identifier + len);
  209. }
  210. void
  211. Host::setIdentifier(const std::string& identifier, const std::string& name) {
  212. // Empty identifier is not allowed.
  213. if (identifier.empty()) {
  214. isc_throw(isc::BadValue, "empty host identifier used");
  215. }
  216. // Set identifier type.
  217. identifier_type_ = getIdentifierType(name);
  218. // Idetifier value can either be specified as string of hexadecimal
  219. // digits or a string in quotes. The latter is copied to a vector excluding
  220. // quote characters.
  221. // Try to convert the values in quotes into a vector of ASCII codes.
  222. // If the identifier lacks opening and closing quote, this will return
  223. // an empty value, in which case we'll try to decode it as a string of
  224. // hexadecimal digits.
  225. try {
  226. std::vector<uint8_t> binary = util::str::quotedStringToBinary(identifier);
  227. if (binary.empty()) {
  228. util::str::decodeFormattedHexString(identifier, binary);
  229. }
  230. // Successfully decoded the identifier, so let's use it.
  231. identifier_value_.swap(binary);
  232. } catch (...) {
  233. // The string doesn't match any known pattern, so we have to
  234. // report an error at this point.
  235. isc_throw(isc::BadValue, "invalid host identifier value '"
  236. << identifier << "'");
  237. }
  238. }
  239. void
  240. Host::setIPv4Reservation(const asiolink::IOAddress& address) {
  241. if (!address.isV4()) {
  242. isc_throw(isc::BadValue, "address '" << address << "' is not a valid"
  243. " IPv4 address");
  244. } else if (address.isV4Zero() || address.isV4Bcast()) {
  245. isc_throw(isc::BadValue, "must not make reservation for the '"
  246. << address << "' address");
  247. }
  248. ipv4_reservation_ = address;
  249. }
  250. void
  251. Host::removeIPv4Reservation() {
  252. ipv4_reservation_ = asiolink::IOAddress::IPV4_ZERO_ADDRESS();
  253. }
  254. void
  255. Host::addReservation(const IPv6Resrv& reservation) {
  256. // Check if it is not duplicating existing reservation.
  257. if (hasReservation(reservation)) {
  258. isc_throw(isc::InvalidOperation, "failed on attempt to add a duplicated"
  259. " host reservation for " << reservation.toText());
  260. }
  261. // Add it.
  262. ipv6_reservations_.insert(IPv6ResrvTuple(reservation.getType(),
  263. reservation));
  264. }
  265. IPv6ResrvRange
  266. Host::getIPv6Reservations(const IPv6Resrv::Type& type) const {
  267. return (ipv6_reservations_.equal_range(type));
  268. }
  269. IPv6ResrvRange
  270. Host::getIPv6Reservations() const {
  271. return (IPv6ResrvRange(ipv6_reservations_.begin(),
  272. ipv6_reservations_.end()));
  273. }
  274. bool
  275. Host::hasIPv6Reservation() const {
  276. return (!ipv6_reservations_.empty());
  277. }
  278. bool
  279. Host::hasReservation(const IPv6Resrv& reservation) const {
  280. IPv6ResrvRange reservations = getIPv6Reservations(reservation.getType());
  281. if (std::distance(reservations.first, reservations.second) > 0) {
  282. for (IPv6ResrvIterator it = reservations.first;
  283. it != reservations.second; ++it) {
  284. if (it->second == reservation) {
  285. return (true);
  286. }
  287. }
  288. }
  289. // No matching reservations found.
  290. return (false);
  291. }
  292. void
  293. Host::addClientClass4(const std::string& class_name) {
  294. addClientClassInternal(dhcp4_client_classes_, class_name);
  295. }
  296. void
  297. Host::addClientClass6(const std::string& class_name) {
  298. addClientClassInternal(dhcp6_client_classes_, class_name);
  299. }
  300. void
  301. Host::addClientClassInternal(ClientClasses& classes,
  302. const std::string& class_name) {
  303. std::string trimmed = util::str::trim(class_name);
  304. if (!trimmed.empty()) {
  305. classes.insert(ClientClass(trimmed));
  306. }
  307. }
  308. void
  309. Host::setNextServer(const asiolink::IOAddress& next_server) {
  310. if (!next_server.isV4()) {
  311. isc_throw(isc::BadValue, "next server address '" << next_server
  312. << "' is not a valid IPv4 address");
  313. } else if (next_server.isV4Bcast()) {
  314. isc_throw(isc::BadValue, "invalid next server address '"
  315. << next_server << "'");
  316. }
  317. next_server_ = next_server;
  318. }
  319. void
  320. Host::setServerHostname(const std::string& server_host_name) {
  321. if (server_host_name.size() > Pkt4::MAX_SNAME_LEN - 1) {
  322. isc_throw(isc::BadValue, "server hostname length must not exceed "
  323. << (Pkt4::MAX_SNAME_LEN - 1));
  324. }
  325. server_host_name_ = server_host_name;
  326. }
  327. void
  328. Host::setBootFileName(const std::string& boot_file_name) {
  329. if (boot_file_name.size() > Pkt4::MAX_FILE_LEN - 1) {
  330. isc_throw(isc::BadValue, "boot file length must not exceed "
  331. << (Pkt4::MAX_FILE_LEN - 1));
  332. }
  333. boot_file_name_ = boot_file_name;
  334. }
  335. std::string
  336. Host::toText() const {
  337. std::ostringstream s;
  338. // Add HW address or DUID.
  339. s << getIdentifierAsText();
  340. // Add IPv4 subnet id if exists (non-zero).
  341. if (ipv4_subnet_id_) {
  342. s << " ipv4_subnet_id=" << ipv4_subnet_id_;
  343. }
  344. // Add IPv6 subnet id if exists (non-zero).
  345. if (ipv6_subnet_id_) {
  346. s << " ipv6_subnet_id=" << ipv6_subnet_id_;
  347. }
  348. // Add hostname.
  349. s << " hostname=" << (hostname_.empty() ? "(empty)" : hostname_);
  350. // Add IPv4 reservation.
  351. s << " ipv4_reservation=" << (ipv4_reservation_.isV4Zero() ? "(no)" :
  352. ipv4_reservation_.toText());
  353. // Add next server.
  354. s << " siaddr=" << (next_server_.isV4Zero() ? "(no)" :
  355. next_server_.toText());
  356. // Add server host name.
  357. s << " sname=" << (server_host_name_.empty() ? "(empty)" : server_host_name_);
  358. // Add boot file name.
  359. s << " file=" << (boot_file_name_.empty() ? "(empty)" : boot_file_name_);
  360. if (ipv6_reservations_.empty()) {
  361. s << " ipv6_reservations=(none)";
  362. } else {
  363. // Add all IPv6 reservations.
  364. for (IPv6ResrvIterator resrv = ipv6_reservations_.begin();
  365. resrv != ipv6_reservations_.end(); ++resrv) {
  366. s << " ipv6_reservation"
  367. << std::distance(ipv6_reservations_.begin(), resrv)
  368. << "=" << resrv->second.toText();
  369. }
  370. }
  371. // Add DHCPv4 client classes.
  372. for (ClientClasses::const_iterator cclass = dhcp4_client_classes_.begin();
  373. cclass != dhcp4_client_classes_.end(); ++cclass) {
  374. s << " dhcp4_class"
  375. << std::distance(dhcp4_client_classes_.begin(), cclass)
  376. << "=" << *cclass;
  377. }
  378. // Add DHCPv6 client classes.
  379. for (ClientClasses::const_iterator cclass = dhcp6_client_classes_.begin();
  380. cclass != dhcp6_client_classes_.end(); ++cclass) {
  381. s << " dhcp6_class"
  382. << std::distance(dhcp6_client_classes_.begin(), cclass)
  383. << "=" << *cclass;
  384. }
  385. return (s.str());
  386. }
  387. } // end of namespace isc::dhcp
  388. } // end of namespace isc