host.cc 12 KB

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