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:
0
.gitmodules
vendored
Normal file
0
.gitmodules
vendored
Normal file
24
ChangeLog
24
ChangeLog
@@ -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
|
||||
|
11
Makefile.am
11
Makefile.am
@@ -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
|
||||
|
15
configure.ac
15
configure.ac
@@ -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:
|
||||
|
@@ -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>
|
||||
|
@@ -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">
|
||||
|
@@ -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">
|
||||
|
@@ -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">
|
||||
|
@@ -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(
|
||||
|
@@ -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());
|
||||
|
@@ -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
|
||||
|
@@ -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) {
|
||||
|
@@ -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
|
||||
|
@@ -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:
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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) {
|
||||
|
@@ -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>
|
||||
//
|
||||
|
@@ -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 ¶meters)
|
||||
: 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.
|
||||
|
@@ -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
|
||||
|
@@ -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
34
src/lib/dhcpsrv/db_log.cc
Normal 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
152
src/lib/dhcpsrv/db_log.h
Normal 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
|
43
src/lib/dhcpsrv/dhcpsrv_db_log.cc
Normal file
43
src/lib/dhcpsrv/dhcpsrv_db_log.cc
Normal 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
|
26
src/lib/dhcpsrv/dhcpsrv_db_log.h
Normal file
26
src/lib/dhcpsrv/dhcpsrv_db_log.h
Normal 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
|
@@ -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
|
||||
|
@@ -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_));
|
||||
|
@@ -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:
|
||||
|
@@ -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,
|
||||
|
@@ -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_);
|
||||
|
@@ -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;
|
||||
|
@@ -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();
|
||||
}
|
||||
|
||||
|
@@ -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
57
src/lib/util/hash.h
Normal 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
|
@@ -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
|
||||
|
34
src/lib/util/tests/hash_unittest.cc
Normal file
34
src/lib/util/tests/hash_unittest.cc
Normal 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")));
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user