1 // Copyright (C) 2016-2021 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 7 #ifndef PGSQL_HOST_DATA_SOURCE_H 8 #define PGSQL_HOST_DATA_SOURCE_H 9 10 #include <database/database_connection.h> 11 #include <dhcpsrv/base_host_data_source.h> 12 #include <pgsql/pgsql_connection.h> 13 #include <pgsql/pgsql_exchange.h> 14 15 namespace isc { 16 namespace dhcp { 17 18 /// Forward declaration to the implementation of the @ref PgSqlHostDataSource. 19 class PgSqlHostDataSourceImpl; 20 21 /// @brief Type of pointers to PgSqlHostDataSourceImpl. 22 typedef boost::shared_ptr<PgSqlHostDataSourceImpl> PgSqlHostDataSourceImplPtr; 23 24 /// Forward declaration for the thread context for the manager pool. 25 class PgSqlHostContext; 26 27 /// @brief Type of pointers to contexts. 28 typedef boost::shared_ptr<PgSqlHostContext> PgSqlHostContextPtr; 29 30 /// @brief PostgreSQL Host Data Source 31 /// 32 /// This class implements the @ref isc::dhcp::BaseHostDataSource interface to 33 /// the PostgreSQL database. Use of this backend presupposes that a PostgreSQL 34 /// database is available and that the Kea schema has been created within it. 35 /// 36 /// Reservations are uniquely identified by identifier type and value. 37 /// The currently supported values are defined in @ref Host::IdentifierType 38 /// as well as in host_identifier_table: 39 /// 40 /// - IDENT_HWADDR 41 /// - IDENT_DUID 42 /// - IDENT_CIRCUIT_ID 43 /// - IDENT_CLIENT_ID 44 /// 45 class PgSqlHostDataSource : public BaseHostDataSource { 46 public: 47 48 /// @brief Constructor 49 /// 50 /// Uses the following keywords in the parameters passed to it to 51 /// connect to the database: 52 /// - name - Name of the database to which to connect (mandatory) 53 /// - host - Host to which to connect (optional, defaults to "localhost") 54 /// - user - Username under which to connect (optional) 55 /// - password - Password for "user" on the database (optional) 56 /// 57 /// If the database is successfully opened, the version number in the 58 /// schema_version table will be checked against hard-coded value in 59 /// the implementation file. 60 /// 61 /// Finally, all the SQL commands are pre-compiled. 62 /// 63 /// @param parameters A data structure relating keywords and values 64 /// concerned with the database. 65 /// 66 /// @throw isc::db::NoDatabaseName Mandatory database name not given 67 /// @throw isc::db::DbOpenError Error opening the database 68 /// @throw isc::db::DbOperationError An operation on the open database has 69 /// failed. 70 PgSqlHostDataSource(const db::DatabaseConnection::ParameterMap& parameters); 71 72 /// @brief Virtual destructor. 73 /// 74 /// Frees database resources and closes the database connection through 75 /// the destruction of member impl_. 76 virtual ~PgSqlHostDataSource(); 77 78 /// @brief Return backend parameters 79 /// 80 /// Returns the backend parameters 81 /// 82 /// @return Parameters of the backend. 83 virtual isc::db::DatabaseConnection::ParameterMap getParameters() const; 84 85 /// @brief Adds a new host to the collection. 86 /// 87 /// The method will insert the given host and all of its children (v4 88 /// options, v6 options, and v6 reservations) into the database. It 89 /// relies on constraints defined as part of the PostgreSQL schema to 90 /// defend against duplicate entries and to ensure referential 91 /// integrity. 92 /// 93 /// Violation of any of these constraints for a host will result in a 94 /// DuplicateEntry exception: 95 /// 96 /// -# IPV4_ADDRESS and DHCP4_SUBNET_ID combination must be unique 97 /// -# IPV6 ADDRESS and PREFIX_LEN combination must be unique 98 /// -# DHCP ID, DHCP ID TYPE, and DHCP4_SUBNET_ID combination must be unique 99 /// -# DHCP ID, DHCP ID TYPE, and DHCP6_SUBNET_ID combination must be unique 100 /// 101 /// In addition, violating the following referential constraints will 102 /// a DbOperationError exception: 103 /// 104 /// -# DHCP ID TYPE must be defined in the HOST_IDENTIFIER_TYPE table 105 /// -# For DHCP4 Options: 106 /// -# HOST_ID must exist with HOSTS 107 /// -# SCOPE_ID must be defined in DHCP_OPTION_SCOPE 108 /// -# For DHCP6 Options: 109 /// -# HOST_ID must exist with HOSTS 110 /// -# SCOPE_ID must be defined in DHCP_OPTION_SCOPE 111 /// -# For IPV6 Reservations: 112 /// -# HOST_ID must exist with HOSTS 113 /// -# Address and Prefix Length must be unique (DuplicateEntry) 114 /// 115 /// @param host Pointer to the new @c Host object being added. 116 /// @throw DuplicateEntry or DbOperationError dependent on the constraint 117 /// violation 118 virtual void add(const HostPtr& host); 119 120 /// @brief Attempts to delete hosts by (subnet-id, address) 121 /// 122 /// This method supports both v4 and v6. 123 /// 124 /// @param subnet_id subnet identifier. 125 /// @param addr specified address. 126 /// @return true if deletion was successful, false if the host was not there. 127 /// @throw various exceptions in case of errors 128 virtual bool del(const SubnetID& subnet_id, 129 const asiolink::IOAddress& addr); 130 131 /// @brief Attempts to delete a host by (subnet4-id, identifier type, identifier) 132 /// 133 /// This method supports v4 hosts only. 134 /// 135 /// @param subnet_id subnet identifier. 136 /// @param identifier_type Identifier type. 137 /// @param identifier_begin Pointer to a beginning of a buffer containing 138 /// an identifier. 139 /// @param identifier_len Identifier length. 140 /// 141 /// @return true if deletion was successful, false if the host was not there. 142 /// @throw various exceptions in case of errors 143 virtual bool del4(const SubnetID& subnet_id, 144 const Host::IdentifierType& identifier_type, 145 const uint8_t* identifier_begin, 146 const size_t identifier_len); 147 148 /// @brief Attempts to delete a host by (subnet6-id, identifier type, identifier) 149 /// 150 /// This method supports v6 hosts only. 151 /// 152 /// @param subnet_id subnet identifier. 153 /// @param identifier_type Identifier type. 154 /// @param identifier_begin Pointer to a beginning of a buffer containing 155 /// an identifier. 156 /// @param identifier_len Identifier length. 157 /// 158 /// @return true if deletion was successful, false if the host was not there. 159 /// @throw various exceptions in case of errors 160 virtual bool del6(const SubnetID& subnet_id, 161 const Host::IdentifierType& identifier_type, 162 const uint8_t* identifier_begin, 163 const size_t identifier_len); 164 165 /// @brief Return all hosts connected to any subnet for which reservations 166 /// have been made using a specified identifier. 167 /// 168 /// This method returns all @c Host objects which represent reservations 169 /// for a specified identifier. This method may return multiple hosts 170 /// because a particular client may have reservations in multiple subnets. 171 /// 172 /// @param identifier_type Identifier type. 173 /// @param identifier_begin Pointer to a beginning of a buffer containing 174 /// an identifier. 175 /// @param identifier_len Identifier length. 176 /// 177 /// @return Collection of const @c Host objects. 178 virtual ConstHostCollection getAll(const Host::IdentifierType& identifier_type, 179 const uint8_t* identifier_begin, 180 const size_t identifier_len) const; 181 182 /// @brief Return all hosts in a DHCPv4 subnet. 183 /// 184 /// This method returns all @ref Host objects which represent reservations 185 /// in a specified subnet. Global reservations are returned for the 186 /// subnet id 0. 187 /// 188 /// @param subnet_id subnet identifier to filter by 189 /// 190 /// @return Collection of const @ref Host objects. 191 virtual ConstHostCollection getAll4(const SubnetID& subnet_id) const; 192 193 /// @brief Return all hosts in a DHCPv6 subnet. 194 /// 195 /// This method returns all @ref Host objects which represent reservations 196 /// in a specified subnet. Global reservations are returned for the 197 /// subnet id 0. 198 /// 199 /// @param subnet_id subnet identifier to filter by 200 /// 201 /// @return Collection of const @ref Host objects. 202 virtual ConstHostCollection getAll6(const SubnetID& subnet_id) const; 203 204 /// @brief Return all hosts with a hostname. 205 /// 206 /// This method returns all @c Host objects which represent reservations 207 /// using a specified hostname. 208 /// 209 /// PostgreSQL uses the hosts_by_hostname index on LOWER(hostname). 210 /// 211 /// @param hostname The lower case hostname. 212 /// 213 /// @return Collection of const @c Host objects. 214 virtual ConstHostCollection getAllbyHostname(const std::string& hostname) const; 215 216 /// @brief Return all hosts with a hostname in a DHCPv4 subnet. 217 /// 218 /// This method returns all @c Host objects which represent reservations 219 /// using a specified hostname in a specified subnet. 220 /// 221 /// @param hostname The lower case hostname. 222 /// @param subnet_id Subnet identifier. 223 /// 224 /// @return Collection of const @c Host objects. 225 virtual ConstHostCollection getAllbyHostname4(const std::string& hostname, 226 const SubnetID& subnet_id) const; 227 228 /// @brief Return all hosts with a hostname in a DHCPv6 subnet. 229 /// 230 /// This method returns all @c Host objects which represent reservations 231 /// using a specified hostname in a specified subnet. 232 /// 233 /// @param hostname The lower case hostname. 234 /// @param subnet_id Subnet identifier. 235 /// 236 /// @return Collection of const @c Host objects. 237 virtual ConstHostCollection getAllbyHostname6(const std::string& hostname, 238 const SubnetID& subnet_id) const; 239 240 /// @brief Returns range of hosts in a DHCPv4 subnet. 241 /// 242 /// This method implements paged browsing of host databases. The 243 /// parameters specify a page size, an index in sources and the 244 /// starting host id of the range. If not zero this host id is 245 /// excluded from the returned range. When a source is exhausted 246 /// the index is updated. There is no guarantee about the order 247 /// of returned host reservations, only the sources and 248 /// reservations from the same source are ordered. 249 /// 250 /// @param subnet_id Subnet identifier. 251 /// @param source_index Index of the source (unused). 252 /// @param lower_host_id Host identifier used as lower bound for the 253 /// returned range. 254 /// @param page_size maximum size of the page returned. 255 /// 256 /// @return Collection of const @c Host objects (may be empty). 257 virtual ConstHostCollection getPage4(const SubnetID& subnet_id, 258 size_t& source_index, 259 uint64_t lower_host_id, 260 const HostPageSize& page_size) const; 261 262 /// @brief Returns range of hosts in a DHCPv6 subnet. 263 /// 264 /// This method implements paged browsing of host databases. The 265 /// parameters specify a page size, an index in sources and the 266 /// starting host id of the range. If not zero this host id is 267 /// excluded from the returned range. When a source is exhausted 268 /// the index is updated. There is no guarantee about the order 269 /// of returned host reservations, only the sources and 270 /// reservations from the same source are ordered. 271 /// 272 /// @param subnet_id Subnet identifier. 273 /// @param source_index Index of the source (unused). 274 /// @param lower_host_id Host identifier used as lower bound for the 275 /// returned range. 276 /// @param page_size maximum size of the page returned. 277 /// 278 /// @return Collection of const @c Host objects (may be empty). 279 virtual ConstHostCollection getPage6(const SubnetID& subnet_id, 280 size_t& source_index, 281 uint64_t lower_host_id, 282 const HostPageSize& page_size) const; 283 284 /// @brief Returns range of hosts. 285 /// 286 /// This method implements paged browsing of host databases. The 287 /// parameters specify a page size, an index in sources and the 288 /// starting host id of the range. If not zero this host id is 289 /// excluded from the returned range. When a source is exhausted 290 /// the index is updated. There is no guarantee about the order 291 /// of returned host reservations, only the sources and 292 /// reservations from the same source are ordered. 293 /// 294 /// @param source_index Index of the source (unused). 295 /// @param lower_host_id Host identifier used as lower bound for the 296 /// returned range. 297 /// @param page_size maximum size of the page returned. 298 /// 299 /// @return Collection of const @c Host objects (may be empty). 300 virtual ConstHostCollection getPage4(size_t& source_index, 301 uint64_t lower_host_id, 302 const HostPageSize& page_size) const; 303 304 /// @brief Returns range of hosts. 305 /// 306 /// This method implements paged browsing of host databases. The 307 /// parameters specify a page size, an index in sources and the 308 /// starting host id of the range. If not zero this host id is 309 /// excluded from the returned range. When a source is exhausted 310 /// the index is updated. There is no guarantee about the order 311 /// of returned host reservations, only the sources and 312 /// reservations from the same source are ordered. 313 /// 314 /// @param source_index Index of the source (unused). 315 /// @param lower_host_id Host identifier used as lower bound for the 316 /// returned range. 317 /// @param page_size maximum size of the page returned. 318 /// 319 /// @return Collection of const @c Host objects (may be empty). 320 virtual ConstHostCollection getPage6(size_t& source_index, 321 uint64_t lower_host_id, 322 const HostPageSize& page_size) const; 323 324 /// @brief Returns a collection of hosts using the specified IPv4 address. 325 /// 326 /// This method may return multiple @c Host objects if they are connected 327 /// to different subnets. 328 /// 329 /// @param address IPv4 address for which the @c Host object is searched. 330 /// 331 /// @return Collection of const @c Host objects. 332 virtual ConstHostCollection getAll4(const asiolink::IOAddress& address) const; 333 334 /// @brief Returns a host connected to the IPv4 subnet. 335 /// 336 /// @param subnet_id Subnet identifier. 337 /// @param identifier_type Identifier type. 338 /// @param identifier_begin Pointer to a beginning of a buffer containing 339 /// an identifier. 340 /// @param identifier_len Identifier length. 341 /// 342 /// @return Const @c Host object for which reservation has been made using 343 /// the specified identifier. 344 virtual ConstHostPtr get4(const SubnetID& subnet_id, 345 const Host::IdentifierType& identifier_type, 346 const uint8_t* identifier_begin, 347 const size_t identifier_len) const; 348 349 /// @brief Returns a host connected to the IPv4 subnet and having 350 /// a reservation for a specified IPv4 address. 351 /// 352 /// One of the use cases for this method is to detect collisions between 353 /// dynamically allocated addresses and reserved addresses. When the new 354 /// address is assigned to a client, the allocation mechanism should check 355 /// if this address is not reserved for some other host and do not allocate 356 /// this address if reservation is present. 357 /// 358 /// @param subnet_id Subnet identifier. 359 /// @param address reserved IPv4 address. 360 /// 361 /// @return Const @c Host object using a specified IPv4 address. 362 /// @throw BadValue is given an IPv6 address 363 virtual ConstHostPtr get4(const SubnetID& subnet_id, 364 const asiolink::IOAddress& address) const; 365 366 /// @brief Returns all hosts connected to the IPv4 subnet and having 367 /// a reservation for a specified address. 368 /// 369 /// In most cases it is desired that there is at most one reservation 370 /// for a given IPv4 address within a subnet. In a default configuration, 371 /// the backend does not allow for inserting more than one host with 372 /// the same IPv4 reservation. In that case, the number of hosts returned 373 /// by this function is 0 or 1. 374 /// 375 /// If the backend is configured to allow multiple hosts with reservations 376 /// for the same IPv4 address in the given subnet, this method can return 377 /// more than one host. 378 /// 379 /// The typical use case when a single IPv4 address is reserved for multiple 380 /// hosts is when these hosts represent different interfaces of the same 381 /// machine and each interface comes with a different MAC address. In that 382 /// case, the same IPv4 address is assigned regardless of which interface is 383 /// used by the DHCP client to communicate with the server. 384 /// 385 /// @param subnet_id Subnet identifier. 386 /// @param address reserved IPv4 address. 387 /// 388 /// @return Collection of const @c Host objects. 389 virtual ConstHostCollection 390 getAll4(const SubnetID& subnet_id, 391 const asiolink::IOAddress& address) const; 392 393 /// @brief Returns a host connected to the IPv6 subnet. 394 /// 395 /// @param subnet_id Subnet identifier. 396 /// @param identifier_type Identifier type. 397 /// @param identifier_begin Pointer to a beginning of a buffer containing 398 /// an identifier. 399 /// @param identifier_len Identifier length. 400 /// 401 /// @return Const @c Host object for which reservation has been made using 402 /// the specified identifier. 403 virtual ConstHostPtr get6(const SubnetID& subnet_id, 404 const Host::IdentifierType& identifier_type, 405 const uint8_t* identifier_begin, 406 const size_t identifier_len) const; 407 408 /// @brief Returns a host using the specified IPv6 prefix. 409 /// 410 /// @param prefix IPv6 prefix for which the @c Host object is searched. 411 /// @param prefix_len IPv6 prefix length. 412 /// 413 /// @return Const @c Host object using a specified IPv6 prefix. 414 virtual ConstHostPtr get6(const asiolink::IOAddress& prefix, 415 const uint8_t prefix_len) const; 416 417 /// @brief Returns a host connected to the IPv6 subnet and having 418 /// a reservation for a specified IPv6 address or prefix. 419 /// 420 /// @param subnet_id Subnet identifier. 421 /// @param address reserved IPv6 address/prefix. 422 /// 423 /// @return Const @c Host object using a specified IPv6 address/prefix. 424 virtual ConstHostPtr get6(const SubnetID& subnet_id, 425 const asiolink::IOAddress& address) const; 426 427 /// @brief Returns all hosts connected to the IPv6 subnet and having 428 /// a reservation for a specified address or delegated prefix (lease). 429 /// 430 /// In most cases it is desired that there is at most one reservation 431 /// for a given IPv6 lease within a subnet. In a default configuration, 432 /// the backend does not allow for inserting more than one host with 433 /// the same IPv6 address or prefix. In that case, the number of hosts 434 /// returned by this function is 0 or 1. 435 /// 436 /// If the backend is configured to allow multiple hosts with reservations 437 /// for the same IPv6 lease in the given subnet, this method can return 438 /// more than one host. 439 /// 440 /// The typical use case when a single IPv6 lease is reserved for multiple 441 /// hosts is when these hosts represent different interfaces of the same 442 /// machine and each interface comes with a different MAC address. In that 443 /// case, the same IPv6 lease is assigned regardless of which interface is 444 /// used by the DHCP client to communicate with the server. 445 /// 446 /// @param subnet_id Subnet identifier. 447 /// @param address reserved IPv6 address/prefix. 448 /// 449 /// @return Collection of const @c Host objects. 450 virtual ConstHostCollection 451 getAll6(const SubnetID& subnet_id, 452 const asiolink::IOAddress& address) const; 453 454 /// @brief Return backend type 455 /// 456 /// Returns the type of database as the string "postgresql". This is 457 /// same value as used for configuration purposes. 458 /// 459 /// @return Type of the backend. getType()460 virtual std::string getType() const { 461 return (std::string("postgresql")); 462 } 463 464 /// @brief Returns the name of the open database 465 /// 466 /// @return String containing the name of the database 467 virtual std::string getName() const; 468 469 /// @brief Returns description of the backend. 470 /// 471 /// This description may be multiline text that describes the backend. 472 /// 473 /// @return Description of the backend. 474 virtual std::string getDescription() const; 475 476 /// @brief Returns backend version. 477 /// 478 /// The method is called by the constructor after opening the database 479 /// but prior to preparing SQL statements, to verify that the schema version 480 /// is correct. Thus it must not rely on a pre-prepared statement or 481 /// formal statement execution error checking. 482 /// 483 /// @return Version number stored in the database, as a pair of unsigned 484 /// integers. "first" is the major version number, "second" the 485 /// minor number. 486 /// 487 /// @throw isc::db::DbOperationError An operation on the open database 488 /// has failed. 489 virtual std::pair<uint32_t, uint32_t> getVersion() const; 490 491 /// @brief Commit Transactions 492 /// 493 /// Commits all pending database operations. 494 virtual void commit(); 495 496 /// @brief Rollback Transactions 497 /// 498 /// Rolls back all pending database operations. 499 virtual void rollback(); 500 501 /// @brief Controls whether IP reservations are unique or non-unique. 502 /// 503 /// In a typical case, the IP reservations are unique and backends verify 504 /// prior to adding a host reservation to the database that the reservation 505 /// for a given IP address/subnet does not exist. In some cases it may be 506 /// required to allow non-unique IP reservations, e.g. in the case when a 507 /// host has several interfaces and independently of which interface is used 508 /// by this host to communicate with the DHCP server the same IP address 509 /// should be assigned. In this case the @c unique value should be set to 510 /// false to disable the checks for uniqueness on the backend side. 511 /// 512 /// @param unique boolean flag indicating if the IP reservations must be 513 /// unique within the subnet or can be non-unique. 514 /// @return always true because this backend supports both the case when 515 /// the addresses must be unique and when they may be non-unique. 516 virtual bool setIPReservationsUnique(const bool unique); 517 518 /// @brief Flag which indicates if the host manager has at least one 519 /// unusable connection. 520 /// 521 /// @return true if there is at least one unusable connection, false 522 /// otherwise 523 virtual bool isUnusable(); 524 525 /// @brief Context RAII Allocator. 526 class PgSqlHostContextAlloc { 527 public: 528 529 /// @brief Constructor 530 /// 531 /// This constructor takes a context of the pool if one is available 532 /// or creates a new one. 533 /// 534 /// @param mgr A parent instance 535 PgSqlHostContextAlloc(PgSqlHostDataSourceImpl& mgr); 536 537 /// @brief Destructor 538 /// 539 /// This destructor puts back the context in the pool. 540 ~PgSqlHostContextAlloc(); 541 542 /// @brief The context 543 PgSqlHostContextPtr ctx_; 544 545 private: 546 /// @brief The manager 547 PgSqlHostDataSourceImpl& mgr_; 548 }; 549 550 private: 551 /// @brief Pointer to the implementation of the @ref PgSqlHostDataSource. 552 PgSqlHostDataSourceImplPtr impl_; 553 }; 554 555 } 556 } 557 558 #endif // PGSQL_HOST_DATA_SOURCE_H 559