2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-08-31 14:05:33 +00:00

Merge remote-tracking branch 'isc-kea/master' into cassandra_improvements

This commit is contained in:
Razvan Becheriu
2018-02-28 10:29:23 +02:00
36 changed files with 1213 additions and 170 deletions

0
.gitmodules vendored Normal file
View File

View File

@@ -1,3 +1,27 @@
1365. [func] fdupont
Added initial skeleton implementation for Radius hook library.
(Trac #5524, git 832aa23b89eab71875bcbdb1e955eb92fdc0e01a)
1364. [func] fdupont
Implemented FNV hashing function. Cassandra backend no longer
explicitly depends on OpenSSL.
(Trac #5502, git 71de75c3bb099f21fdef0d41806da281d6271287)
1363. [func] fdupont
Both DHCPv4 and DHCPv6 servers can now listen on loopback
interfaces. This capability requires setting socket type to UDP in
DHCPv4. Note the feature has not been thoroughly tested.
(Trac #5390, git f38cbd73581a7a0f8634a63cb17f9b60407e3acc)
1362. [func] fdupont
Extended forensic (aka legal) logging with database capability.
(Trac #5420, git 94bd3cc313e9f2a982ef8f8adf0cf44024c76499)
1361. [func] tmark
Added support for automatically recalculating lease statistics
to the CQL backend.
(Trac 5487, git c807388d581ee1c3e479324f3c399f27feba1c96)
1360. [func] razvan, andrei
A new parameter exit-wait-time has been added to perfdhcp. It is
now possible to tell perfdhcp to wait certain amount of time after

View File

@@ -10,9 +10,12 @@ GENHTML=@GENHTML@
DISTCHECK_GTEST_CONFIGURE_FLAG=@DISTCHECK_GTEST_CONFIGURE_FLAG@
DISTCHECK_CRYPTO_CONFIGURE_FLAG=@DISTCHECK_CRYPTO_CONFIGURE_FLAG@
DISTCHECK_BOOST_CONFIGURE_FLAG=@DISTCHECK_BOOST_CONFIGURE_FLAG@
OVERALL_COVERAGE_DIR=$(abs_top_builddir)/coverage-cpp-html
DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG=@DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG@
DISTCHECK_KEA_SHELL_CONFIGURE_FLAG=@DISTCHECK_KEA_SHELL_CONFIGURE_FLAG@
DISTCHECK_PREMIUM_CONFIGURE_FLAG=@DISTCHECK_PREMIUM_CONFIGURE_FLAG@
DISTCHECK_CONTRIB_CONFIGURE_FLAG=@DISTCHECK_CONTRIB_CONFIGURE_FLAG@
OVERALL_COVERAGE_DIR=$(abs_top_builddir)/coverage-cpp-html
DISTCLEANFILES = config.report
@@ -34,6 +37,12 @@ DISTCHECK_CONFIGURE_FLAGS += $(DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG)
# Keep kea-shell if enabled
DISTCHECK_CONFIGURE_FLAGS += $(DISTCHECK_KEA_SHELL_CONFIGURE_FLAG)
# Keep the premium config
DISTCHECK_CONFIGURE_FLAGS += $(DISTCHECK_PREMIUM_CONFIGURE_FLAG)
# Keep the contrib config
DISTCHECK_CONFIGURE_FLAGS += $(DISTCHECK_CONTRIB_CONFIGURE_FLAG)
dist_doc_DATA = AUTHORS COPYING ChangeLog README
.PHONY: check-valgrind check-valgrind-suppress

View File

@@ -74,20 +74,24 @@ AM_COND_IF([DEBUG_ENABLED], [AC_DEFINE([ENABLE_DEBUG], [1], [Enable low-performi
# Include premium configuration
PREMIUM_DIR=
DISTCHECK_PREMIUM_CONFIGURE_FLAG=
AC_DEFUN([AX_PREMIUM],[])
# m4_sinclude includes the file if it exists at autoreconf time
m4_sinclude(premium/config.m4)
m4_sinclude(premium/tier1.m4)
m4_sinclude(premium/tier2.m4)
AC_SUBST(PREMIUM_DIR)
AC_SUBST(DISTCHECK_PREMIUM_CONFIGURE_FLAG)
AX_PREMIUM
# Include contrib configuration
# (currently only a provision copied from premium support)
CONTRIB_DIR=
DISTCHECK_CONTRIB_CONFIGURE_FLAG=
AC_DEFUN([AX_CONTRIB],[])
m4_sinclude(contrib/config.m4)
AC_SUBST(CONTRIB_DIR)
AC_SUBST(DISTCHECK_CONTRIB_CONFIGURE_FLAG)
AX_CONTRIB
# Libtool configuration
@@ -233,7 +237,7 @@ fi
AC_SUBST(WARNING_GCC_44_STRICT_ALIASING_CFLAG)
CPPP="$CPP"
# gcc 5 preprocessor requires -P for checking its output
if test "$CXX_DUMP_VERSION" \> "5"; then
if expr "$CXX_DUMP_VERSION" \> "5" > /dev/null; then
CPPP="$CPP -P"
fi
@@ -1648,6 +1652,15 @@ Google Benchmark:
END
fi
if test "$FREERADIUS_INCLUDE" != ""; then
cat >> config.report << END
FreeRADIUS client:
FREERADIUS_INCLUDE: ${FREERADIUS_INCLUDE}
FREERADIUS_LIB: ${FREERADIUS_LIB}
END
fi
cat >> config.report << END
Developer:

View File

@@ -267,7 +267,7 @@
<!-- @todo: document lease file upgrades once they are implemented in kea-admin -->
</section>
<section>
<section id="mysql-database">
<title>MySQL</title>
<para>
@@ -408,7 +408,7 @@ $ <userinput>kea-admin lease-upgrade mysql -u <replaceable>database-user</replac
</section>
</section> <!-- end of MySQL sections -->
<section>
<section id="pgsql-database">
<title>PostgreSQL</title>
<para>
@@ -589,7 +589,7 @@ $ <userinput>kea-admin lease-upgrade pgsql -u <replaceable>database-user</replac
</section>
</section> <!-- end of PostgreSQL sections -->
<section>
<section id="cql-database">
<title>CQL (Cassandra)</title>
<para>

View File

@@ -773,6 +773,24 @@ temporarily override a list of interface names and listen on all interfaces.
Note interfaces are not re-detected during <command>config-test</command>.
</para>
<para>Usually loopback interfaces (e.g. the "lo" or "lo0" interface)
may not be configured but if a loopback interface is explicitely configured
and IP/UDP sockets are specified the loopback interface is accepted.
</para>
<para>It can be used for instance to run Kea in a FreeBSD jail having
only a loopback interface, servicing relayed DHCP request:
<screen>
"Dhcp4": {
"interfaces-config": {
"interfaces": [ <userinput>"lo0"</userinput> ],
"dhcp-socket-type": "udp"
},
...
}</screen>
</para>
</section>
<section xml:id="dhcpinform-unicast-issues">

View File

@@ -655,6 +655,21 @@ temporarily override a list of interface names and listen on all interfaces.
}
</screen>
<para>The loopback interfaces (i.e. the "lo" or "lo0" interface)
are not configured by default, unles explicitely mentioned in
the configration. Note Kea requires a link-local address which does
not exist on all systems, or a specified unicast address as in:
</para>
<screen>
"Dhcp6": {
"interfaces-config": {
"interfaces": [ <userinput>"lo/::1"</userinput> ]
},
...
}
</screen>
</section>
<section xml:id="ipv6-subnet-id">

View File

@@ -7,6 +7,7 @@
-->
<!-- Converted by db4-upgrade version 1.1 -->
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="hooks-libraries">
<title>Hooks Libraries</title>
<section xml:id="hooks-libraries-introduction">
<title>Introduction</title>
@@ -439,15 +440,20 @@ path/base-name.CCYYMMDD.txt
(for new leases) and lease4_renew (for renewed leases) hooks.
</para>
<para>
An entry is a single string with no embedded end-of-line markers
and has the following sections:
An entry is a single string with no embedded end-of-line markers,
a prepended timestamp and has the following sections:
<screen>
address duration device-id {client-info} {relay-info}
timestamp address duration device-id {client-info} {relay-info}
</screen>
</para>
<para>
Where:
<itemizedlist>
<listitem><para>
timestamp - the current date and time the log entry was written
in "%Y-%m-%d %H:%M:%S %Z" strftime format ("%Z" is the time zone
name).
</para></listitem>
<listitem><para>
address - the leased IPv4 address given out and whether it was
assigned or renewed.
@@ -477,10 +483,7 @@ address duration device-id {client-info} {relay-info}
For instance (line breaks added for readability, they would not
be present in the log file).
<screen>
Address: 192.2.1.100 has been renewed for 1 hrs 52 min 15 secs to a device with
hardware address: hwtype=1 08:00:2b:02:3f:4e, client-id: 17:34:e2:ff:09:92:54
connected via relay at address: 192.2.16.33, identified by circuit-id:
68:6f:77:64:79 and remote-id: 87:f6:79:77:ef
2018-01-06 01:02:03 CET Address: 192.2.1.100 has been renewed for 1 hrs 52 min 15 secs to a device with hardware address: hwtype=1 08:00:2b:02:3f:4e, client-id: 17:34:e2:ff:09:92:54 connected via relay at address: 192.2.16.33, identified by circuit-id: 68:6f:77:64:79 and remote-id: 87:f6:79:77:ef
</screen>
</para>
<para>
@@ -493,7 +496,7 @@ connected via relay at address: 192.2.16.33, identified by circuit-id:
<para>
<command>lease4-add:</command>
<screen>
Administrator added a lease of address: *address* to a device with hardware address: *device-id*
*timestamp* Administrator added a lease of address: *address* to a device with hardware address: *device-id*
</screen>
Dependent on the arguments of the add command, it may also include the
client-id and duration.
@@ -501,13 +504,13 @@ Administrator added a lease of address: *address* to a device with hardware addr
<para>
Example:
<screen>
Administrator added a lease of address: 192.0.2.202 to a device with hardware address: 1a:1b:1c:1d:1e:1f for 1 days 0 hrs 0 mins 0 secs
2018-01-06 01:02:03 CET Administrator added a lease of address: 192.0.2.202 to a device with hardware address: 1a:1b:1c:1d:1e:1f for 1 days 0 hrs 0 mins 0 secs
</screen>
</para>
<para>
<command>lease4-update:</command>
<screen>
Administrator updated information on the lease of address: *address* to a device with hardware address: *device-id*
*timestamp* Administrator updated information on the lease of address: *address* to a device with hardware address: *device-id*
</screen>
Dependent on the arguments of the update command, it may also include the
client-id and lease duration.
@@ -515,27 +518,27 @@ Administrator updated information on the lease of address: *address* to a device
<para>
Example:
<screen>
Administrator updated information on the lease of address: 192.0.2.202 to a device with hardware address: 1a:1b:1c:1d:1e:1f, client-id: 1234567890
2018-01-06 01:02:03 CET Administrator updated information on the lease of address: 192.0.2.202 to a device with hardware address: 1a:1b:1c:1d:1e:1f, client-id: 1234567890
</screen>
</para>
<para>
<command>lease4-del:</command>
Deletes have two forms, one by address and one by identifier and identifier type:
<screen>
Administrator deleted the lease for address: *address*
*timestamp* Administrator deleted the lease for address: *address*
</screen>
or
<screen>
Administrator deleted a lease for a device identified by: *identifier-type* of *identifier*
*timestamp* Administrator deleted a lease for a device identified by: *identifier-type* of *identifier*
</screen>
Currently only a type of @b hw-address (hardware address) is supported.
</para>
<para>
Examples:
<screen>
Administrator deleted the lease for address: 192.0.2.202
2018-01-06 01:02:03 CET Administrator deleted the lease for address: 192.0.2.202
Administrator deleted a lease for a device identified by: hw-address of 1a:1b:1c:1d:1e:1f
2018-01-06 01:02:12 CET Administrator deleted a lease for a device identified by: hw-address of 1a:1b:1c:1d:1e:1f
</screen>
</para>
</section>
@@ -548,15 +551,20 @@ Administrator deleted a lease for a device identified by: hw-address of 1a:1b:1c
(for renewed leases) and lease6_rebind (for rebound leases).
</para>
<para>
An entry is a single string with no embedded end-of-line markers
and has the following sections:
An entry is a single string with no embedded end-of-line markers,
a prepended timestamp and has the following sections:
<screen>
address duration device-id {relay-info}*
timestamp address duration device-id {relay-info}*
</screen>
</para>
<para>
Where:
<itemizedlist>
<listitem><para>
timestamp - the current date and time the log entry was written
in "%Y-%m-%d %H:%M:%S %Z" strftime format ("%Z" is the time zone
name).
</para></listitem>
<listitem><para>
address - the leased IPv6 address or prefix given out and whether
it was assigned or renewed.
@@ -585,11 +593,7 @@ address duration device-id {relay-info}*
For instance (line breaks added for readability, they would not
be present in the log file).
<screen>
Address:2001:db8:1:: has been assigned for 0 hrs 11 mins 53 secs to a device with
DUID: 17:34:e2:ff:09:92:54 and hardware address: hwtype=1 08:00:2b:02:3f:4e
(from Raw Socket) connected via relay at address: fe80::abcd for client on
link address: 3001::1, hop count: 1, identified by remote-id:
01:02:03:04:0a:0b:0c:0d:0e:0f and subscriber-id: 1a:2b:3c:4d:5e:6f
2018-01-06 01:02:03 PST Address:2001:db8:1:: has been assigned for 0 hrs 11 mins 53 secs to a device with DUID: 17:34:e2:ff:09:92:54 and hardware address: hwtype=1 08:00:2b:02:3f:4e (from Raw Socket) connected via relay at address: fe80::abcd for client on link address: 3001::1, hop count: 1, identified by remote-id: 01:02:03:04:0a:0b:0c:0d:0e:0f and subscriber-id: 1a:2b:3c:4d:5e:6f
</screen>
</para>
<para>
@@ -602,47 +606,47 @@ link address: 3001::1, hop count: 1, identified by remote-id:
<para>
<command>lease6-add:</command>
<screen>
Administrator added a lease of address: *address* to a device with DUID: *DUID*
*timestamp* Administrator added a lease of address: *address* to a device with DUID: *DUID*
</screen>
Dependent on the arguments of the add command, it may also include the hardware address and duration.
</para>
<para>
Example:
<screen>
Administrator added a lease of address: 2001:db8::3 to a device with DUID: 1a:1b:1c:1d:1e:1f:20:21:22:23:24 for 1 days 0 hrs 0 mins 0 secs
2018-01-06 01:02:03 PST Administrator added a lease of address: 2001:db8::3 to a device with DUID: 1a:1b:1c:1d:1e:1f:20:21:22:23:24 for 1 days 0 hrs 0 mins 0 secs
</screen>
</para>
<para>
<command>lease6-update:</command>
<screen>
Administrator updated information on the lease of address: *address* to a device with DUID: *DUID*
*timestamp* Administrator updated information on the lease of address: *address* to a device with DUID: *DUID*
</screen>
Dependent on the arguments of the update command, it may also include the hardware address and lease duration.
</para>
<para>
Example:
<screen>
Administrator updated information on the lease of address: 2001:db8::3 to a device with DUID: 1a:1b:1c:1d:1e:1f:20:21:22:23:24, hardware address: 1a:1b:1c:1d:1e:1f
2018-01-06 01:02:03 PST Administrator updated information on the lease of address: 2001:db8::3 to a device with DUID: 1a:1b:1c:1d:1e:1f:20:21:22:23:24, hardware address: 1a:1b:1c:1d:1e:1f
</screen>
</para>
<para>
<command>lease6-del:</command>
Deletes have two forms, one by address and one by identifier and identifier type:
<screen>
Administrator deleted the lease for address: *address*
*timestamp* Administrator deleted the lease for address: *address*
</screen>
or
<screen>
Administrator deleted a lease for a device identified by: *identifier-type* of *identifier*
*timestamp* Administrator deleted a lease for a device identified by: *identifier-type* of *identifier*
</screen>
Currently only a type of DUID is supported.
</para>
<para>
Examples:
<screen>
Administrator deleted the lease for address: 2001:db8::3
2018-01-06 01:02:03 PST Administrator deleted the lease for address: 2001:db8::3
Administrator deleted a lease for a device identified by: duid of 1a:1b:1c:1d:1e:1f:20:21:22:23:24
2018-01-06 01:02:11 PST Administrator deleted a lease for a device identified by: duid of 1a:1b:1c:1d:1e:1f:20:21:22:23:24
</screen>
</para>
</section>
@@ -759,6 +763,60 @@ Administrator deleted a lease for a device identified by: duid of 1a:1b:1c:1d:1e
to learn more about user contexts in Kea configuration.
</para>
</section>
<section id="forensic-log-database">
<title>Database backend</title>
<para>
Log entries can be inserted into a database when Kea is configured
with database backend support: a table named 'logs' is used with a
timestamp (timeuuid for Cassandra CQL) generated by the database
software and a text log with the same format than for files
without the timestamp.
</para>
<para>
Please refer to <xref linkend="mysql-database"/> for MySQL,
to <xref linkend="pgsql-database"/> for PostgreSQL or
to <xref linkend="cql-database"/> for Cassandra CQL.
Scripts are in
<filename><replaceable>path-to-kea</replaceable>/share/kea/legal_log/scripts</filename> directory, for instance the PostgreSQL create schema
command is:
<screen>
$ <userinput>psql -d <replaceable>database-name</replaceable> -U <replaceable>user-name</replaceable> -f <replaceable>path-to-kea</replaceable>/share/kea/legal_log/scripts/pgsql/legldb_create.pgsql</userinput>
Password for user <replaceable>user-name</replaceable>:
START TRANSACTION
CREATE TABLE
CREATE INDEX
CREATE TABLE
INSERT 0 1
COMMIT
$
</screen>
</para>
<para>
Configuration parameters are extended by standard lease database
parameters as defined in <xref linkend="database-configuration4"/>.
The "type" parameter should be "mysql", "postgresql", "cql" or
be "logfile". When it is absent or set to "logfile" files are
used.
</para>
<para>
This database feature is experimental and will be likely
improved, for instance to add an address / prefix index (currently
the only index is the timestamp). No specific tools is provided
to operate the database but standard tools are applicable,
for instance to dump the logs table from a CQL database:
<screen>
$ <userinput>echo 'SELECT dateOf(timeuuid), log FROM logs;' | cqlsh -k <replaceable>database-name</replaceable></userinput>
system.dateof(timeuuid) | log
---------------------------------+---------------------------------------
2018-01-06 01:02:03.227000+0000 | Address: 192.2.1.100 has been renewed ...
...
(12 rows)
$
</screen>
</para>
</section>
</section>
<section xml:id="flex-id">

View File

@@ -58,6 +58,24 @@ for retry in "none" "--std=c++11" "--std=c++0x" "--std=c++1x" "fail"; do
[AC_MSG_RESULT([no])
continue])
AC_MSG_CHECKING(override method support)
feature="override method"
AC_COMPILE_IFELSE(
[AC_LANG_PROGRAM(
[class Foo {
public:
virtual ~Foo() {};
virtual void foobar();
};
class Bar : public Foo {
public:
virtual ~Bar() {};
virtual void foobar() override;
};],[])],
[AC_MSG_RESULT([yes])],
[AC_MSG_RESULT([no])
continue])
AC_MSG_CHECKING(aggregate initialization support)
feature="aggregate initialization"
AC_COMPILE_IFELSE(
@@ -107,6 +125,19 @@ for retry in "none" "--std=c++11" "--std=c++0x" "--std=c++1x" "fail"; do
[AC_MSG_RESULT([no])
continue])
AC_MSG_CHECKING(constexpr support)
feature="constexpr"
AC_COMPILE_IFELSE(
[AC_LANG_PROGRAM(
[#include <string>
typedef char const* const Tag;
constexpr Tag FOOBAR = "FOOBAR";],
[const std::string foobar(FOOBAR);
return static_cast<int>(foobar.length());])],
[AC_MSG_RESULT([yes])],
[AC_MSG_RESULT([no])
continue])
AC_MSG_CHECKING(lambda support)
feature="lambda"
AC_COMPILE_IFELSE(

View File

@@ -1,4 +1,4 @@
// Copyright (C) 2011-2017 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2011-2018 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -134,7 +134,9 @@ void Iface::setMac(const uint8_t* mac, size_t len) {
<< MAX_MAC_LEN);
}
mac_len_ = len;
memcpy(mac_, mac, len);
if (len > 0) {
memcpy(mac_, mac, len);
}
}
bool Iface::delAddress(const isc::asiolink::IOAddress& addr) {
@@ -170,7 +172,8 @@ IfaceMgr::IfaceMgr()
control_buf_(new char[control_buf_len_]),
packet_filter_(new PktFilterInet()),
packet_filter6_(new PktFilterInet6()),
test_mode_(false)
test_mode_(false),
allow_loopback_(false)
{
try {
@@ -463,7 +466,9 @@ IfaceMgr::openSockets4(const uint16_t port, const bool use_bcast,
// that the interface configuration is valid and that the interface
// is not a loopback interface. In both cases, we want to report
// that the socket will not be opened.
if (iface->flag_loopback_) {
// Relax the check when the loopback interface was explicitely
// allowed
if (iface->flag_loopback_ && !allow_loopback_) {
IFACEMGR_ERROR(SocketConfigError, error_handler,
"must not open socket on the loopback"
" interface " << iface->getName());
@@ -568,7 +573,9 @@ IfaceMgr::openSockets6(const uint16_t port,
// that the interface configuration is valid and that the interface
// is not a loopback interface. In both cases, we want to report
// that the socket will not be opened.
if (iface->flag_loopback_) {
// Relax the check when the loopback interface was explicitely
// allowed
if (iface->flag_loopback_ && !allow_loopback_) {
IFACEMGR_ERROR(SocketConfigError, error_handler,
"must not open socket on the loopback"
" interface " << iface->getName());

View File

@@ -1,4 +1,4 @@
// Copyright (C) 2011-2015,2017 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2011-2018 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -595,6 +595,15 @@ public:
return (test_mode_);
}
/// @brief Allows or disallows the loopback interface
///
/// By default the loopback interface is not considered when opening
/// sockets. This flag provides a way to relax this constraint.
///
void setAllowLoopBack(const bool allow_loopback) {
allow_loopback_ = allow_loopback;
}
/// @brief Check if packet be sent directly to the client having no address.
///
/// Checks if IfaceMgr can send DHCPv4 packet to the client
@@ -838,8 +847,8 @@ public:
///
/// This method opens sockets only on interfaces which have the
/// @c inactive6_ field set to false (are active). If the interface is active
/// but it is not running, it is down, or is a loopback interface,
/// an error is reported.
/// but it is not running, it is down, or is a loopback interface when
/// loopback is not allowed, an error is reported.
///
/// On the systems with multiple interfaces, it is often desired that the
/// failure to open a socket on a particular interface doesn't cause a
@@ -883,8 +892,8 @@ public:
///
/// This method opens sockets only on interfaces which have the
/// @c inactive4_ field set to false (are active). If the interface is active
/// but it is not running, it is down, or is a loopback interface,
/// an error is reported.
/// but it is not running, it is down, or is a loopback interface when
/// oopback is not allowed, an error is reported.
///
/// The type of the socket being open depends on the selected Packet Filter
/// represented by a class derived from @c isc::dhcp::PktFilter abstract
@@ -1217,6 +1226,9 @@ private:
/// @brief Indicates if the IfaceMgr is in the test mode.
bool test_mode_;
/// @brief Allows to use loopback
bool allow_loopback_;
};
}; // namespace isc::dhcp

View File

@@ -1,4 +1,4 @@
// Copyright (C) 2011-2017 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2011-2018 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -1480,6 +1480,35 @@ TEST_F(IfaceMgrTest, openSockets4) {
EXPECT_TRUE(ifacemgr.getIface("lo")->getSockets().empty());
}
// This test verifies that IPv4 sockets are open on the loopback interface
// when the loopback is active and allowed.
TEST_F(IfaceMgrTest, openSockets4Loopback) {
NakedIfaceMgr ifacemgr;
// Remove all real interfaces and create a set of dummy interfaces.
ifacemgr.createIfaces();
// Allow the loopback interface.
ifacemgr.setAllowLoopBack(true);
// Make the loopback interface active.
ifacemgr.getIface("lo")->inactive4_ = false;
// Use the custom packet filter object. This object mimics the socket
// opening operation - the real socket is not open.
boost::shared_ptr<TestPktFilter> custom_packet_filter(new TestPktFilter());
ASSERT_TRUE(custom_packet_filter);
ASSERT_NO_THROW(ifacemgr.setPacketFilter(custom_packet_filter));
// Simulate opening sockets using the dummy packet filter.
ASSERT_NO_THROW(ifacemgr.openSockets4(DHCP4_SERVER_PORT, true, 0));
// Expect that the sockets are open on all interfaces.
EXPECT_EQ(1, ifacemgr.getIface("eth0")->getSockets().size());
EXPECT_EQ(1, ifacemgr.getIface("eth1")->getSockets().size());
EXPECT_EQ(1, ifacemgr.getIface("lo")->getSockets().size());
}
// This test verifies that the socket is not open on the interface which is
// down, but sockets are open on all other non-loopback interfaces.
TEST_F(IfaceMgrTest, openSockets4IfaceDown) {
@@ -1688,6 +1717,40 @@ TEST_F(IfaceMgrTest, openSockets6LinkLocal) {
#endif
}
// This test checks that the sockets are open on the loopback interface
// when the loopback is active and allowed.
TEST_F(IfaceMgrTest, openSockets6Loopback) {
NakedIfaceMgr ifacemgr;
// Remove all real interfaces and create a set of dummy interfaces.
ifacemgr.createIfaces();
// Allow the loopback interface.
ifacemgr.setAllowLoopBack(true);
// Make the loopback interface active.
ifacemgr.getIface("lo")->inactive6_ = false;
// The loopback interface has no link-local (as for Linux but not BSD)
// so add one.
ifacemgr.getIface("lo")->addUnicast(IOAddress("::1"));
boost::shared_ptr<PktFilter6Stub> filter(new PktFilter6Stub());
ASSERT_TRUE(filter);
ASSERT_NO_THROW(ifacemgr.setPacketFilter(filter));
// Simulate opening sockets using the dummy packet filter.
bool success = false;
ASSERT_NO_THROW(success = ifacemgr.openSockets6(DHCP6_SERVER_PORT));
EXPECT_TRUE(success);
// Check that the loopback interface has at least an open socket.
EXPECT_EQ(1, ifacemgr.getIface("lo")->getSockets().size());
// This socket should be bound to ::1
EXPECT_TRUE(ifacemgr.isBound("lo", "::1"));
}
// This test checks that socket is not open on the interface which doesn't
// have a link-local address.
TEST_F(IfaceMgrTest, openSockets6NoLinkLocal) {

View File

@@ -17,7 +17,7 @@ if HAVE_PGSQL
AM_CPPFLAGS += $(PGSQL_CPPFLAGS)
endif
if HAVE_CQL
AM_CPPFLAGS += $(CQL_CPPFLAGS) $(CRYPTO_INCLUDES)
AM_CPPFLAGS += $(CQL_CPPFLAGS)
endif
AM_CXXFLAGS = $(KEA_CXXFLAGS)
@@ -114,8 +114,10 @@ libkea_dhcpsrv_la_SOURCES += d2_client_mgr.cc d2_client_mgr.h
libkea_dhcpsrv_la_SOURCES += daemon.cc daemon.h
libkea_dhcpsrv_la_SOURCES += database_connection.cc database_connection.h
libkea_dhcpsrv_la_SOURCES += db_exceptions.h
libkea_dhcpsrv_la_SOURCES += db_log.cc db_log.h
libkea_dhcpsrv_la_SOURCES += dhcp4o6_ipc.cc dhcp4o6_ipc.h
libkea_dhcpsrv_la_SOURCES += dhcpsrv_log.cc dhcpsrv_log.h
libkea_dhcpsrv_la_SOURCES += dhcpsrv_db_log.cc dhcpsrv_db_log.h
libkea_dhcpsrv_la_SOURCES += host.cc host.h
libkea_dhcpsrv_la_SOURCES += host_container.h
libkea_dhcpsrv_la_SOURCES += host_data_source_factory.cc host_data_source_factory.h

View File

@@ -22,14 +22,15 @@ $ make
The benchmarks are built in @b src/lib/dhcpsrv/benchmarks directory.
Note that the benchmarks are backend-specific, so please make sure you
enable the backends you want to measure (See --with-mysql, --with-pgsql,
--with-cql parameters for configure). Once the benchmark is built, you
can run specific benchmarks. Make sure you have the environment ready
for use, i.e. the actual database backend is set up, the DB or keyspace
is created. The setup is the same as for running unit-tests.
DB name or keyspace should be keatest, and username keatest,
password keatest should give you full write access to the database.
Any data present in the DB before the tests will be removed.
enable the backends you want to measure (See --with-mysql,
--with-pgsql --with-cql parameters for configure). Once the
benchmark is built, you can run specific benchmarks. Make sure you
have the environment ready for use, i.e. the actual database backend
is set up, the DB or keyspace is created. The setup is the same as for
running unit-tests. DB name or keyspace should be keatest, and
username keatest, password keatest should give you full write access
to the database. Any data present in the DB before the tests will be
removed.
To get a list of available benchmarks, use the following command:

View File

@@ -1,4 +1,4 @@
// Copyright (C) 2014-2015,2017 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2014-2018 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -56,15 +56,39 @@ CfgIface::openSockets(const uint16_t family, const uint16_t port,
// Close any open sockets because we're going to modify some properties
// of the IfaceMgr. Those modifications require that sockets are closed.
closeSockets();
// The loopback interface can be used only when:
// - UDP socket will be used, i.e. not IPv4 and RAW socket
// - the loopback interface is in the interface set or the address map.
bool loopback_used_ = false;
if ((family == AF_INET6) || (socket_type_ == SOCKET_UDP)) {
// Check interface set
for (IfaceSet::const_iterator iface_name = iface_set_.begin();
iface_name != iface_set_.end(); ++iface_name) {
IfacePtr iface = IfaceMgr::instance().getIface(*iface_name);
if (iface && iface->flag_loopback_) {
loopback_used_ = true;
}
}
// Check address map
for (ExplicitAddressMap::const_iterator unicast = address_map_.begin();
unicast != address_map_.end(); ++unicast) {
IfacePtr iface = IfaceMgr::instance().getIface(unicast->first);
if (iface && iface->flag_loopback_) {
loopback_used_ = true;
}
}
}
// If wildcard interface '*' was not specified, set all interfaces to
// inactive state. We will later enable them selectively using the
// interface names specified by the user. If wildcard interface was
// specified, mark all interfaces active. In all cases, mark loopback
// inactive.
setState(family, !wildcard_used_, true);
// specified, mark all interfaces active. Mark loopback inactive when
// not explicitely allowed.
setState(family, !wildcard_used_, !loopback_used_);
IfaceMgr& iface_mgr = IfaceMgr::instance();
// Remove selection of unicast addresses from all interfaces.
iface_mgr.clearUnicasts();
// Allow the loopback interface when required.
iface_mgr.setAllowLoopBack(loopback_used_);
// For the DHCPv4 server, if the user has selected that raw sockets
// should be used, we will try to configure the Interface Manager to
// support the direct responses to the clients that don't have the

View File

@@ -1,4 +1,4 @@
// Copyright (C) 2015-2017 Deutsche Telekom AG.
// Copyright (C) 2015-2018 Deutsche Telekom AG.
//
// Authors: Razvan Becheriu <razvan.becheriu@qualitance.com>
// Andrei Pavel <andrei.pavel@qualitance.com>
@@ -20,7 +20,7 @@
#include <dhcpsrv/cql_connection.h>
#include <dhcpsrv/cql_exchange.h>
#include <dhcpsrv/db_exceptions.h>
#include <dhcpsrv/dhcpsrv_log.h>
#include <dhcpsrv/db_log.h>
#include <string>
@@ -69,7 +69,7 @@ CqlConnection::~CqlConnection() {
if (rc != CASS_OK) {
// We're closing the connection anyway. Let's not throw at this stage.
LOG_ERROR(dhcpsrv_logger, DHCPSRV_CQL_DEALLOC_ERROR).arg(error);
DB_LOG_ERROR(CQL_DEALLOC_ERROR).arg(error);
}
}
@@ -349,20 +349,17 @@ CqlConnection::setConsistency(bool force, CassConsistency consistency) {
void
CqlConnection::startTransaction() {
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
DHCPSRV_CQL_CONNECTION_BEGIN_TRANSACTION);
DB_LOG_DEBUG(DB_DBG_TRACE_DETAIL, CQL_CONNECTION_BEGIN_TRANSACTION);
}
void
CqlConnection::commit() {
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
DHCPSRV_CQL_CONNECTION_COMMIT);
DB_LOG_DEBUG(DB_DBG_TRACE_DETAIL, CQL_CONNECTION_COMMIT);
}
void
CqlConnection::rollback() {
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
DHCPSRV_CQL_CONNECTION_ROLLBACK);
DB_LOG_DEBUG(DB_DBG_TRACE_DETAIL, CQL_CONNECTION_ROLLBACK);
}
const std::string

View File

@@ -28,13 +28,10 @@
#include <dhcpsrv/db_exceptions.h>
#include <dhcpsrv/dhcpsrv_log.h>
#include <util/buffer.h>
#include <util/hash.h>
#include <util/optional_value.h>
#include <asiolink/io_address.h>
/// @todo: With this include, Cassandra backend requires compilation with openssl.
/// Kea supports two crypto libs: openssl and botan. The abstraction layer provided
/// is via cryptolink.
#include <openssl/md5.h> // for MD5_DIGEST_LENGTH
#include <stdint.h> // for uint64_t
#include <boost/algorithm/string/classification.hpp> // for boost::is_any_of
@@ -148,8 +145,7 @@ public:
/// @brief Constructor
///
/// Specifies table columns.
/// @param connection specifies the connection to conduct this exchange on
CqlHostExchange(CqlConnection& connection);
CqlHostExchange();
/// @brief Virtual destructor.
virtual ~CqlHostExchange();
@@ -227,9 +223,8 @@ public:
/// @brief Create unique hash for storage in table id.
///
/// Hash function used for creating a pseudo-unique hash from member
/// values which uniquely determine an entry in the table. Uses OpenSSL's
/// MD5 implementation.
/// @todo: This must be generic and use cryptolink wrapper. See ticket #5502.
/// values which uniquely determine an entry in the table. Uses FNV-1a
/// on 64 bits.
///
/// The primary key aggregates: host_ipv4_subnet_id, host_ipv6_subnet_id,
/// host_ipv4_address, reserved_ipv6_prefix_address,
@@ -324,9 +319,6 @@ private:
/// Pointer to Host object holding information being inserted into database.
HostPtr host_;
/// @brief Connection to the Cassandra database
CqlConnection& connection_;
/// @brief Primary key. Aggregates: host_identifier, host_identifier_type,
/// reserved_ipv6_prefix_address, reserved_ipv6_prefix_length, option_code,
/// option_space.
@@ -760,10 +752,9 @@ StatementMap CqlHostExchange::tagged_statements_ = {
}}
};
CqlHostExchange::CqlHostExchange(CqlConnection& connection)
: host_(NULL), connection_(connection), id_(0), host_identifier_type_(0),
host_ipv4_subnet_id_(0), host_ipv6_subnet_id_(0), host_ipv4_address_(0),
host_ipv4_next_server_(0),
CqlHostExchange::CqlHostExchange()
: host_(NULL), id_(0), host_identifier_type_(0), host_ipv4_subnet_id_(0),
host_ipv6_subnet_id_(0), host_ipv4_address_(0), host_ipv4_next_server_(0),
host_ipv4_server_hostname_(NULL_DHCP4_SERVER_HOSTNAME),
host_ipv4_boot_file_name_(NULL_DHCP4_BOOT_FILE_NAME),
user_context_(NULL_USER_CONTEXT),
@@ -1115,31 +1106,10 @@ CqlHostExchange::createBindForDelete(const HostPtr& host,
}
}
uint64_t
md5Hash(const std::string& input) {
/// @todo: Convert this code to cryptolink calls and replace the
/// direct use fromn md5.
// Prepare structures for MD5().
const size_t word_size = MD5_DIGEST_LENGTH / sizeof(uint64_t);
uint64_t hash[word_size];
unsigned char* digest = reinterpret_cast<unsigned char*>(hash);
unsigned char* string = reinterpret_cast<unsigned char*>(const_cast<char*>(input.c_str()));
std::fill(hash, hash + word_size, 0);
// Get MD5 hash value.
MD5(string, input.size(), digest);
// Return the first part of the hash value which still retains all
// properties of the full hash value.
return (hash[0]);
}
cass_int64_t
CqlHostExchange::hashIntoId() const {
// Allocates a fixed maximum length in the stringstream for each
// aggregated field to avoid collisions between distinct entries.
// Add a separator between aggregated field to avoid collisions
// between distinct entries.
// Get key.
std::stringstream key_stream;
@@ -1165,9 +1135,9 @@ CqlHostExchange::hashIntoId() const {
<< option_space_;
const std::string key = key_stream.str();
const cass_int64_t md5 = static_cast<cass_int64_t>(md5Hash(key));
const cass_int64_t hash = static_cast<cass_int64_t>(Hash64::hash(key));
return (md5);
return (hash);
}
boost::any
@@ -1598,7 +1568,7 @@ private:
/// @brief hash function for HostMap
///
/// Returns a 64-bits key value. The key is generated with MD5 hash
/// Returns a 64-bits key value. The key is generated with FNV-1a 64 bit
/// algorithm.
///
/// @param key being hashed
@@ -1609,21 +1579,16 @@ hash_value(const HostKey& key) {
// Get key.
std::stringstream key_stream;
HostIdentifier host_identifier = std::get<HOST_IDENTIFIER>(key);
key_stream << std::setw(DUID::MAX_DUID_LEN) << std::setfill('0')
<< DUID(host_identifier).toText();
key_stream << std::setw(2) << std::setfill('0')
<< std::get<HOST_IDENTIFIER_TYPE>(key);
key_stream << std::setw(10) << std::setfill('0')
<< std::get<IPv4_SUBNET_ID>(key);
key_stream << std::setw(10) << std::setfill('0')
<< std::get<IPv6_SUBNET_ID>(key);
key_stream << std::setw(V4ADDRESS_TEXT_MAX_LEN) << std::setfill('0')
<< std::get<IPv4_RESERVATION>(key);
key_stream << DUID(host_identifier).toText() << "-";
key_stream << std::get<HOST_IDENTIFIER_TYPE>(key) << "-";
key_stream << std::get<IPv4_SUBNET_ID>(key) << "-";
key_stream << std::get<IPv6_SUBNET_ID>(key) << "-";
key_stream << std::get<IPv4_RESERVATION>(key);
const std::string key_string = key_stream.str();
const uint64_t md5 = md5Hash(key_string);
const uint64_t hash = Hash64::hash(key_string);
return (static_cast<std::size_t>(md5));
return (static_cast<std::size_t>(hash));
}
/// @brief equals operator for HostKey
@@ -2071,7 +2036,7 @@ CqlHostDataSourceImpl::getHostCollection(StatementTag statement_tag,
AnyArray& where_values) const {
// Run statement.
std::unique_ptr<CqlHostExchange> host_exchange(new CqlHostExchange(dbconn_));
std::unique_ptr<CqlHostExchange> host_exchange(new CqlHostExchange());
AnyArray collection = host_exchange->executeSelect(dbconn_, where_values,
statement_tag, false);
@@ -2113,7 +2078,7 @@ CqlHostDataSourceImpl::insertOrDeleteHost(bool insert,
const OptionDescriptor& option_descriptor) {
AnyArray assigned_values;
std::unique_ptr<CqlHostExchange> host_exchange(new CqlHostExchange(dbconn_));
std::unique_ptr<CqlHostExchange> host_exchange(new CqlHostExchange());
try {
if (insert) {

View File

@@ -1,4 +1,4 @@
// Copyright (C) 2016-2017 Deutsche Telekom AG.
// Copyright (C) 2016-2018 Deutsche Telekom AG.
//
// Author: Andrei Pavel <andrei.pavel@qualitance.com>
//

View File

@@ -1454,12 +1454,300 @@ CqlLease6Exchange::getExpiredLeases(const size_t &max_leases,
}
}
/// @brief Base CQL derivation of the statistical lease data query
///
/// This class provides the functionality such as results storage and row
/// fetching common to fulfilling the statistical lease data query.
///
class CqlLeaseStatsQuery : public LeaseStatsQuery {
public:
/// @brief Constructor
///
/// @param conn An open connection to the database housing the lease data
/// @param statement The lease data SQL prepared statement tag to execute
/// @param fetch_type Indicates whether or not lease_type should be
/// fetched from the result set (should be true for v6)
CqlLeaseStatsQuery(CqlConnection& conn, StatementTag& statement,
const bool fetch_type)
: conn_(conn), statement_(statement), fetch_type_(fetch_type),
cummulative_rows_(), next_row_(cummulative_rows_.begin()),
subnet_id_(0), lease_type_(0), lease_state_(0) {
}
/// @brief Destructor
virtual ~CqlLeaseStatsQuery() {};
/// @brief Creates the lease statistical data result set
///
/// The result set is populated by executing a prepared SQL query
/// against the database which sums the leases per lease state per
/// subnet id. Positions internal row tracking to point to the
/// first row of the aggregate results.
void start();
/// @brief Executes protocol specific lease query SELECT statement
///
/// Currently we do not have a good way for Cassandra to roll up the
/// lease counts per subnet, type, and state as we do the other back
/// ends. This method executes the select statement which returns
/// a result set containing a row of data for every lease:
/// -v4 - subnet-id, lease-state
/// -v6 - subnet-id, lease-type, lease-state
///
/// It then iterates over this result set, aggregating the data into a
/// a map of LeaseStatRows.
///
/// If we didn't have to roll up the raw lease data first, we could
/// have derived this class from CqlExchange and used it's executeSelect
/// (from which this method borrows heavily). However, that would mean
/// copying all the raw lease data into a collection returned by
/// executeSelect and then aggregating that into cummulative rows.
/// The way we are now we go turn the raw lease data directly into the
/// cummulative row map.
///
/// @param connection connection used to communicate with the Cassandra
/// database
/// @param where_values array of bound objects used to filter the results
/// @param statement_tag prepared statement being executed
///
/// @throw DbOperationError
void executeSelect(const CqlConnection& connection, const AnyArray& data,
StatementTag statement_tag);
/// @brief Fetches the next row in the result set
///
/// Once the internal result set has been populated by invoking the
/// the start() method, this method is used to iterate over the
/// result set rows. Once the last row has been fetched, subsequent
/// calls will return false.
///
/// @param row Storage for the fetched row
///
/// @return True if the fetch succeeded, false if there are no more
/// rows to fetch.
bool getNextRow(LeaseStatsRow& row);
/// @brief Create BIND array to receive C++ data.
///
/// Used in executeSelect() to retrieve from database
///
/// @param data array of bound objects representing data to be retrieved
/// @param statement_tag prepared statement being executed; defaults to an
/// invalid index
virtual void
createBindForSelect(AnyArray& data, StatementTag statement_tag = NULL);
/// @brief Statement tags definitions
/// @{
// Return recalculated lease4 lease statistics
static constexpr StatementTag RECOUNT_LEASE4_STATS = "RECOUNT_LEASE4_STATS";
// Return recalculated lease6 lease statistics
static constexpr StatementTag RECOUNT_LEASE6_STATS = "RECOUNT_LEASE6_STATS";
/// @}
/// @brief Cassandra statements
static StatementMap tagged_statements_;
private:
/// @brief Database connection to use to execute the query
CqlConnection& conn_;
/// @brief The query's prepared statement tag
StatementTag statement_;
/// @brief Indicates if query supplies lease type
bool fetch_type_;
/// @brief map containing the aggregated lease counts
std::map<LeaseStatsRow, int> cummulative_rows_;
/// @brief cursor pointing to the next row to read in aggregate map
std::map<LeaseStatsRow, int>::iterator next_row_;
/// @brief bind variable for retrieving subnet-id from a result set row
int subnet_id_;
/// @brief bind variable for retrieving lease-type from a result set row
int lease_type_;
/// @brief bind variable for retrieving lease-state from a result set row
int lease_state_;
};
constexpr StatementTag CqlLeaseStatsQuery::RECOUNT_LEASE4_STATS;
constexpr StatementTag CqlLeaseStatsQuery::RECOUNT_LEASE6_STATS;
StatementMap CqlLeaseStatsQuery::tagged_statements_{
// Return subnet_id and state of each v4 lease
{RECOUNT_LEASE4_STATS,
{RECOUNT_LEASE4_STATS,
"SELECT "
"subnet_id, state "
"FROM lease4 "
}},
// Return subnet_id, lease_type, and state of each v6 lease
{RECOUNT_LEASE6_STATS,
{RECOUNT_LEASE6_STATS,
"SELECT "
"subnet_id, lease_type, state "
"FROM lease6 "
}},
};
void
CqlLeaseStatsQuery::start() {
AnyArray data; // there are no where clause parameters
// This gets a collection of data for ALL leases, and
// then rolls them up into cummulative_rows_
executeSelect(conn_, data, statement_);
// Set our row iterator to the beginning
next_row_ = cummulative_rows_.begin();
}
bool
CqlLeaseStatsQuery::getNextRow(LeaseStatsRow& row) {
// If we're past the end, punt.
if (next_row_ == cummulative_rows_.end()) {
return (false);
}
// Start by copying from the map row key
row.subnet_id_ = next_row_->first.subnet_id_;
row.lease_type_ = next_row_->first.lease_type_;
row.lease_state_ = next_row_->first.lease_state_;
// Grab the count from the map value
row.state_count_ = next_row_->second;
// Point to the next row.
++next_row_;
return (true);
}
void
CqlLeaseStatsQuery::createBindForSelect(AnyArray& data, StatementTag) {
data.clear();
data.add(&subnet_id_);
if (fetch_type_) {
data.add(&lease_type_);
}
data.add(&lease_state_);
}
void
CqlLeaseStatsQuery::executeSelect(const CqlConnection& connection, const AnyArray& data,
StatementTag statement_tag) {
CassError rc;
CassStatement* statement = NULL;
CassFuture* future = NULL;
AnyArray local_data = data;
// Find the query statement first.
StatementMap::const_iterator it = connection.statements_.find(statement_tag);
if (it == connection.statements_.end()) {
isc_throw(DbOperationError,
"CqlLeastStatsQuery::executeSelect(): Statement "
<< statement_tag << "has not been prepared.");
}
// Bind the data before the query is executed.
CqlTaggedStatement tagged_statement = it->second;
if (tagged_statement.is_raw_) {
// The entire query is the first element in data.
std::string* query = boost::any_cast<std::string*>(local_data.back());
local_data.pop_back();
statement = cass_statement_new(query->c_str(), local_data.size());
} else {
statement = cass_prepared_bind(tagged_statement.prepared_statement_);
if (!statement) {
isc_throw(DbOperationError,
"CqlLeaseStatsQuery::executeSelect(): unable to bind statement "
<< tagged_statement.name_);
}
}
// Set specific level of consistency if we're told to do so.
if (connection.force_consistency_) {
rc = cass_statement_set_consistency(statement, connection.consistency_);
if (rc != CASS_OK) {
cass_statement_free(statement);
isc_throw(DbOperationError,
"CqlLeaseStatsQuery::executeSelect(): unable to set statement "
"consistency for statement "
<< tagged_statement.name_
<< ", Cassandra error code: " << cass_error_desc(rc));
}
}
CqlCommon::bindData(local_data, statement);
// Everything's ready. Call the actual statement.
future = cass_session_execute(connection.session_, statement);
if (!future) {
cass_statement_free(statement);
isc_throw(DbOperationError,
"CqlLeaseStatsQuery::executeSelect(): no CassFuture for statement "
<< tagged_statement.name_);
}
// Wait for the statement execution to complete.
cass_future_wait(future);
const std::string error = connection.checkFutureError(
"CqlLeaseStatsQuery::executeSelect(): cass_session_execute() != CASS_OK",
future, statement_tag);
rc = cass_future_error_code(future);
if (rc != CASS_OK) {
cass_future_free(future);
cass_statement_free(statement);
isc_throw(DbOperationError, error);
}
// Get column values.
const CassResult* result_collection = cass_future_get_result(future);
// lease type is always NA for v4
if (!fetch_type_) {
lease_type_ = Lease::TYPE_NA;
}
// Since we're currently forced to pull data for all leases, we
// iterate over them, aggregating them into cummulative LeaseStatsRows
AnyArray return_values;
CassIterator* rows = cass_iterator_from_result(result_collection);
while (cass_iterator_next(rows)) {
const CassRow* row = cass_iterator_get_row(rows);
createBindForSelect(return_values, statement_tag);
CqlCommon::getData(row, return_values);
LeaseStatsRow raw_row(subnet_id_, static_cast<Lease::Type>(lease_type_),
lease_state_, 1);
auto cum_row = cummulative_rows_.find(raw_row);
if (cum_row != cummulative_rows_.end()) {
cummulative_rows_[raw_row] = cum_row->second + 1;
} else {
cummulative_rows_.insert(std::make_pair(raw_row, 1));
}
}
// Free resources.
cass_iterator_free(rows);
cass_result_free(result_collection);
cass_future_free(future);
cass_statement_free(statement);
return;
}
CqlLeaseMgr::CqlLeaseMgr(const DatabaseConnection::ParameterMap &parameters)
: LeaseMgr(), dbconn_(parameters) {
dbconn_.openDatabase();
dbconn_.prepareStatements(CqlLease4Exchange::tagged_statements_);
dbconn_.prepareStatements(CqlLease6Exchange::tagged_statements_);
dbconn_.prepareStatements(CqlVersionExchange::tagged_statements_);
dbconn_.prepareStatements(CqlLeaseStatsQuery::tagged_statements_);
}
CqlLeaseMgr::~CqlLeaseMgr() {
@@ -1901,6 +2189,24 @@ CqlLeaseMgr::deleteExpiredReclaimedLeases6(const uint32_t secs) {
return n_of_deleted_leases;
}
LeaseStatsQueryPtr
CqlLeaseMgr::startLeaseStatsQuery4() {
LeaseStatsQueryPtr query(
new CqlLeaseStatsQuery(dbconn_, CqlLeaseStatsQuery::RECOUNT_LEASE4_STATS,
false));
query->start();
return(query);
}
LeaseStatsQueryPtr
CqlLeaseMgr::startLeaseStatsQuery6() {
LeaseStatsQueryPtr query(
new CqlLeaseStatsQuery(dbconn_, CqlLeaseStatsQuery::RECOUNT_LEASE6_STATS,
true));
query->start();
return(query);
}
size_t
CqlLeaseMgr::wipeLeases4(const SubnetID & /*subnet_id*/) {
/// @todo: Need to implement this, so wipe leases would work.

View File

@@ -203,12 +203,12 @@ public:
/// @param subnet_id subnet identifier.
///
/// @return Lease collection (may be empty if no IPv4 lease found).
virtual Lease4Collection getLeases4(SubnetID subnet_id) const;
virtual Lease4Collection getLeases4(SubnetID subnet_id) const override;
/// @brief Returns all IPv4 leases.
///
/// @return Lease collection (may be empty if no IPv4 lease found).
virtual Lease4Collection getLeases4() const;
virtual Lease4Collection getLeases4() const override;
/// @brief Returns existing IPv6 lease for a given IPv6 address.
///
@@ -352,6 +352,26 @@ public:
virtual uint64_t
deleteExpiredReclaimedLeases6(const uint32_t secs) override;
/// @brief Creates and runs the IPv4 lease stats query
///
/// It creates an instance of a CqlLeaseStatsQuery4 and then
/// invokes its start method, which fetches its statistical data
/// result set by executing the RECOUNT_LEASE_STATS4 query.
/// The query object is then returned.
///
/// @return The populated query as a pointer to an LeaseStatsQuery
virtual LeaseStatsQueryPtr startLeaseStatsQuery4() override;
/// @brief Creates and runs the IPv6 lease stats query
///
/// It creates an instance of a CqllLeaseStatsQuery and then
/// invokes its start method, which fetches its statistical data
/// result set by executing the RECOUNT_LEASE_STATS6 query.
/// The query object is then returned.
///
/// @return The populated query as a pointer to an LeaseStatsQuery
virtual LeaseStatsQueryPtr startLeaseStatsQuery6() override;
/// @brief Removes specified IPv4 leases.
///
/// This rather dangerous method is able to remove all leases from specified

View File

@@ -1,4 +1,4 @@
// Copyright (C) 2015-2016 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2015-2018 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -8,7 +8,7 @@
#include <dhcpsrv/database_connection.h>
#include <dhcpsrv/db_exceptions.h>
#include <dhcpsrv/dhcpsrv_log.h>
#include <dhcpsrv/db_log.h>
#include <exceptions/exceptions.h>
#include <boost/algorithm/string.hpp>
@@ -50,7 +50,7 @@ DatabaseConnection::parse(const std::string& dbaccess) {
string value = token.substr(pos + 1);
mapped_tokens.insert(make_pair(name, value));
} else {
LOG_ERROR(dhcpsrv_logger, DHCPSRV_INVALID_ACCESS).arg(dbaccess);
DB_LOG_ERROR(DB_INVALID_ACCESS).arg(dbaccess);
isc_throw(InvalidParameter, "Cannot parse " << token
<< ", expected format is name=value");
}

34
src/lib/dhcpsrv/db_log.cc Normal file
View File

@@ -0,0 +1,34 @@
// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
/// Defines the logger used by the NSAS
#include <exceptions/exceptions.h>
#include <dhcpsrv/db_log.h>
#include <dhcpsrv/dhcpsrv_db_log.h>
using namespace isc::log;
namespace isc {
namespace dhcp {
const MessageID&
DbLogger::translateMessage(const DbMessageID& id) const {
try {
return (map_.at(id));
} catch (const std::out_of_range&) {
isc_throw(isc::Unexpected, "can't map message: " << id);
}
}
void checkDbLoggerStack() {
if (db_logger_stack.empty()) {
isc_throw(isc::Unexpected, "database logger stack is empty");
}
}
} // namespace dhcp
} // namespace isc

152
src/lib/dhcpsrv/db_log.h Normal file
View File

@@ -0,0 +1,152 @@
// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef DB_LOG_H
#define DB_LOG_H
#include <log/macros.h>
#include <map>
#include <list>
/// @file db_log.h
///
/// We want to reuse the database backend connection and exchange code
/// for other uses, in particular for hook libraries. But this code
/// includes some calls to the system logger for debug and uncommon
/// cases and of course we do not want to get log messages from
/// a hook library to seem to come from DHCP server core.
///
/// The solution is to use a database logger which calls the right
/// logger with mapped messages.
namespace isc {
namespace dhcp {
///@{
/// @brief Database logging levels
///
/// Defines the levels used to output debug messages in the database
/// support. Note that higher numbers equate to more verbose (and detailed)
/// output.
/// @brief Additional information
///
/// Record detailed tracing. This is generally reserved for tracing access to
/// the lease database.
const int DB_DBG_TRACE_DETAIL = isc::log::DBGLVL_TRACE_DETAIL;
///@}
///@{
/// @brief Database messages
///
enum DbMessageID {
DB_INVALID_ACCESS,
PGSQL_DEALLOC_ERROR,
PGSQL_FATAL_ERROR,
PGSQL_START_TRANSACTION,
PGSQL_COMMIT,
PGSQL_ROLLBACK,
MYSQL_FATAL_ERROR,
MYSQL_START_TRANSACTION,
MYSQL_COMMIT,
MYSQL_ROLLBACK,
CQL_DEALLOC_ERROR,
CQL_CONNECTION_BEGIN_TRANSACTION,
CQL_CONNECTION_COMMIT,
CQL_CONNECTION_ROLLBACK
};
///@}
/// @brief Database logger class
///
class DbLogger {
public:
/// @brief Translation map type
typedef std::map<DbMessageID, isc::log::MessageID> MessageMap;
/// @brief Constructor
///
/// @param logger logger which will be called
/// @param map message id translation map
DbLogger(isc::log::Logger& logger, const MessageMap& map)
: logger_(logger), map_(map) {
}
/// @brief Translate message
///
/// @param id database message id
/// @return logger message
/// @throw Unexpected if the id is not in the message map
const isc::log::MessageID& translateMessage(const DbMessageID& id) const;
/// @brief The logger
isc::log::Logger& logger_;
/// @brief The translation map
const MessageMap& map_;
};
/// @brief Database logger stack
typedef std::list<DbLogger> DbLoggerStack;
/// @brief Global database logger stack (initialized to dhcpsrv logger)
extern DbLoggerStack db_logger_stack;
/// @brief Check database logger stack
///
/// @throw Unexpected if the stack is empty
void checkDbLoggerStack();
///@{
/// @brief Macros
#define DB_LOG_DEBUG(LEVEL, MESSAGE) \
checkDbLoggerStack(); \
if (!db_logger_stack.back().logger_.isDebugEnabled((LEVEL))) { \
} else \
db_logger_stack.back().logger_.debug((LEVEL), \
db_logger_stack.back().translateMessage((MESSAGE)))
#define DB_LOG_INFO(MESSAGE) \
checkDbLoggerStack(); \
if (!db_logger_stack.back().logger_.isInfoEnabled()) { \
} else \
db_logger_stack.back().logger_.info( \
db_logger_stack.back().translateMessage((MESSAGE)))
#define DB_LOG_WARN(MESSAGE) \
checkDbLoggerStack(); \
if (!db_logger_stack.back().logger_.isWarnEnabled()) { \
} else \
db_logger_stack.back().logger_.warn( \
db_logger_stack.back().translateMessage((MESSAGE)))
#define DB_LOG_ERROR(MESSAGE) \
checkDbLoggerStack(); \
if (!db_logger_stack.back().logger_.isErrorEnabled()) { \
} else \
db_logger_stack.back().logger_.error( \
db_logger_stack.back().translateMessage((MESSAGE)))
#define DB_LOG_FATAL(MESSAGE) \
checkDbLoggerStack(); \
if (!db_logger_stack.back().logger_.isFatalEnabled()) { \
} else \
db_logger_stack.back().logger_.fatal( \
db_logger_stack.back().translateMessage((MESSAGE)))
///@}
} // namespace dhcp
} // namespace isc
#endif // DB_LOG_H

View File

@@ -0,0 +1,43 @@
// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
/// Defines the logger used by the NSAS
#include <dhcpsrv/dhcpsrv_db_log.h>
#include <dhcpsrv/dhcpsrv_log.h>
namespace isc {
namespace dhcp {
const DbLogger::MessageMap dhcpsrv_db_message_map = {
{ DB_INVALID_ACCESS, DHCPSRV_INVALID_ACCESS },
{ PGSQL_DEALLOC_ERROR, DHCPSRV_PGSQL_DEALLOC_ERROR },
{ PGSQL_FATAL_ERROR, DHCPSRV_PGSQL_FATAL_ERROR },
{ PGSQL_START_TRANSACTION, DHCPSRV_PGSQL_START_TRANSACTION },
{ PGSQL_COMMIT, DHCPSRV_PGSQL_COMMIT },
{ PGSQL_ROLLBACK, DHCPSRV_PGSQL_ROLLBACK },
{ MYSQL_FATAL_ERROR, DHCPSRV_MYSQL_FATAL_ERROR },
{ MYSQL_START_TRANSACTION, DHCPSRV_MYSQL_START_TRANSACTION },
{ MYSQL_COMMIT, DHCPSRV_MYSQL_COMMIT },
{ MYSQL_ROLLBACK, DHCPSRV_MYSQL_ROLLBACK },
{ CQL_DEALLOC_ERROR, DHCPSRV_CQL_DEALLOC_ERROR },
{ CQL_CONNECTION_BEGIN_TRANSACTION,
DHCPSRV_CQL_CONNECTION_BEGIN_TRANSACTION },
{ CQL_CONNECTION_COMMIT, DHCPSRV_CQL_CONNECTION_COMMIT },
{ CQL_CONNECTION_ROLLBACK, DHCPSRV_CQL_CONNECTION_ROLLBACK }
};
DbLogger dhcpsrv_db_logger(dhcpsrv_logger, dhcpsrv_db_message_map);
// Do this initialization here!
DbLoggerStack db_logger_stack = { dhcpsrv_db_logger };
} // namespace dhcp
} // namespace isc

View File

@@ -0,0 +1,26 @@
// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef DHCPSRV_DB_LOG_H
#define DHCPSRV_DB_LOG_H
#include <dhcpsrv/db_log.h>
namespace isc {
namespace dhcp {
/// @brief DHCP server database message map
extern const DbLogger::MessageMap dhcpsrv_db_message_map;
/// @brief DHCP server database Logger
///
/// It is the default database logger.
extern DbLogger dhcpsrv_db_logger;
} // namespace dhcp
} // namespace isc
#endif // DHCPSRV_DB_LOG_H

View File

@@ -101,6 +101,26 @@ struct LeaseStatsRow {
lease_state_(lease_state), state_count_(state_count) {
}
/// @brief Less-than operator
bool operator< (const LeaseStatsRow &rhs) const {
if (subnet_id_ < rhs.subnet_id_) {
return (true);
}
if (subnet_id_ == rhs.subnet_id_ &&
lease_type_ < rhs.lease_type_) {
return (true);
}
if (subnet_id_ == rhs.subnet_id_ &&
lease_type_ == rhs.lease_type_ &&
lease_state_ < rhs.lease_state_) {
return (true);
}
return (false);
}
/// @brief The subnet ID to which this data applies
SubnetID subnet_id_;
/// @brief The lease_type to which the count applies
@@ -114,7 +134,7 @@ struct LeaseStatsRow {
/// @brief Base class for fulfilling a statistical lease data query
///
/// LeaseMgr derivations implement this class such that it provides
/// upto date statistical lease data organized as rows of LeaseStatsRow
/// up to date statistical lease data organized as rows of LeaseStatsRow
/// instances. The rows must be accessible in ascending order by subnet id.
class LeaseStatsQuery {
public:
@@ -140,9 +160,12 @@ public:
virtual bool getNextRow(LeaseStatsRow& row);
};
/// @brief Defines a pointer to an LeaseStatsQuery.
/// @brief Defines a pointer to a LeaseStatsQuery.
typedef boost::shared_ptr<LeaseStatsQuery> LeaseStatsQueryPtr;
/// @brief Defines a pointer to a LeaseStatsRow.
typedef boost::shared_ptr<LeaseStatsRow> LeaseStatsRowPtr;
/// @brief Abstract Lease Manager
///
/// This is an abstract API for lease database backends. It provides unified
@@ -433,7 +456,7 @@ public:
///
/// LeaseMgr derivations implement this method such that it creates and
/// returns an instance of an LeaseStatsQuery whose result set has been
/// populated with upto date IPv4 lease statistical data. Each row of the
/// populated with up to date IPv4 lease statistical data. Each row of the
/// result set is an LeaseStatRow which ordered ascending by subnet ID.
///
/// @return A populated LeaseStatsQuery
@@ -464,7 +487,7 @@ public:
///
/// LeaseMgr derivations implement this method such that it creates and
/// returns an instance of an LeaseStatsQuery whose result set has been
/// populated with upto date IPv6 lease statistical data. Each row of the
/// populated with up to date IPv6 lease statistical data. Each row of the
/// result set is an LeaseStatRow which ordered ascending by subnet ID.
///
/// @return A populated LeaseStatsQuery

View File

@@ -1,4 +1,4 @@
// Copyright (C) 2012-2017 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2012-2018 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -7,7 +7,7 @@
#include <config.h>
#include <dhcpsrv/dhcpsrv_log.h>
#include <dhcpsrv/db_log.h>
#include <dhcpsrv/mysql_connection.h>
#include <exceptions/exceptions.h>
@@ -370,8 +370,7 @@ MySqlConnection::convertFromDatabaseTime(const MYSQL_TIME& expire,
void
MySqlConnection::startTransaction() {
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
DHCPSRV_MYSQL_START_TRANSACTION);
DB_LOG_DEBUG(DB_DBG_TRACE_DETAIL, MYSQL_START_TRANSACTION);
// We create prepared statements for all other queries, but MySQL
// don't support prepared statements for START TRANSACTION.
int status = mysql_query(mysql_, "START TRANSACTION");
@@ -383,7 +382,7 @@ MySqlConnection::startTransaction() {
void
MySqlConnection::commit() {
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MYSQL_COMMIT);
DB_LOG_DEBUG(DB_DBG_TRACE_DETAIL, MYSQL_COMMIT);
if (mysql_commit(mysql_) != 0) {
isc_throw(DbOperationError, "commit failed: "
<< mysql_error(mysql_));
@@ -392,7 +391,7 @@ MySqlConnection::commit() {
void
MySqlConnection::rollback() {
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MYSQL_ROLLBACK);
DB_LOG_DEBUG(DB_DBG_TRACE_DETAIL, MYSQL_ROLLBACK);
if (mysql_rollback(mysql_) != 0) {
isc_throw(DbOperationError, "rollback failed: "
<< mysql_error(mysql_));

View File

@@ -1,4 +1,4 @@
// Copyright (C) 2012-2017 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2012-2018 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -8,7 +8,7 @@
#define MYSQL_CONNECTION_H
#include <dhcpsrv/database_connection.h>
#include <dhcpsrv/dhcpsrv_log.h>
#include <dhcpsrv/db_log.h>
#include <exceptions/exceptions.h>
#include <boost/scoped_ptr.hpp>
#include <mysql.h>
@@ -390,11 +390,11 @@ public:
case CR_OUT_OF_MEMORY:
case CR_CONNECTION_ERROR:
// We're exiting on fatal
LOG_ERROR(dhcpsrv_logger, DHCPSRV_MYSQL_FATAL_ERROR)
.arg(what)
.arg(text_statements_[static_cast<int>(index)])
.arg(mysql_error(mysql_))
.arg(mysql_errno(mysql_));
DB_LOG_ERROR(MYSQL_FATAL_ERROR)
.arg(what)
.arg(text_statements_[static_cast<int>(index)])
.arg(mysql_error(mysql_))
.arg(mysql_errno(mysql_));
exit (-1);
default:

View File

@@ -1000,7 +1000,7 @@ Subnet6ConfigParser::initSubnet(data::ConstElementPtr params,
<< ", rapid-commit is " << (rapid_commit ? "enabled" : "disabled");
LOG_INFO(dhcpsrv_logger, DHCPSRV_CFGMGR_NEW_SUBNET4).arg(output.str());
LOG_INFO(dhcpsrv_logger, DHCPSRV_CFGMGR_NEW_SUBNET6).arg(output.str());
// Create a new subnet.
Subnet6* subnet6 = new Subnet6(addr, len, t1, t2, pref, valid,

View File

@@ -1,4 +1,4 @@
// Copyright (C) 2016-2017 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2016-2018 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -6,7 +6,7 @@
#include <config.h>
#include <dhcpsrv/dhcpsrv_log.h>
#include <dhcpsrv/db_log.h>
#include <dhcpsrv/pgsql_connection.h>
// PostgreSQL errors should be tested based on the SQL state code. Each state
@@ -114,8 +114,7 @@ PgSqlConnection::~PgSqlConnection() {
PgSqlResult r(PQexec(conn_, "DEALLOCATE all"));
if(PQresultStatus(r) != PGRES_COMMAND_OK) {
// Highly unlikely but we'll log it and go on.
LOG_ERROR(dhcpsrv_logger, DHCPSRV_PGSQL_DEALLOC_ERROR)
.arg(PQerrorMessage(conn_));
DB_LOG_ERROR(PGSQL_DEALLOC_ERROR).arg(PQerrorMessage(conn_));
}
}
}
@@ -295,10 +294,10 @@ PgSqlConnection::checkStatementError(const PgSqlResult& r,
(memcmp(sqlstate, "54", 2) == 0) || // Program Limit exceeded
(memcmp(sqlstate, "57", 2) == 0) || // Operator intervention
(memcmp(sqlstate, "58", 2) == 0))) { // System error
LOG_ERROR(dhcpsrv_logger, DHCPSRV_PGSQL_FATAL_ERROR)
.arg(statement.name)
.arg(PQerrorMessage(conn_))
.arg(sqlstate);
DB_LOG_ERROR(PGSQL_FATAL_ERROR)
.arg(statement.name)
.arg(PQerrorMessage(conn_))
.arg(sqlstate);
exit (-1);
}
@@ -311,8 +310,7 @@ PgSqlConnection::checkStatementError(const PgSqlResult& r,
void
PgSqlConnection::startTransaction() {
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
DHCPSRV_PGSQL_START_TRANSACTION);
DB_LOG_DEBUG(DB_DBG_TRACE_DETAIL, PGSQL_START_TRANSACTION);
PgSqlResult r(PQexec(conn_, "START TRANSACTION"));
if (PQresultStatus(r) != PGRES_COMMAND_OK) {
const char* error_message = PQerrorMessage(conn_);
@@ -323,7 +321,7 @@ PgSqlConnection::startTransaction() {
void
PgSqlConnection::commit() {
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_PGSQL_COMMIT);
DB_LOG_DEBUG(DB_DBG_TRACE_DETAIL, PGSQL_COMMIT);
PgSqlResult r(PQexec(conn_, "COMMIT"));
if (PQresultStatus(r) != PGRES_COMMAND_OK) {
const char* error_message = PQerrorMessage(conn_);
@@ -333,7 +331,7 @@ PgSqlConnection::commit() {
void
PgSqlConnection::rollback() {
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_PGSQL_ROLLBACK);
DB_LOG_DEBUG(DB_DBG_TRACE_DETAIL, PGSQL_ROLLBACK);
PgSqlResult r(PQexec(conn_, "ROLLBACK"));
if (PQresultStatus(r) != PGRES_COMMAND_OK) {
const char* error_message = PQerrorMessage(conn_);

View File

@@ -1,4 +1,4 @@
// Copyright (C) 2014-2015,2017 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2014-2018 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -184,6 +184,67 @@ TEST_F(CfgIfaceTest, multipleAddressesSameInterfaceV4) {
EXPECT_TRUE(socketOpen("eth1", "192.0.2.5"));
}
// This test checks that it is possible to specify the loopback interface.
TEST_F(CfgIfaceTest, explicitLoopbackV4) {
CfgIface cfg;
ASSERT_NO_THROW(cfg.use(AF_INET, "lo"));
// Use UDP sockets
ASSERT_NO_THROW(cfg.useSocketType(AF_INET, CfgIface::SOCKET_UDP));
// Open sockets on specified interfaces and addresses.
cfg.openSockets(AF_INET, DHCP4_SERVER_PORT);
EXPECT_TRUE(socketOpen("lo", "127.0.0.1"));
// Close all sockets and make sure they are really closed.
cfg.closeSockets();
ASSERT_FALSE(socketOpen("lo", "127.0.0.1"));
// Reset configuration.
cfg.reset();
// Retry with wirdcard
ASSERT_NO_THROW(cfg.use(AF_INET, "*"));
ASSERT_NO_THROW(cfg.use(AF_INET, "lo"));
ASSERT_NO_THROW(cfg.useSocketType(AF_INET, CfgIface::SOCKET_UDP));
cfg.openSockets(AF_INET, DHCP4_SERVER_PORT);
// It is now allowed to use loopback, even with wildcard.
EXPECT_TRUE(socketOpen("lo", "127.0.0.1"));
cfg.closeSockets();
ASSERT_FALSE(socketOpen("lo", "127.0.0.1"));
// Retry without UDP sockets (lo can be only used with udp sockets)
cfg.reset();
ASSERT_NO_THROW(cfg.use(AF_INET, "lo"));
cfg.openSockets(AF_INET, DHCP4_SERVER_PORT);
// No loopback socket
EXPECT_FALSE(socketOpen("lo", "127.0.0.1"));
// Retry with a second interface
cfg.reset();
ASSERT_NO_THROW(cfg.use(AF_INET, "eth0"));
ASSERT_NO_THROW(cfg.use(AF_INET, "lo"));
ASSERT_NO_THROW(cfg.useSocketType(AF_INET, CfgIface::SOCKET_UDP));
cfg.openSockets(AF_INET, DHCP4_SERVER_PORT);
// The logic used to require lo to be the only interface. That constraint
// was removed.
EXPECT_TRUE(socketOpen("lo", "127.0.0.1"));
cfg.closeSockets();
EXPECT_FALSE(socketOpen("lo", "127.0.0.1"));
// Finally with interfaces and addresses
cfg.reset();
ASSERT_NO_THROW(cfg.use(AF_INET, "eth0/10.0.0.1"));
ASSERT_NO_THROW(cfg.use(AF_INET, "lo/127.0.0.1"));
ASSERT_NO_THROW(cfg.useSocketType(AF_INET, CfgIface::SOCKET_UDP));
cfg.openSockets(AF_INET, DHCP4_SERVER_PORT);
// Only loopback is no longer a constraint
EXPECT_TRUE(socketOpen("lo", "127.0.0.1"));
cfg.closeSockets();
EXPECT_FALSE(socketOpen("lo", "127.0.0.1"));
}
// This test checks that the interface names can be explicitly selected
// by their names and IPv6 sockets are opened on these interfaces.
TEST_F(CfgIfaceTest, explicitNamesV6) {
@@ -299,6 +360,57 @@ TEST_F(CfgIfaceTest, invalidValues) {
ASSERT_THROW(cfg.use(AF_INET6, "*"), DuplicateIfaceName);
}
// This test checks that it is possible to specify the loopback interface.
// Note that without a link-local address an unicast address is required.
TEST_F(CfgIfaceTest, explicitLoopbackV6) {
CfgIface cfg;
ASSERT_NO_THROW(cfg.use(AF_INET6, "lo/::1"));
// Open sockets on specified interfaces and addresses.
cfg.openSockets(AF_INET6, DHCP6_SERVER_PORT);
EXPECT_TRUE(socketOpen("lo", AF_INET6));
// Close all sockets and make sure they are really closed.
cfg.closeSockets();
ASSERT_FALSE(socketOpen("lo", AF_INET6));
// Reset configuration.
cfg.reset();
// Retry with wirdcard
ASSERT_NO_THROW(cfg.use(AF_INET6, "*"));
ASSERT_NO_THROW(cfg.use(AF_INET6, "lo/::1"));
cfg.openSockets(AF_INET6, DHCP6_SERVER_PORT);
// The logic used to require lo to be used only on its own, not with a
// wildcard. That constraint was removed.
EXPECT_TRUE(socketOpen("lo", AF_INET6));
cfg.closeSockets();
ASSERT_FALSE(socketOpen("lo", AF_INET6));
// Retry with a second interface
cfg.reset();
ASSERT_NO_THROW(cfg.use(AF_INET6, "eth0"));
ASSERT_NO_THROW(cfg.use(AF_INET6, "lo/::1"));
cfg.openSockets(AF_INET6, DHCP6_SERVER_PORT);
// The logic used to require lo to be used only on its own, not with a
// wildcard. That constraint was removed.
EXPECT_TRUE(socketOpen("lo", AF_INET6));
cfg.closeSockets();
ASSERT_FALSE(socketOpen("lo", AF_INET6));
// Finally with interfaces and addresses
cfg.reset();
ASSERT_NO_THROW(cfg.use(AF_INET6, "eth0/2001:db8:1::1"));
ASSERT_NO_THROW(cfg.use(AF_INET6, "lo/::1"));
cfg.openSockets(AF_INET6, DHCP6_SERVER_PORT);
// The logic used to require lo to be used only on its own, not with a
// wildcard. That constraint was removed.
EXPECT_TRUE(socketOpen("lo", AF_INET6));
cfg.closeSockets();
ASSERT_FALSE(socketOpen("lo", AF_INET6));
}
// Test that the equality and inequality operators work fine for CfgIface.
TEST_F(CfgIfaceTest, equality) {
CfgIface cfg1;

View File

@@ -370,6 +370,7 @@ TEST(CqlOpenTest, OpenDatabase) {
<< "*** before the CQL tests will run correctly.\n";
}
// Check that attempting to get an instance of the lease manager when
// none is set throws an exception.
EXPECT_THROW(LeaseMgrFactory::instance(), NoLeaseManager);
@@ -741,17 +742,13 @@ TEST_F(CqlLeaseMgrTest, deleteExpiredReclaimedLeases6) {
testDeleteExpiredReclaimedLeases6();
}
// Verifies that IPv4 lease statistics can be recalculated.
/// @todo: uncomment this once stats recalculation is implemented
/// for Cassandra (see #5487)
TEST_F(CqlLeaseMgrTest, DISABLED_recountLeaseStats4) {
/// @brief Verifies that IPv4 lease statistics can be recalculated.
TEST_F(CqlLeaseMgrTest, recountLeaseStats4) {
testRecountLeaseStats4();
}
// Verifies that IPv6 lease statistics can be recalculated.
/// @todo: uncomment this once stats recalculation is implemented
/// for Cassandra (see #5487)
TEST_F(CqlLeaseMgrTest, DISABLED_recountLeaseStats6) {
/// @brief Verifies that IPv6 lease statistics can be recalculated.
TEST_F(CqlLeaseMgrTest, recountLeaseStats6) {
testRecountLeaseStats6();
}

View File

@@ -11,6 +11,7 @@ libkea_util_la_SOURCES = boost_time_utils.h boost_time_utils.cc
libkea_util_la_SOURCES += buffer.h io_utilities.h
libkea_util_la_SOURCES += csv_file.h csv_file.cc
libkea_util_la_SOURCES += filename.h filename.cc
libkea_util_la_SOURCES += hash.h
libkea_util_la_SOURCES += labeled_value.h labeled_value.cc
libkea_util_la_SOURCES += memory_segment.h
libkea_util_la_SOURCES += memory_segment_local.h memory_segment_local.cc
@@ -50,6 +51,7 @@ libkea_util_include_HEADERS = \
buffer.h \
csv_file.h \
filename.h \
hash.h \
io_utilities.h \
labeled_value.h \
memory_segment.h \

57
src/lib/util/hash.h Normal file
View File

@@ -0,0 +1,57 @@
// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef HASH_H
#define HASH_H
#include <cstddef>
#include <cstdint>
#include <string>
namespace isc {
namespace util {
/// @brief Hash implementation based on Fowler-Noll-Vo hash function
///
struct Hash64 {
/// @brief Compute the hash
///
/// FNV-1a hash function
///
/// @param data data to hash
/// @param length length of data
/// @return the hash value
static uint64_t hash(const uint8_t* data, size_t length) {
uint64_t hash = FNV_offset_basis;
for (size_t i = 0; i < length; ++i) {
hash = hash ^ data[i];
hash = hash * FNV_prime;
}
return (hash);
}
/// @brief Compute the hash
///
/// FNV-1a hash function
///
/// @param str not empty string to hash
/// @return the hash value
static uint64_t hash(const std::string& str) {
return (hash(reinterpret_cast<const uint8_t*>(str.c_str()),
str.size()));
}
/// @brief Offset basis
static const uint64_t FNV_offset_basis = 14695981039346656037ull;
/// @brief Prime
static const uint64_t FNV_prime = 1099511628211ull;
};
} // end of namespace isc::util
} // end of namespace isc
#endif

View File

@@ -36,6 +36,7 @@ run_unittests_SOURCES += csv_file_unittest.cc
run_unittests_SOURCES += fd_share_tests.cc
run_unittests_SOURCES += fd_tests.cc
run_unittests_SOURCES += filename_unittest.cc
run_unittests_SOURCES += hash_unittest.cc
run_unittests_SOURCES += hex_unittest.cc
run_unittests_SOURCES += io_utilities_unittest.cc
run_unittests_SOURCES += labeled_value_unittest.cc

View File

@@ -0,0 +1,34 @@
// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#include <config.h>
#include <util/hash.h>
#include <gtest/gtest.h>
#include <cstring>
#include <vector>
using namespace isc::util;
using namespace std;
namespace {
TEST(HashTest, empty) {
EXPECT_EQ(14695981039346656037ull, Hash64::hash(0, 0));
}
TEST(HashTest, foobar) {
EXPECT_EQ(9625390261332436968ull, Hash64::hash(string("foobar")));
}
TEST(HashTest, chongo) {
EXPECT_EQ(5080352029159061781ull,
Hash64::hash(string("chongo was here!\n")));
}
}