#include <asiolink/io_address.h>
#include <asiolink/io_service.h>
+#include <cc/data.h>
#include <database/db_exceptions.h>
#include <dhcp/duid.h>
#include <dhcp/option.h>
/// @return number of leases removed.
virtual size_t wipeLeases6(const SubnetID& subnet_id) = 0;
+ /// @brief Checks if the IPv4 lease limits set in the given user context are exceeded.
+ /// Abstract method.
+ ///
+ /// @param user_context all or part of the lease's user context which, for the intents and
+ /// purposes of lease limiting should have the following format
+ /// (not all nodes are mandatory and values are given only as examples):
+ /// { "ISC": { "limits": { "client-classes": [ { "name": "foo", "address-limit": 2 } ],
+ /// "subnet": { "id": 1, "address-limit": 2 } } } }
+ ///
+ /// @return a string describing a limit that is being exceeded, or an empty
+ /// string if no limits are exceeded
+ virtual std::string checkLimits4(isc::data::ConstElementPtr const& user_context) const = 0;
+
+ /// @brief Checks if the IPv6 lease limits set in the given user context are exceeded.
+ /// Abstract method.
+ ///
+ /// @param user_context all or part of the lease's user context which, for the intents and
+ /// purposes of lease limiting should have the following format
+ /// (not all nodes are mandatory and values are given only as examples):
+ /// { "ISC": { "limits": { "client-classes": [ { "name": "foo", "address-limit": 2, "prefix-limit": 1 } ],
+ /// "subnet": { "id": 1, "address-limit": 2, "prefix-limit": 1 } } } }
+ ///
+ /// @return a string describing a limit that is being exceeded, or an empty
+ /// string if no limits are exceeded
+ virtual std::string checkLimits6(isc::data::ConstElementPtr const& user_context) const = 0;
+
/// @brief Return backend type
///
/// Returns the type of the backend (e.g. "mysql", "memfile" etc.)
using namespace isc::db;
using namespace isc::util;
+using isc::data::ConstElementPtr;
+
namespace isc {
namespace dhcp {
return (num);
}
+std::string
+Memfile_LeaseMgr::checkLimits4(ConstElementPtr const& user_context) const {
+ isc_throw(NotImplemented, "Memfile_LeaseMgr::checkLimits4() not implemented");
+}
+
+std::string
+Memfile_LeaseMgr::checkLimits6(ConstElementPtr const& user_context) const {
+ isc_throw(NotImplemented, "Memfile_LeaseMgr::checkLimits4() not implemented");
+}
+
} // namespace dhcp
} // namespace isc
/// @return number of leases removed.
virtual size_t wipeLeases6(const SubnetID& subnet_id);
+ /// @brief Checks if the IPv4 lease limits set in the given user context are exceeded.
+ /// Memfile implementation.
+ ///
+ /// @param user_context all or part of the lease's user context which, for the intents and
+ /// purposes of lease limiting should have the following format
+ /// (not all nodes are mandatory and values are given only as examples):
+ /// { "ISC": { "limits": { "client-classes": [ { "name": "foo", "address-limit": 2 } ],
+ /// "subnet": { "id": 1, "address-limit": 2 } } } }
+ ///
+ /// @return a string describing a limit that is being exceeded, or an empty
+ /// string if no limits are exceeded
+ std::string checkLimits4(isc::data::ConstElementPtr const& user_context) const override;
+
+ /// @brief Checks if the IPv6 lease limits set in the given user context are exceeded.
+ /// Memfile implementation.
+ ///
+ /// @param user_context all or part of the lease's user context which, for the intents and
+ /// purposes of lease limiting should have the following format
+ /// (not all nodes are mandatory and values are given only as examples):
+ /// { "ISC": { "limits": { "client-classes": [ { "name": "foo", "address-limit": 2, "prefix-limit": 1 } ],
+ /// "subnet": { "id": 1, "address-limit": 2, "prefix-limit": 1 } } } }
+ ///
+ /// @return a string describing a limit that is being exceeded, or an empty
+ /// string if no limits are exceeded
+ std::string checkLimits6(isc::data::ConstElementPtr const& user_context) const override;
+
private:
/// @name Internal methods called while holding the mutex in multi threading
"SELECT subnet_id, lease_type, state, leases as state_count "
"FROM lease6_stat "
"WHERE subnet_id >= ? and subnet_id <= ? "
- "ORDER BY subnet_id, lease_type, state"}
- }
-};
+ "ORDER BY subnet_id, lease_type, state"},
+ // TODO: remove single quotes from the following two SELECTs when the functions are implemented
+ {MySqlLeaseMgr::CHECK_LEASE4_LIMITS, "SELECT 'checkLease4Limits(?)'"},
+ {MySqlLeaseMgr::CHECK_LEASE6_LIMITS, "SELECT 'checkLease6Limits(?)'"},
+} }; // tagged_statements
} // namespace
return (deleted_leases);
}
+string
+MySqlLeaseMgr::checkLimits(ConstElementPtr const& user_context, StatementIndex const stindex) const {
+ constexpr int column_count(1);
+
+ // Set up the WHERE clause value.
+ MYSQL_BIND inbind[column_count];
+ memset(inbind, 0, sizeof(inbind));
+ std::string const& user_context_string(user_context->str());
+ inbind[0].buffer = const_cast<char*>(user_context_string.c_str());
+ inbind[0].buffer_length = user_context_string.length();
+ inbind[0].buffer_type = MYSQL_TYPE_STRING;
+
+ my_bool error[column_count];
+ MySqlLeaseExchange::setErrorIndicators(inbind, error, column_count);
+
+ // Get a context and a prepared statement.
+ MySqlLeaseContextAlloc get_context(*this);
+ MySqlLeaseContextPtr ctx = get_context.ctx_;
+ MYSQL_STMT* prepared_statement(ctx->conn_.statements_[stindex]);
+
+ // Bind the selection parameters to the statement.
+ int status(mysql_stmt_bind_param(prepared_statement, inbind));
+ checkError(ctx, status, stindex, "unable to bind WHERE clause parameter");
+
+ // Set up the MYSQL_BIND array for the data being returned and bind it to
+ // the statement.
+ std::vector<MYSQL_BIND> outbind;
+ outbind.push_back(MYSQL_BIND());
+ my_bool result_null(MLM_FALSE);
+ char result[USER_CONTEXT_MAX_LEN];
+ unsigned long result_length(sizeof(result));
+ outbind[0].buffer_type = MYSQL_TYPE_STRING;
+ outbind[0].buffer = reinterpret_cast<char*>(result);
+ outbind[0].buffer_length = result_length;
+ outbind[0].length = &result_length;
+ outbind[0].is_null = &result_null;
+ status = mysql_stmt_bind_result(prepared_statement, &outbind[0]);
+ checkError(ctx, status, stindex, "unable to bind SELECT clause parameters");
+
+ // Execute the statement.
+ status = MysqlExecuteStatement(prepared_statement);
+ checkError(ctx, status, stindex, "unable to execute");
+
+ // Ensure that all the lease information is retrieved in one go to avoid
+ // overhead of going back and forth between client and server.
+ status = mysql_stmt_store_result(prepared_statement);
+ checkError(ctx, status, stindex, "unable to set up for storing all results");
+
+ // Set up the fetch "release" object to release resources associated
+ // with the call to mysql_stmt_fetch when this method exits, then
+ // retrieve the data.
+ MySqlFreeResult fetch_release(prepared_statement);
+ int count = 0;
+ while ((status = mysql_stmt_fetch(prepared_statement)) == 0) {
+ // "result" should be populated now. We'll use that directly outside the loop.
+
+ if (++count > 1) {
+ isc_throw(MultipleRecords, "multiple records were found in the "
+ "database where only one was expected for query "
+ << ctx->conn_.text_statements_[stindex]);
+ }
+ }
+
+ // How did the fetch end?
+ if (status == 1) {
+ // Error - unable to fetch results
+ checkError(ctx, status, stindex, "unable to fetch results");
+ } else if (status == MYSQL_DATA_TRUNCATED) {
+ // Data truncated - throw an exception indicating what was at fault.
+ std::string columns[column_count];
+ columns[0] = "limits";
+ isc_throw(DataTruncated, ctx->conn_.text_statements_[stindex]
+ << " returned truncated data: columns affected are "
+ << MySqlLeaseExchange::getColumnsInError(error, columns, column_count));
+ }
+
+ return result;
+}
+
+string
+MySqlLeaseMgr::checkLimits4(ConstElementPtr const& user_context) const {
+ return checkLimits(user_context, CHECK_LEASE4_LIMITS);
+}
+
+string
+MySqlLeaseMgr::checkLimits6(ConstElementPtr const& user_context) const {
+ return checkLimits(user_context, CHECK_LEASE6_LIMITS);
+}
+
LeaseStatsQueryPtr
MySqlLeaseMgr::startLeaseStatsQuery4() {
// Get a context
ALL_LEASE6_STATS, // Fetches IPv6 lease statistics
SUBNET_LEASE6_STATS, // Fetched IPv6 lease stats for a single subnet.
SUBNET_RANGE_LEASE6_STATS, // Fetched IPv6 lease stats for a subnet range.
+ CHECK_LEASE4_LIMITS, // Check if allocated IPv4 leases are inside the set limits.
+ CHECK_LEASE6_LIMITS, // Check if allocated IPv6 leases are inside the set limits.
NUM_STATEMENTS // Number of statements
};
uint64_t deleteExpiredReclaimedLeasesCommon(const uint32_t secs,
StatementIndex statement_index);
+ /// @brief Checks if the lease limits set in the given user context are exceeded.
+ /// Contains common logic used by @ref checkLimits4 and @ref checkLimits6.
+ ///
+ /// @param user_context all or part of the lease's user context which, for the intents and
+ /// purposes of lease limiting should have the following format
+ /// (not all nodes are mandatory and values are given only as examples):
+ /// { "ISC": { "limits": { "client-classes": [ { "name": "foo", "address-limit": 2, "prefix-limit": 1 } ],
+ /// "subnet": { "id": 1, "address-limit": 2, "prefix-limit": 1 } } } }
+ ///
+ /// @return a string describing a limit that is being exceeded, or an empty
+ /// string if no limits are exceeded
+ std::string
+ checkLimits(isc::data::ConstElementPtr const& user_context, StatementIndex const stindex) const;
+
+ /// @brief Checks if the IPv4 lease limits set in the given user context are exceeded.
+ /// MySQL implementation.
+ ///
+ /// @param user_context all or part of the lease's user context which, for the intents and
+ /// purposes of lease limiting should have the following format
+ /// (not all nodes are mandatory and values are given only as examples):
+ /// { "ISC": { "limits": { "client-classes": [ { "name": "foo", "address-limit": 2 } ],
+ /// "subnet": { "id": 1, "address-limit": 2 } } } }
+ ///
+ /// @return a string describing a limit that is being exceeded, or an empty
+ /// string if no limits are exceeded
+ std::string checkLimits4(isc::data::ConstElementPtr const& user_context) const override;
+
+ /// @brief Checks if the IPv6 lease limits set in the given user context are exceeded.
+ /// MySQL implementation.
+ ///
+ /// @param user_context all or part of the lease's user context which, for the intents and
+ /// purposes of lease limiting should have the following format
+ /// (not all nodes are mandatory and values are given only as examples):
+ /// { "ISC": { "limits": { "client-classes": [ { "name": "foo", "address-limit": 2, "prefix-limit": 1 } ],
+ /// "subnet": { "id": 1, "address-limit": 2, "prefix-limit": 1 } } } }
+ ///
+ /// @return a string describing a limit that is being exceeded, or an empty
+ /// string if no limits are exceeded
+ std::string checkLimits6(isc::data::ConstElementPtr const& user_context) const override;
+
/// @brief Check Error and Throw Exception
///
/// This method invokes @ref MySqlConnection::checkError.
" FROM lease6_stat "
" WHERE subnet_id >= $1 and subnet_id <= $2 "
" ORDER BY subnet_id, lease_type, state" },
+
+ // TODO: remove single quotes from the following two SELECTs when the functions are implemented
+
+ // CHECK_LEASE4_LIMITS
+ { 1, { OID_TEXT },
+ "check_lease4_limits",
+ "SELECT 'checkLease4Limits($1)'" },
+
+ // CHECK_LEASE6_LIMITS
+ { 1, { OID_TEXT },
+ "check_lease6_limits",
+ "SELECT 'checkLease6Limits($1)'" },
+
// End of list sentinel
{ 0, { 0 }, NULL, NULL}
};
return (deleteLeaseCommon(statement_index, bind_array));
}
+string
+PgSqlLeaseMgr::checkLimits(ConstElementPtr const& user_context, StatementIndex const stindex) const {
+ // Bindings
+ PsqlBindArray bind_array;
+ std::string const user_context_str(user_context->str());
+ bind_array.add(user_context_str);
+
+ // Get a context.
+ PgSqlLeaseContextAlloc get_context(*this);
+ PgSqlLeaseContextPtr ctx(get_context.ctx_);
+
+ PgSqlResult r(PQexecPrepared(ctx->conn_,
+ tagged_statements[stindex].name,
+ tagged_statements[stindex].nbparams,
+ &bind_array.values_[0],
+ &bind_array.lengths_[0],
+ &bind_array.formats_[0], 0));
+
+ ctx->conn_.checkStatementError(r, tagged_statements[stindex]);
+
+ int rows = PQntuples(r);
+ if (rows > 1) {
+ isc_throw(MultipleRecords, "multiple records were found in the "
+ "database where only one was expected for query "
+ << tagged_statements[stindex].name);
+ }
+
+ std::string const limits(PgSqlExchange::getRawColumnValue(r, 0, 0));
+ return limits;
+}
+
+string
+PgSqlLeaseMgr::checkLimits4(ConstElementPtr const& user_context) const {
+ return checkLimits(user_context, CHECK_LEASE4_LIMITS);
+}
+
+string
+PgSqlLeaseMgr::checkLimits6(ConstElementPtr const& user_context) const {
+ return checkLimits(user_context, CHECK_LEASE6_LIMITS);
+}
+
LeaseStatsQueryPtr
PgSqlLeaseMgr::startLeaseStatsQuery4() {
// Get a context
ALL_LEASE6_STATS, // Fetches IPv6 lease statistics
SUBNET_LEASE6_STATS, // Fetched IPv6 lease stats for a single subnet.
SUBNET_RANGE_LEASE6_STATS, // Fetched IPv6 lease stats for a subnet range.
+ CHECK_LEASE4_LIMITS, // Check if allocated IPv4 leases are inside the set limits.
+ CHECK_LEASE6_LIMITS, // Check if allocated IPv6 leases are inside the set limits.
NUM_STATEMENTS // Number of statements
};
uint64_t deleteExpiredReclaimedLeasesCommon(const uint32_t secs,
StatementIndex statement_index);
+ /// @brief Checks if the lease limits set in the given user context are exceeded.
+ /// Contains common logic used by @ref checkLimits4 and @ref checkLimits6.
+ ///
+ /// @param user_context all or part of the lease's user context which, for the intents and
+ /// purposes of lease limiting should have the following format
+ /// (not all nodes are mandatory and values are given only as examples):
+ /// { "ISC": { "limits": { "client-classes": [ { "name": "foo", "address-limit": 2, "prefix-limit": 1 } ],
+ /// "subnet": { "id": 1, "address-limit": 2, "prefix-limit": 1 } } } }
+ ///
+ /// @return a string describing a limit that is being exceeded, or an empty
+ /// string if no limits are exceeded
+ std::string
+ checkLimits(isc::data::ConstElementPtr const& user_context, StatementIndex const stindex) const;
+
+ /// @brief Checks if the IPv4 lease limits set in the given user context are exceeded.
+ /// PostgreSQL implementation.
+ ///
+ /// @param user_context all or part of the lease's user context which, for the intents and
+ /// purposes of lease limiting should have the following format
+ /// (not all nodes are mandatory and values are given only as examples):
+ /// { "ISC": { "limits": { "client-classes": [ { "name": "foo", "address-limit": 2 } ],
+ /// "subnet": { "id": 1, "address-limit": 2 } } } }
+ ///
+ /// @return a string describing a limit that is being exceeded, or an empty
+ /// string if no limits are exceeded
+ std::string checkLimits4(isc::data::ConstElementPtr const& user_context) const override;
+
+ /// @brief Checks if the IPv6 lease limits set in the given user context are exceeded.
+ /// MySQL implementation.
+ ///
+ /// @param user_context all or part of the lease's user context which, for the intents and
+ /// purposes of lease limiting should have the following format
+ /// (not all nodes are mandatory and values are given only as examples):
+ /// { "ISC": { "limits": { "client-classes": [ { "name": "foo", "address-limit": 2, "prefix-limit": 1 } ],
+ /// "subnet": { "id": 1, "address-limit": 2, "prefix-limit": 1 } } } }
+ ///
+ /// @return a string describing a limit that is being exceeded, or an empty
+ /// string if no limits are exceeded
+ std::string checkLimits6(isc::data::ConstElementPtr const& user_context) const override;
+
/// @brief Context RAII Allocator.
class PgSqlLeaseContextAlloc {
public:
isc_throw(NotImplemented, "ConcreteLeaseMgr::wipeLeases6 not implemented");
}
+ std::string checkLimits4(isc::data::ConstElementPtr const& user_context) const override {
+ isc_throw(NotImplemented, "ConcreteLeaseMgr::checkLimits4() not implemented");
+ }
+
+ std::string checkLimits6(isc::data::ConstElementPtr const& user_context) const override{
+ isc_throw(NotImplemented, "ConcreteLeaseMgr::checkLimits6() not implemented");
+ }
+
/// @brief Returns backend type.
///
/// Returns the type of the backend (e.g. "mysql", "memfile" etc.)