BIND 10 master, updated. 34fddf2e75b80d9e517a8f9c3321aa4878cda795 Merge branch 'trac3207'
BIND 10 source code commits
bind10-changes at lists.isc.org
Thu Jan 2 19:43:01 UTC 2014
The branch, master has been updated
via 34fddf2e75b80d9e517a8f9c3321aa4878cda795 (commit)
via 94be309810fb7f5964fa52c8348a12e100bd0428 (commit)
via b425b35008aa702182f722dc34e8fffc27f19270 (commit)
via 2f2d2cfd4de37453e0fd7a2c6e23ffce94784771 (commit)
via 51fa695a018b40c1a35363690a352ea03bdc6918 (commit)
via 3b1b5032e2afd07bf7d947e185b59a04d9805e08 (commit)
via fe031382e774b530a89ad19aa4c6d894a2c2444a (commit)
via 081cc5311fd9e8cc52b0610b77bb72997eeba835 (commit)
via c9c132e4cb353b7fbe16e5c98ef72f8c7e498f81 (commit)
via 66748b4b5ecd06dc1af0793c8470e4b5e2ee7bd9 (commit)
via d130cf2cb4c0d294a43d47d989732e466a8be24a (commit)
via 327d9ff442bd25fa742c3ea03aa4630440c00661 (commit)
from 7715d34151a84d87702c18ea0717fdb7f63affb0 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit 34fddf2e75b80d9e517a8f9c3321aa4878cda795
Merge: 7715d34 94be309
Author: Thomas Markwalder <tmark at isc.org>
Date: Thu Jan 2 14:20:43 2014 -0500
Merge branch 'trac3207'
Completes initial implmementation of example hooks libary, user_chk.
-----------------------------------------------------------------------
Summary of changes:
doc/Doxyfile | 1 +
doc/devel/mainpage.dox | 1 +
src/hooks/dhcp/user_chk/Makefile.am | 5 +-
src/hooks/dhcp/user_chk/libdhcp_user_chk.dox | 207 ++++++++
src/hooks/dhcp/user_chk/load_unload.cc | 21 +-
src/hooks/dhcp/user_chk/pkt_receive_co.cc | 144 ++++++
src/hooks/dhcp/user_chk/pkt_send_co.cc | 541 ++++++++++++++++++++
src/hooks/dhcp/user_chk/subnet_select_co.cc | 151 ++----
src/hooks/dhcp/user_chk/tests/Makefile.am | 4 +
src/hooks/dhcp/user_chk/tests/test_users_1.txt | 2 +
.../dhcp/user_chk/tests/user_file_unittests.cc | 15 +-
.../dhcp/user_chk/tests/user_registry_unittests.cc | 1 +
src/hooks/dhcp/user_chk/tests/user_unittests.cc | 1 +
src/hooks/dhcp/user_chk/tests/userid_unittests.cc | 12 +-
src/hooks/dhcp/user_chk/user.cc | 13 +-
src/hooks/dhcp/user_chk/user.h | 6 +-
src/hooks/dhcp/user_chk/user_chk.h | 51 ++
src/hooks/dhcp/user_chk/user_chk_log.cc | 4 +
src/hooks/dhcp/user_chk/user_data_source.h | 4 +
src/hooks/dhcp/user_chk/user_file.cc | 3 +
src/hooks/dhcp/user_chk/user_file.h | 7 +-
src/hooks/dhcp/user_chk/user_registry.cc | 3 +
src/hooks/dhcp/user_chk/user_registry.h | 4 +
23 files changed, 1072 insertions(+), 129 deletions(-)
create mode 100644 src/hooks/dhcp/user_chk/libdhcp_user_chk.dox
create mode 100644 src/hooks/dhcp/user_chk/pkt_receive_co.cc
create mode 100644 src/hooks/dhcp/user_chk/pkt_send_co.cc
create mode 100644 src/hooks/dhcp/user_chk/user_chk.h
-----------------------------------------------------------------------
diff --git a/doc/Doxyfile b/doc/Doxyfile
index 9967df2..c2aec85 100644
--- a/doc/Doxyfile
+++ b/doc/Doxyfile
@@ -667,6 +667,7 @@ INPUT = ../src/bin/auth \
../src/bin/dhcp6 \
../src/bin/resolver \
../src/bin/sockcreator \
+ ../src/hooks/dhcp/user_chk \
../src/lib/acl \
../src/lib/asiolink \
../src/lib/bench \
diff --git a/doc/devel/mainpage.dox b/doc/devel/mainpage.dox
index 4110b26..64e2782 100644
--- a/doc/devel/mainpage.dox
+++ b/doc/devel/mainpage.dox
@@ -46,6 +46,7 @@
* - @subpage dhcpv6Hooks
* - @subpage hooksComponentDeveloperGuide
* - @subpage hooksmgMaintenanceGuide
+ * - @subpage libdhcp_user_chk
*
* @section dnsMaintenanceGuide DNS Maintenance Guide
* - Authoritative DNS (todo)
diff --git a/src/hooks/dhcp/user_chk/Makefile.am b/src/hooks/dhcp/user_chk/Makefile.am
index 874719b..6b68f38 100644
--- a/src/hooks/dhcp/user_chk/Makefile.am
+++ b/src/hooks/dhcp/user_chk/Makefile.am
@@ -25,7 +25,7 @@ AM_CXXFLAGS += $(WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG)
BUILT_SOURCES =
# Ensure that the message file is included in the distribution
-EXTRA_DIST =
+EXTRA_DIST = libdhcp_user_chk.dox
# Get rid of generated message files on a clean
#CLEANFILES = *.gcno *.gcda user_chk_messages.h user_chk_messages.cc s-messages
@@ -34,8 +34,11 @@ CLEANFILES = *.gcno *.gcda
lib_LTLIBRARIES = libdhcp_user_chk.la
libdhcp_user_chk_la_SOURCES =
libdhcp_user_chk_la_SOURCES += load_unload.cc
+libdhcp_user_chk_la_SOURCES += pkt_receive_co.cc
+libdhcp_user_chk_la_SOURCES += pkt_send_co.cc
libdhcp_user_chk_la_SOURCES += subnet_select_co.cc
libdhcp_user_chk_la_SOURCES += user.cc user.h
+libdhcp_user_chk_la_SOURCES += user_chk.h
# Until logging in dynamically loaded libraries is fixed, exclude these.
#libdhcp_user_chk_la_SOURCES += user_chk_log.cc user_chk_log.h
libdhcp_user_chk_la_SOURCES += user_data_source.h
diff --git a/src/hooks/dhcp/user_chk/libdhcp_user_chk.dox b/src/hooks/dhcp/user_chk/libdhcp_user_chk.dox
new file mode 100644
index 0000000..09d0e48
--- /dev/null
+++ b/src/hooks/dhcp/user_chk/libdhcp_user_chk.dox
@@ -0,0 +1,207 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+/**
+ at page libdhcp_user_chk DHCP User Check Hooks Library
+
+ at section libdhcp_user_chkIntro Libdhcp_user_chk: An Example Hooks Library
+## Introduction
+libdhcp_user_chk is an example hooks library which customizes the DHCP query
+processing provided by BIND X DHCP server modules (b10-dhcp4 and b10-dhcp6).
+Specifically it allows subnet selection and DHCP response option customization
+based upon a registry of DHCP clients. Note that the words "client" and "user" are used interchangeably herein. The intent of the custom behavior is three
+fold:
+
+1. To assign "new" or "unregistered" users to a restricted subnet, while "known"
+or "registered" users are assigned to unrestricted subnets.
+
+2. To allow DHCP response options or vendor option values to be customized
+based upon user identity.
+
+3. To provide a real time record of the user registration activity which can be sampled by an external consumer.
+
+## User Registry Classes
+At the heart of the library is a class hierarchy centered around the class,
+user_chk::UserRegistry. This class represents a maintainable, searchable
+registry of "known" users and their attributes. It provides services to load,
+clear, and refresh the registry as well as to add, find, and remove users.
+
+Each entry in the registry is an instance of the class, user_chk::User. Users
+are uniquely identified by their user_chk::UserId. UserIds are comprised of
+data taken from the DHCP request. IPv4 users have a type of "HW_ADDR" and
+their id is the hardware address from the request. IPv6 users have a type of
+"DUID" and their id is the DUID from the request.
+
+The registry may be manually populated or loaded from a source of data which
+implements the UserDataSource interface. Currently, a single implementation has
+been implemented, user_chk::UserFile. UserFile supports reading the registry
+from a text file in which each line is a user entry in JSON format. Each entry
+contains the id type and user id along with optional attributes. Attributes are
+essentially name/value pairs whose significance is left up to the calling layer.
+UserFile does not enforce any specific content beyond id type and id.
+(See user_file.h for more details on file content).
+
+## Callout Processing
+The library implements callouts for packet receive, subnet select, and packet
+send for both IPv4 and IPv6. Regardless of the protocol type, the process
+flow upon receipt of an inbound request is the same and is as follows:
+
+-# pkt_receive callout is invoked
+ -# Refresh the user registry
+ -# Extract user id from DHCP request and store it to context
+ -# Look up user id in registry and store resultant user pointer to context
+
+ Note that each time a packet is received, the user registry is refreshed.
+ This ensures that the registry content always has the latest external
+ updates. The primary goal at this stage is check the registry for the
+ user and push the result to the context making it available to subsequent
+ callouts.
+
+-# subnet_select callout is invoked
+ -# Retrieve the user pointer from context
+ -# If pointer is null (i.e. user is not registered), replace subnet
+ selection with restricted subnet
+
+ By convention, the last subnet in the collection of subnets available is
+ assumed to be the "restricted access" subnet. A more sophisticated mechanism is likely to be needed.
+
+-# pkt_send callout is invoked:
+ -# Retrieve the user id and user pointer from context
+ -# If user is not null add the options based on user's attributes,
+ otherwise use default user's attributes
+ -# Generate user check outcome
+
+ This final step is what produces the real time record, referred to as the
+ "user check outcome" file.
+
+## Using the library
+Two steps are required in order to use the library:
+-# The user registry file must be created and deployed
+-# The BIND10 DHCP module(s) must be configured to load the library
+
+### Creating the Registry File
+Currently, the library uses a hard coded pathname for the user registry defined
+in load_unload.cc:
+
+ const char* registry_fname = "/tmp/user_chk_registry.txt";
+
+Each line in the file is a self-contained JSON snippet which must have the
+following two entries:
+
+ - "type" whose value is "HW_ADDR" for IPv4 users or "DUID" for IPv6 users
+ - "id" whose value is either the hardware address or the DUID from the
+ request formatted as a string of hex digits, with or without ":" delimiters.
+
+and may have the zero or more of the following entries:
+
+ - "bootfile" whose value is the pathname of the desired file
+ - "tftp_server" whose value is the hostname or IP address of the desired
+ server
+
+Sample user registry file is shown below:
+
+ at code
+{ "type" : "HW_ADDR", "id" : "0c:0e:0a:01:ff:04", "bootfile" : "/tmp/v4bootfile" }
+{ "type" : "HW_ADDR", "id" : "0c:0e:0a:01:ff:06", "tftp_server" : "tftp.v4.example.com" }
+{ "type" : "DUID", "id" : "00:01:00:01:19:ef:e6:3b:00:0c:01:02:03:04", "bootfile" : "/tmp/v6bootfile" }
+{ "type" : "DUID", "id" : "00:01:00:01:19:ef:e6:3b:00:0c:01:02:03:06", "tftp_server" : "tftp.v6.example.com" }
+ at endcode
+
+It is possible to specify additional attributes. They will be loaded and stored with the user's entry in the registry. This allows the library to be extended to perform additional actions based on these attributes.
+
+Upon start up the library will attempt to load this file. If it does not exist
+the library will unload.
+
+### Configuring the DHCP Modules
+it must be configured as a hook library for the
+desired DHCP server modules. Note that the user_chk library is installed alongside the BIND10 libraries in "<install-dir>/lib" where <install-dir> is determined by the --prefix option of the configure script. It defaults to "/usr/local". Assuming the default value then, configuring b10-dhcp4 to load the user_chk
+library could be done with the following BIND10 configuration commands:
+
+ at code
+config add Dhcp4/hook_libraries
+config set Dhcp4/hook_libraries[0] "/usr/local/lib/libdhcp_user_chk.so"
+config commit
+ at endcode
+
+To configure it for b10-dhcp6, the commands are simply as shown below:
+
+ at code
+config add Dhcp6/hook_libraries
+config set Dhcp6/hook_libraries[0] "/usr/local/lib/libdhcp_user_chk.so"
+config commit
+ at endcode
+
+## User Check Outcome
+Once up and running, the library should begin adding entries to the outcome
+file. Currently, the library uses a hard coded pathname for the user registry defined in load_unload.cc:
+
+ const char* user_chk_output_fname = "/tmp/user_chk_outcome.txt";
+
+If the file cannot be created (or opened), the library will unload.
+
+For each lease granted, the library will add the following information to the
+end of the file: the id type, the user id, the lease or prefix granted, and
+whether or not the user was found in the registry. This information is written
+in the form of "name=value" with one value per line. (See subnet_callout.cc for details.)
+
+A sample outcome file is shown below:
+
+ at code
+id_type=HW_ADDR
+client=hwtype=1 0c:0e:0a:01:ff:04
+addr=175.16.1.100
+registered=yes
+
+id_type=HW_ADDR
+client=hwtype=1 0c:0e:0a:01:ff:05
+addr=152.0.2.10
+registered=no
+
+id_type=HW_ADDR
+client=hwtype=1 0c:0e:0a:01:ff:06
+addr=175.16.1.101
+registered=yes
+
+id_type=HW_ADDR
+client=hwtype=1 0c:0e:0a:01:ff:04
+addr=175.16.1.102
+registered=yes
+
+id_type=DUID
+client=00:01:00:01:19:ef:e6:3b:00:0c:01:02:03:04
+addr=2001:db8:2::1:0:0/96
+registered=yes
+
+id_type=DUID
+client=00:01:00:01:19:ef:e6:3b:00:0c:01:02:03:04
+addr=2001:db8:2::
+registered=yes
+
+id_type=DUID
+client=00:01:00:01:19:ef:e6:3b:00:0c:01:02:03:05
+addr=5005:778:2::
+registered=no
+
+id_type=DUID
+client=00:01:00:01:19:ef:e6:3b:00:0c:01:02:03:06
+addr=2001:db8:2::1
+registered=yes
+
+ at endcode
+
+Note the library always opens this file in append mode and does not limit its size.
+
+
+
+*/
diff --git a/src/hooks/dhcp/user_chk/load_unload.cc b/src/hooks/dhcp/user_chk/load_unload.cc
index 79afb82..dff2622 100644
--- a/src/hooks/dhcp/user_chk/load_unload.cc
+++ b/src/hooks/dhcp/user_chk/load_unload.cc
@@ -12,7 +12,7 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
-/// @file This file defines the load and unload hooks library functions.
+/// @file load_unload.cc Defines the load and unload hooks library functions.
#include <hooks/hooks.h>
#include <user_registry.h>
@@ -23,6 +23,7 @@
#include <errno.h>
using namespace isc::hooks;
+using namespace user_chk;
/// @brief Pointer to the registry instance.
UserRegistryPtr user_registry;
@@ -38,6 +39,24 @@ const char* registry_fname = "/tmp/user_chk_registry.txt";
/// @todo Hard-coded for now, this should be configurable.
const char* user_chk_output_fname = "/tmp/user_chk_outcome.txt";
+/// @brief Text label of user id in the inbound query in callout context
+const char* query_user_id_label = "query_user_id";
+
+/// @brief Text label of registered user pointer in callout context
+const char* registered_user_label = "registered_user";
+
+/// @brief Text id used to identify the default IPv4 user in the registry
+/// The format is a string containing an even number of hex digits. This
+/// value is to look up the default IPv4 user in the user registry for the
+/// the purpose of retrieving default values for user options.
+const char* default_user4_id_str = "00000000";
+
+/// @brief Text id used to identify the default IPv6 user in the registry
+/// The format is a string containing an even number of hex digits. This
+/// value is to look up the default IPv6 user in the user registry for the
+/// the purpose of retrieving default values for user options.
+const char *default_user6_id_str = "00000000";
+
// Functions accessed by the hooks framework use C linkage to avoid the name
// mangling that accompanies use of the C++ compiler as well as to avoid
// issues related to namespaces.
diff --git a/src/hooks/dhcp/user_chk/pkt_receive_co.cc b/src/hooks/dhcp/user_chk/pkt_receive_co.cc
new file mode 100644
index 0000000..bf00922
--- /dev/null
+++ b/src/hooks/dhcp/user_chk/pkt_receive_co.cc
@@ -0,0 +1,144 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+/// @file pkt_receive.cc Defines the pkt4_receive and pkt6_receive callout functions.
+
+#include <hooks/hooks.h>
+#include <dhcp/pkt4.h>
+#include <dhcp/dhcp6.h>
+#include <dhcp/pkt6.h>
+#include <user_chk.h>
+
+using namespace isc::dhcp;
+using namespace isc::hooks;
+using namespace user_chk;
+using namespace std;
+
+// Functions accessed by the hooks framework use C linkage to avoid the name
+// mangling that accompanies use of the C++ compiler as well as to avoid
+// issues related to namespaces.
+extern "C" {
+
+/// @brief This callout is called at the "pkt4_receive" hook.
+///
+/// This function determines if the DHCP client identified by the inbound
+/// DHCP query packet is in the user registry.
+/// Upon entry, the registry is refreshed. Next the hardware address is
+/// extracted from query and saved to the context as the "query_user_id".
+/// This id is then used to search the user registry. The resultant UserPtr
+/// whether the user is found or not, is saved to the callout context as
+/// "registered_user". This makes the registered user, if not null, available
+/// to subsequent callouts.
+///
+/// @param handle CalloutHandle which provides access to context.
+///
+/// @return 0 upon success, non-zero otherwise.
+int pkt4_receive(CalloutHandle& handle) {
+ if (!user_registry) {
+ std::cout << "DHCP UserCheckHook : pkt4_receive UserRegistry is null"
+ << std::endl;
+ return (1);
+ }
+
+ try {
+ // Refresh the registry.
+ user_registry->refresh();
+
+ // Get the HWAddress to use as the user identifier.
+ Pkt4Ptr query;
+ handle.getArgument("query4", query);
+ HWAddrPtr hwaddr = query->getHWAddr();
+
+ // Store the id we search with so it is available down the road.
+ handle.setContext(query_user_id_label, hwaddr);
+
+ // Look for the user in the registry.
+ UserPtr registered_user = user_registry->findUser(*hwaddr);
+
+ // Store user regardless. Empty user pointer means non-found. It is
+ // cheaper to fetch it and test it, than to use an exception throw.
+ handle.setContext(registered_user_label, registered_user);
+ std::cout << "DHCP UserCheckHook : pkt4_receive user : "
+ << hwaddr->toText() << " is "
+ << (registered_user ? " registered" : " not registered")
+ << std::endl;
+ } catch (const std::exception& ex) {
+ std::cout << "DHCP UserCheckHook : pkt4_receive unexpected error: "
+ << ex.what() << std::endl;
+ return (1);
+ }
+
+ return (0);
+}
+
+/// @brief This callout is called at the "pkt6_receive" hook.
+///
+/// This function determines if the DHCP client identified by the inbound
+/// DHCP query packet is in the user registry.
+/// Upon entry, the registry is refreshed. Next the DUID is extracted from
+/// query and saved to the context as the "query_user_id". This id is then
+/// used to search the user registry. The resultant UserPtr whether the user
+/// is found or not, is saved to the callout context as "registered_user".
+/// This makes the registered user, if not null, available to subsequent
+/// callouts.
+///
+/// @param handle CalloutHandle which provides access to context.
+///
+/// @return 0 upon success, non-zero otherwise.
+int pkt6_receive(CalloutHandle& handle) {
+ if (!user_registry) {
+ std::cout << "DHCP UserCheckHook : pkt6_receive UserRegistry is null"
+ << std::endl;
+ return (1);
+ }
+
+ try {
+ // Refresh the registry.
+ user_registry->refresh();
+
+ // Fetch the inbound packet.
+ Pkt6Ptr query;
+ handle.getArgument("query6", query);
+
+ // Get the DUID to use as the user identifier.
+ OptionPtr opt_duid = query->getOption(D6O_CLIENTID);
+ if (!opt_duid) {
+ std::cout << "DHCP6 query is missing DUID" << std::endl;
+ return (1);
+ }
+ DuidPtr duid = DuidPtr(new DUID(opt_duid->getData()));
+
+ // Store the id we search with so it is available down the road.
+ handle.setContext(query_user_id_label, duid);
+
+ // Look for the user in the registry.
+ UserPtr registered_user = user_registry->findUser(*duid);
+
+ // Store user regardless. Empty user pointer means non-found. It is
+ // cheaper to fetch it and test it, than to use an exception throw.
+ handle.setContext(registered_user_label, registered_user);
+ std::cout << "DHCP UserCheckHook : pkt6_receive user : "
+ << duid->toText() << " is "
+ << (registered_user ? " registered" : " not registered")
+ << std::endl;
+ } catch (const std::exception& ex) {
+ std::cout << "DHCP UserCheckHook : pkt6_receive unexpected error: "
+ << ex.what() << std::endl;
+ return (1);
+ }
+
+ return (0);
+}
+
+} // end extern "C"
diff --git a/src/hooks/dhcp/user_chk/pkt_send_co.cc b/src/hooks/dhcp/user_chk/pkt_send_co.cc
new file mode 100644
index 0000000..805333b
--- /dev/null
+++ b/src/hooks/dhcp/user_chk/pkt_send_co.cc
@@ -0,0 +1,541 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+/// @file pkt_send.cc Defines the pkt4_send and pkt6_send callout functions.
+
+#include <asiolink/io_address.h>
+#include <hooks/hooks.h>
+#include <dhcp/dhcp4.h>
+#include <dhcp/dhcp6.h>
+#include <dhcp/option_string.h>
+#include <dhcp/option_custom.h>
+#include <dhcp/option6_ia.h>
+#include <dhcp/option6_iaaddr.h>
+#include <dhcp/option6_iaprefix.h>
+#include <dhcp/docsis3_option_defs.h>
+#include <dhcp/pkt4.h>
+#include <dhcp/pkt6.h>
+#include <user_chk.h>
+
+using namespace isc::dhcp;
+using namespace isc::hooks;
+using namespace user_chk;
+using namespace std;
+
+// prototypes for local helper functions
+void generate_output_record(const std::string& id_type_str,
+ const std::string& id_val_str,
+ const std::string& addr_str,
+ const bool& registered);
+std::string getV6AddrStr (Pkt6Ptr response);
+std::string getAddrStrIA_NA(OptionPtr options);
+std::string getAddrStrIA_PD(OptionPtr options);
+bool checkIAStatus(boost::shared_ptr<Option6IA>& ia_opt);
+
+void add4Options(Pkt4Ptr& response, const UserPtr& user);
+void add4Option(Pkt4Ptr& response, uint8_t opt_code, std::string& opt_value);
+void add6Options(Pkt6Ptr& response, const UserPtr& user);
+void add6Option(OptionPtr& vendor, uint8_t opt_code, std::string& opt_value);
+const UserPtr& getDefaultUser4();
+const UserPtr& getDefaultUser6();
+
+// Functions accessed by the hooks framework use C linkage to avoid the name
+// mangling that accompanies use of the C++ compiler as well as to avoid
+// issues related to namespaces.
+extern "C" {
+
+/// @brief This callout is called at the "pkt4_send" hook.
+///
+/// This function generates the user check outcome and modifies options
+/// to the IPv4 response packet based on whether the user is registered or not.
+///
+/// It retrieves a pointer to the registered user from the callout context.
+/// This value should have been set upstream. If the registered user pointer
+/// is non-null (i.e the user is registered), then a registered user outcome
+/// is recorded in the outcome output and the vendor properties are altered
+/// based upon this user's properties.
+///
+/// A null value means the user is not registered and a unregistered user
+/// outcome is recorded in the outcome output and the vendor properties
+/// are altered based upon the default IPv4 user in the registry (if defined).
+///
+/// @param handle CalloutHandle which provides access to context.
+///
+/// @return 0 upon success, non-zero otherwise.
+int pkt4_send(CalloutHandle& handle) {
+ try {
+ Pkt4Ptr response;
+ handle.getArgument("response4", response);
+
+ uint8_t packet_type = response->getType();
+ if (packet_type == DHCPNAK) {
+ std::cout << "DHCP UserCheckHook : pkt4_send"
+ << "skipping packet type: "
+ << static_cast<int>(packet_type) << std::endl;
+ return (0);
+ }
+
+ // Get the user id saved from the query packet.
+ HWAddrPtr hwaddr;
+ handle.getContext(query_user_id_label, hwaddr);
+
+ // Get registered_user pointer.
+ UserPtr registered_user;
+ handle.getContext(registered_user_label, registered_user);
+
+ // Fetch the lease address.
+ isc::asiolink::IOAddress addr = response->getYiaddr();
+
+ if (registered_user) {
+ // add options based on user
+ // then generate registered output record
+ std::cout << "DHCP UserCheckHook : pkt4_send registered_user is: "
+ << registered_user->getUserId() << std::endl;
+
+ // Add the outcome entry to the output file.
+ generate_output_record(UserId::HW_ADDRESS_STR, hwaddr->toText(),
+ addr.toText(), true);
+ add4Options(response, registered_user);
+ } else {
+ // add default options based
+ // then generate not registered output record
+ std::cout << "DHCP UserCheckHook : pkt4_send no registered_user"
+ << std::endl;
+ // Add the outcome entry to the output file.
+ generate_output_record(UserId::HW_ADDRESS_STR, hwaddr->toText(),
+ addr.toText(), false);
+
+ add4Options(response, getDefaultUser4());
+ }
+ } catch (const std::exception& ex) {
+ std::cout << "DHCP UserCheckHook : pkt4_send unexpected error: "
+ << ex.what() << std::endl;
+ return (1);
+ }
+
+ return (0);
+}
+
+/// @brief This callout is called at the "pkt6_send" hook.
+///
+/// This function generates the user check outcome and modifies options
+/// to the IPv6 response packet based on whether the user is registered or not.
+///
+/// It retrieves a pointer to the registered user from the callout context.
+/// This value should have been set upstream. If the registered user pointer
+/// is non-null (i.e the user is registered), then a registered user outcome
+/// is recorded in the outcome output and the vendor properties are altered
+/// based upon this user's properties.
+///
+/// A null value means the user is not registered and a unregistered user
+/// outcome is recorded in the outcome output and the vendor properties
+/// are altered based upon the default IPv6 user in the registry (if defined).
+/// @param handle CalloutHandle which provides access to context.
+///
+/// @return 0 upon success, non-zero otherwise.
+int pkt6_send(CalloutHandle& handle) {
+ try {
+ Pkt6Ptr response;
+ handle.getArgument("response6", response);
+
+ // Fetch the lease address as a string
+ std::string addr_str = getV6AddrStr(response);
+ if (addr_str.empty()) {
+ // packet did not contain an address, must be failed.
+ std::cout << "pkt6_send: Skipping packet address is blank"
+ << std::endl;
+ return (0);
+ }
+
+ // Get the user id saved from the query packet.
+ DuidPtr duid;
+ handle.getContext(query_user_id_label, duid);
+
+ // Get registered_user pointer.
+ UserPtr registered_user;
+ handle.getContext(registered_user_label, registered_user);
+
+ if (registered_user) {
+ // add options based on user
+ // then generate registered output record
+ std::cout << "DHCP UserCheckHook : pkt6_send registered_user is: "
+ << registered_user->getUserId() << std::endl;
+ // Add the outcome entry to the output file.
+ generate_output_record(UserId::DUID_STR, duid->toText(),
+ addr_str, true);
+ add6Options(response, registered_user);
+ } else {
+ // add default options based
+ // then generate not registered output record
+ std::cout << "DHCP UserCheckHook : pkt6_send no registered_user"
+ << std::endl;
+ // Add the outcome entry to the output file.
+ generate_output_record(UserId::DUID_STR, duid->toText(),
+ addr_str, false);
+ add6Options(response, getDefaultUser6());
+ }
+ } catch (const std::exception& ex) {
+ std::cout << "DHCP UserCheckHook : pkt6_send unexpected error: "
+ << ex.what() << std::endl;
+ return (1);
+ }
+
+ return (0);
+}
+
+} // extern C
+
+/// @brief Adds IPv4 options to the response packet based on given user
+///
+/// Adds or replaces IPv4 options with values from the given user, if
+/// the user has corresponding properties defined. Currently it supports
+/// the following options:
+///
+/// - DHO_BOOT_FILE_NAME from user property "bootfile"
+/// - DHO_TFTP_SERVER_NAME from user property "tftp_server"
+///
+/// @param response IPv4 response packet
+/// @param user User from whom properties are sourced
+void add4Options(Pkt4Ptr& response, const UserPtr& user) {
+ // If user is null, do nothing.
+ if (!user) {
+ return;
+ }
+
+ // If the user has bootfile property, update it in the response.
+ std::string opt_value = user->getProperty("bootfile");
+ if (!opt_value.empty()) {
+ std::cout << "DHCP UserCheckHook : add4Options "
+ << "adding boot file:" << opt_value << std::endl;
+
+ // Add boot file to packet.
+ add4Option(response, DHO_BOOT_FILE_NAME, opt_value);
+
+ // Boot file also goes in file field.
+ response->setFile((const uint8_t*)(opt_value.c_str()),
+ opt_value.length());
+ }
+
+ // If the user has tftp server property, update it in the response.
+ opt_value = user->getProperty("tftp_server");
+ if (!opt_value.empty()) {
+ std::cout << "DHCP UserCheckHook : add4Options "
+ << "adding TFTP server:" << opt_value << std::endl;
+
+ // Add tftp server option to packet.
+ add4Option(response, DHO_TFTP_SERVER_NAME, opt_value);
+ }
+ // add next option here
+}
+
+/// @brief Adds/updates are specific IPv4 string option in response packet.
+///
+/// @param response IPV4 response packet to update
+/// @param opt_code DHCP standard numeric code of the option
+/// @param opt_value String value of the option
+void add4Option(Pkt4Ptr& response, uint8_t opt_code, std::string& opt_value) {
+ // Remove the option if it exists.
+ OptionPtr opt = response->getOption(opt_code);
+ if (opt) {
+ response->delOption(opt_code);
+ }
+
+ // Now add the option.
+ opt.reset(new OptionString(Option::V4, opt_code, opt_value));
+ response->addOption(opt);
+}
+
+
+/// @brief Adds IPv6 vendor options to the response packet based on given user
+///
+/// Adds or replaces IPv6 vendor options with values from the given user, if
+/// the user has the corresponding properties defined. Currently it supports
+/// the following options:
+///
+/// - DOCSIS3_V6_CONFIG_FILE from user property "bootfile"
+/// - DOCSIS3_V6_TFTP_SERVERS from user property "tftp_server"
+///
+/// @param response IPv5 reponse packet
+/// @param user User from whom properties are sourced
+void add6Options(Pkt6Ptr& response, const UserPtr& user) {
+ if (!user) {
+ return;
+ }
+
+ /// @todo: if packets have no vendor opt... do we need to add it
+ /// if its not there? If so how?
+ OptionPtr vendor = response->getOption(D6O_VENDOR_OPTS);
+ if (!vendor) {
+ std::cout << "DHCP UserCheckHook : add6Options "
+ << "response has no vendor option to update" << std::endl;
+ return;
+ }
+
+ // If the user defines bootfile, set the option in response.
+ std::string opt_value = user->getProperty("bootfile");
+ if (!opt_value.empty()) {
+ std::cout << "DHCP UserCheckHook : add6Options "
+ << "adding boot file:" << opt_value << std::endl;
+ add6Option(vendor, DOCSIS3_V6_CONFIG_FILE, opt_value);
+ }
+
+ // If the user defines tftp server, set the option in response.
+ opt_value = user->getProperty("tftp_server");
+ if (!opt_value.empty()) {
+ std::cout << "DHCP UserCheckHook : add6Options "
+ << "adding tftp server:" << opt_value << std::endl;
+
+ add6Option(vendor, DOCSIS3_V6_TFTP_SERVERS, opt_value);
+ }
+
+ // add next option here
+}
+
+/// @brief Adds/updates a specific IPv6 string vendor option.
+///
+/// @param vendor IPv6 vendor option set to update
+/// @param opt_code DHCP standard numeric code of the option
+/// @param opt_value String value of the option
+void add6Option(OptionPtr& vendor, uint8_t opt_code, std::string& opt_value) {
+ vendor->delOption(opt_code);
+ OptionPtr option(new OptionString(Option::V6, opt_code, opt_value));
+ vendor->addOption(option);
+}
+
+
+/// @brief Adds an entry to the end of the user check outcome file.
+///
+/// @todo This ought to be replaced with an abstract output similar to
+/// UserDataSource to allow greater flexibility.
+///
+/// Each user entry is written in an ini-like format, with one name-value pair
+/// per line as follows:
+///
+/// id_type=<id type>
+/// client=<id str>
+/// subnet=<subnet str>
+/// registered=<is registered>"
+///
+/// where:
+/// <id type> text label of the id type: "HW_ADDR" or "DUID"
+/// <id str> user's id formatted as either isc::dhcp::Hwaddr.toText() or
+/// isc::dhcp::DUID.toText()
+/// <subnet str> selected subnet formatted as isc::dhcp::Subnet4::toText() or
+/// isc::dhcp::Subnet6::toText() as appropriate.
+/// <is registered> "yes" or "no"
+///
+/// Sample IPv4 entry would like this:
+///
+/// @code
+/// id_type=DUID
+/// client=00:01:00:01:19:ef:e6:3b:00:0c:01:02:03:04
+/// subnet=2001:db8:2::/64
+/// registered=yes
+/// id_type=duid
+/// @endcode
+///
+/// Sample IPv4 entry would like this:
+///
+/// @code
+/// id_type=DUID
+/// id_type=HW_ADDR
+/// client=hwtype=1 00:0c:01:02:03:05
+/// subnet=152.77.5.0/24
+/// registered=no
+/// @endcode
+///
+/// @param id_type_str text label identify the id type
+/// @param id_val_str text representation of the user id
+/// @param subnet_str text representation of the selected subnet
+/// @param registered boolean indicating if the user is registered or not
+void generate_output_record(const std::string& id_type_str,
+ const std::string& id_val_str,
+ const std::string& addr_str,
+ const bool& registered)
+{
+ user_chk_output << "id_type=" << id_type_str << std::endl
+ << "client=" << id_val_str << std::endl
+ << "addr=" << addr_str << std::endl
+ << "registered=" << (registered ? "yes" : "no")
+ << std::endl
+ << std::endl; // extra line in between
+
+ // @todo Flush is here to ensure output is immediate for demo purposes.
+ // Performance would generally dictate not using it.
+ flush(user_chk_output);
+}
+
+/// @brief Stringify the lease address or prefix IPv6 response packet
+///
+/// Converts the lease value, either an address or a prefix, into a string
+/// suitable for the user check outcome output. Note that this will use
+/// the first address or prefix in the response for responses with more than
+/// one value.
+///
+/// @param response IPv6 response packet from which to extract the lease value.
+///
+/// @return A string containing the lease value.
+/// @throw isc::BadValue if the response contains neither an IA_NA nor IA_PD
+/// option.
+std::string getV6AddrStr(Pkt6Ptr response) {
+ OptionPtr tmp = response->getOption(D6O_IA_NA);
+ if (tmp) {
+ return(getAddrStrIA_NA(tmp));
+ }
+
+ // IA_NA not there so try IA_PD
+ tmp = response->getOption(D6O_IA_PD);
+ if (!tmp) {
+ isc_throw (isc::BadValue, "Response has neither IA_NA nor IA_PD");
+ }
+
+ return(getAddrStrIA_PD(tmp));
+}
+
+/// @brief Stringify the lease address in an D6O_IA_NA option set
+///
+/// Converts the IA_NA lease address into a string suitable for the user check
+/// outcome output.
+///
+/// @param options pointer to the Option6IA instance from which to extract the
+/// lease address.
+///
+/// @return A string containing the lease address.
+///
+/// @throw isc::BadValue if the lease address cannot be extracted from options.
+std::string getAddrStrIA_NA(OptionPtr options) {
+ boost::shared_ptr<Option6IA> ia =
+ boost::dynamic_pointer_cast<Option6IA>(options);
+
+ if (!ia) {
+ isc_throw (isc::BadValue, "D6O_IA_NA option invalid");
+ }
+
+ // If status indicates a failure return a blank string.
+ if (!checkIAStatus(ia)) {
+ return (std::string(""));
+ }
+
+ options = ia->getOption(D6O_IAADDR);
+ if (!options) {
+ isc_throw (isc::BadValue, "D6O_IAADDR option missing");
+ }
+
+ boost::shared_ptr<Option6IAAddr> addr_option;
+ addr_option = boost::dynamic_pointer_cast<Option6IAAddr>(options);
+ if (!addr_option) {
+ isc_throw (isc::BadValue, "D6O_IAADDR Option6IAAddr missing");
+ }
+
+ isc::asiolink::IOAddress addr = addr_option->getAddress();
+ return (addr.toText());
+}
+
+/// @brief Stringify the lease prefix in an D6O_IA_PD option set
+///
+/// Converts the IA_PD lease prefix into a string suitable for the user check
+/// outcome output.
+///
+/// @param options pointer to the Option6IA instance from which to extract the
+/// lease prefix.
+///
+/// @return A string containing lease prefix
+///
+/// @throw isc::BadValue if the prefix cannot be extracted from options.
+std::string getAddrStrIA_PD(OptionPtr options) {
+ boost::shared_ptr<Option6IA> ia =
+ boost::dynamic_pointer_cast<Option6IA>(options);
+
+ // Make sure we have an IA_PD option.
+ if (!ia) {
+ isc_throw (isc::BadValue, "D6O_IA_PD option invalid");
+ }
+
+ // Check the response for success status. If it isn't a success response
+ // there will not be a lease prefix value which is denoted by returning
+ // an empty string.
+ if (!checkIAStatus(ia)) {
+ return (std::string(""));
+ }
+
+ // Get the prefix option the IA_PD option.
+ options = ia->getOption(D6O_IAPREFIX);
+ if (!options) {
+ isc_throw(isc::BadValue, "D60_IAPREFIX option is missing");
+ }
+
+ boost::shared_ptr<Option6IAPrefix> addr_option;
+ addr_option = boost::dynamic_pointer_cast<Option6IAPrefix>(options);
+ if (!addr_option) {
+ isc_throw (isc::BadValue, "D6O_IA_PD addr option bad");
+ }
+
+ // Get the address and prefix length values.
+ isc::asiolink::IOAddress addr = addr_option->getAddress();
+ uint8_t prefix_len = addr_option->getLength();
+
+ // Build the output string and return it.
+ stringstream buf;
+ buf << addr.toText() << "/" << static_cast<int>(prefix_len);
+ return (buf.str());
+}
+
+/// @brief Tests given IA option set for successful status.
+///
+/// This function is used to determine if the given Option6IA represents
+/// a successful lease operation. If it contains no status option or a status
+/// option of 0 (which is defined to mean success), then the option represents
+/// success and should contain a lease value (address or prefix).
+///
+/// @param ia pointer to the Option6IA to test
+///
+/// @return True if the option represents success, false otherwise.
+bool checkIAStatus(boost::shared_ptr<Option6IA>& ia) {
+ OptionCustomPtr status =
+ boost::dynamic_pointer_cast
+ <OptionCustom>(ia->getOption(D6O_STATUS_CODE));
+
+ // If a non-zero status is present, bail.
+ if (status) {
+ int status_val = status->readInteger<uint16_t>(0);
+ if (status_val != 0) {
+ std::cout << "SKIPPING PACKET STATUS is not success:"
+ << status_val << std::endl;
+ return (false);
+ }
+ }
+
+ return (true);
+}
+
+/// @brief Fetches the default IPv4 user from the registry.
+///
+/// The default user may be used to provide default property values.
+///
+/// @return A pointer to the IPv4 user or null if not defined.
+const UserPtr& getDefaultUser4() {
+ return (user_registry->findUser(UserId(UserId::HW_ADDRESS,
+ default_user4_id_str)));
+}
+
+/// @brief Fetches the default IPv6 user from the registry.
+///
+/// The default user may be used to provide default property values.
+///
+/// @return A pointer to the IPv6 user or null if not defined.
+const UserPtr& getDefaultUser6() {
+ return (user_registry->findUser(UserId(UserId::DUID,
+ default_user6_id_str)));
+}
+
diff --git a/src/hooks/dhcp/user_chk/subnet_select_co.cc b/src/hooks/dhcp/user_chk/subnet_select_co.cc
index 3e08f13..c30f37b 100644
--- a/src/hooks/dhcp/user_chk/subnet_select_co.cc
+++ b/src/hooks/dhcp/user_chk/subnet_select_co.cc
@@ -12,100 +12,37 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
-/// @file Defines the subnet4_select and subnet6_select callout functions.
+/// @file subnet_select.cc Defines the subnet4_select and subnet6_select callout functions.
#include <hooks/hooks.h>
#include <dhcp/pkt4.h>
#include <dhcp/dhcp6.h>
#include <dhcp/pkt6.h>
#include <dhcpsrv/subnet.h>
-#include <user_registry.h>
-
-#include <fstream>
-#include <string>
+#include <user_chk.h>
using namespace isc::dhcp;
using namespace isc::hooks;
+using namespace user_chk;
using namespace std;
-extern UserRegistryPtr user_registry;
-extern std::fstream user_chk_output;
-extern const char* registry_fname;
-extern const char* user_chk_output_fname;
-
// Functions accessed by the hooks framework use C linkage to avoid the name
// mangling that accompanies use of the C++ compiler as well as to avoid
// issues related to namespaces.
extern "C" {
-/// @brief Adds an entry to the end of the user check outcome file.
-///
-/// Each user entry is written in an ini-like format, with one name-value pair
-/// per line as follows:
-///
-/// id_type=<id type>
-/// client=<id str>
-/// subnet=<subnet str>
-/// registered=<is registered>"
-///
-/// where:
-/// <id type> text label of the id type: "HW_ADDR" or "DUID"
-/// <id str> user's id formatted as either isc::dhcp::Hwaddr.toText() or
-/// isc::dhcp::DUID.toText()
-/// <subnet str> selected subnet formatted as isc::dhcp::Subnet4::toText() or
-/// isc::dhcp::Subnet6::toText() as appropriate.
-/// <is registered> "yes" or "no"
-///
-/// Sample IPv4 entry would like this:
-///
-/// @code
-/// id_type=DUID
-/// client=00:01:00:01:19:ef:e6:3b:00:0c:01:02:03:04
-/// subnet=2001:db8:2::/64
-/// registered=yes
-/// id_type=duid
-/// @endcode
-///
-/// Sample IPv4 entry would like this:
-///
-/// @code
-/// id_type=DUID
-/// id_type=HW_ADDR
-/// client=hwtype=1 00:0c:01:02:03:05
-/// subnet=152.77.5.0/24
-/// registered=no
-/// @endcode
-///
-/// @param id_type_str text label identify the id type
-/// @param id_val_str text representation of the user id
-/// @param subnet_str text representation of the selected subnet
-/// @param registered boolean indicating if the user is registered or not
-void generate_output_record(const std::string& id_type_str,
- const std::string& id_val_str,
- const std::string& subnet_str,
- const bool& registered)
-{
- user_chk_output << "id_type=" << id_type_str << std::endl
- << "client=" << id_val_str << std::endl
- << "subnet=" << subnet_str << std::endl
- << "registered=" << (registered ? "yes" : "no")
- << std::endl;
-
- // @todo Flush is here to ensure output is immediate for demo purposes.
- // Performance would generally dictate not using it.
- flush(user_chk_output);
-}
-
/// @brief This callout is called at the "subnet4_select" hook.
///
-/// This function searches the UserRegistry for the client indicated by the
-/// inbound IPv4 DHCP packet. If the client is found in the registry output
-/// the generate outcome record and return.
+/// This function alters the selected subnet based upon whether or not the
+/// requesting DHCP client is a "registered user". It fetches a pointer to
+/// the registered user from the callout context. This value is presumed to
+/// have been set upstream. If it is non-null that it points to the client's
+/// user entry in the UserRegistry. If it is null, the client is not
+/// registered.
///
-/// If the client is not found in the registry replace the selected subnet
-/// with the restricted access subnet, then generate the outcome record and
-/// return. By convention, it is assumed that last subnet in the list of
-/// available subnets is the restricted access subnet.
+/// If the client is registered, then replace the selected subnet with the
+/// restricted access subnet. By convention, it is assumed that last subnet in
+/// the list of available subnets is the restricted access subnet.
///
/// @param handle CalloutHandle which provides access to context.
///
@@ -122,35 +59,23 @@ int subnet4_select(CalloutHandle& handle) {
const isc::dhcp::Subnet4Collection *subnets = NULL;
handle.getArgument("subnet4collection", subnets);
if (subnets->empty()) {
- return 0;
+ return (0);
}
- // Refresh the registry.
- user_registry->refresh();
-
- // Get the HWAddress as the user identifier.
- Pkt4Ptr query;
- handle.getArgument("query4", query);
- HWAddrPtr hwaddr = query->getHWAddr();
+ // Get registered_user pointer.
+ UserPtr registered_user;
+ handle.getContext(registered_user_label, registered_user);
- // Look for the user.
- UserPtr registered_user = user_registry->findUser(*hwaddr);
if (registered_user) {
// User is in the registry, so leave the pre-selected subnet alone.
Subnet4Ptr subnet;
handle.getArgument("subnet4", subnet);
- // Add the outcome entry to the output file.
- generate_output_record(UserId::HW_ADDRESS_STR, hwaddr->toText(),
- subnet->toText(), true);
} else {
// User is not in the registry, so assign them to the last subnet
// in the collection. By convention we are assuming this is the
// restricted subnet.
Subnet4Ptr subnet = subnets->back();
handle.setArgument("subnet4", subnet);
- // Add the outcome entry to the output file.
- generate_output_record(UserId::HW_ADDRESS_STR, hwaddr->toText(),
- subnet->toText(), false);
}
} catch (const std::exception& ex) {
std::cout << "DHCP UserCheckHook : subnet6_select unexpected error: "
@@ -163,14 +88,16 @@ int subnet4_select(CalloutHandle& handle) {
/// @brief This callout is called at the "subnet6_select" hook.
///
-/// This function searches the UserRegistry for the client indicated by the
-/// inbound IPv6 DHCP packet. If the client is found in the registry generate
-/// the outcome record and return.
+/// This function alters the selected subnet based upon whether or not the
+/// requesting DHCP client is a "registered user". It fetches a pointer to
+/// the registered user from the callout context. This value is presumed to
+/// have been set upstream. If it is non-null that it points to the client's
+/// user entry in the UserRegistry. If it is null, the client is not
+/// registered.
///
-/// If the client is not found in the registry replace the selected subnet
-/// with the restricted access subnet, then generate the outcome record and
-/// return. By convention, it is assumed that last subnet in the list of
-/// available subnets is the restricted access subnet.
+/// If the client is registered, then replace the selected subnet with the
+/// restricted access subnet. By convention, it is assumed that last subnet in
+/// the list of available subnets is the restricted access subnet.
///
/// @param handle CalloutHandle which provides access to context.
///
@@ -187,43 +114,23 @@ int subnet6_select(CalloutHandle& handle) {
const isc::dhcp::Subnet6Collection *subnets = NULL;
handle.getArgument("subnet6collection", subnets);
if (subnets->empty()) {
- return 0;
- }
-
- // Refresh the registry.
- user_registry->refresh();
-
- // Get the HWAddress as the user identifier.
- Pkt6Ptr query;
- handle.getArgument("query6", query);
-
- DuidPtr duid;
- OptionPtr opt_duid = query->getOption(D6O_CLIENTID);
- if (!opt_duid) {
- std::cout << "DHCP6 query is missing DUID" << std::endl;
- return (1);
+ return (0);
}
- duid = DuidPtr(new DUID(opt_duid->getData()));
+ // Get registered_user pointer.
+ UserPtr registered_user;
+ handle.getContext(registered_user_label, registered_user);
- // Look for the user.
- UserPtr registered_user = user_registry->findUser(*duid);
if (registered_user) {
// User is in the registry, so leave the pre-selected subnet alone.
Subnet6Ptr subnet;
handle.getArgument("subnet6", subnet);
- // Add the outcome entry to the output file.
- generate_output_record(UserId::DUID_STR, duid->toText(),
- subnet->toText(), true);
} else {
// User is not in the registry, so assign them to the last subnet
// in the collection. By convention we are assuming this is the
// restricted subnet.
Subnet6Ptr subnet = subnets->back();
handle.setArgument("subnet6", subnet);
- // Add the outcome entry to the output file.
- generate_output_record(UserId::DUID_STR, duid->toText(),
- subnet->toText(), false);
}
} catch (const std::exception& ex) {
std::cout << "DHCP UserCheckHook : subnet6_select unexpected error: "
diff --git a/src/hooks/dhcp/user_chk/tests/Makefile.am b/src/hooks/dhcp/user_chk/tests/Makefile.am
index d76f215..3a73f6d 100644
--- a/src/hooks/dhcp/user_chk/tests/Makefile.am
+++ b/src/hooks/dhcp/user_chk/tests/Makefile.am
@@ -34,9 +34,12 @@ TESTS += libdhcp_user_chk_unittests
libdhcp_user_chk_unittests_SOURCES =
libdhcp_user_chk_unittests_SOURCES += ../load_unload.cc
+libdhcp_user_chk_unittests_SOURCES += ../pkt_receive_co.cc
+libdhcp_user_chk_unittests_SOURCES += ../pkt_send_co.cc
libdhcp_user_chk_unittests_SOURCES += ../subnet_select_co.cc
libdhcp_user_chk_unittests_SOURCES += ../version.cc
libdhcp_user_chk_unittests_SOURCES += ../user.cc ../user.h
+libdhcp_user_chk_unittests_SOURCES += ../user_chk.h
# Until logging in dynamically loaded libraries is fixed, exclude these.
#libdhcp_user_chk_unittests_SOURCES += ../user_chk_log.cc ../user_chk_log.h
#libdhcp_user_chk_unittests_SOURCES += ../user_chk_messages.cc ../user_chk_messages.h
@@ -67,6 +70,7 @@ libdhcp_user_chk_unittests_LDADD += $(top_builddir)/src/lib/hooks/libb10-hooks.l
libdhcp_user_chk_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
libdhcp_user_chk_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
libdhcp_user_chk_unittests_LDADD += $(top_builddir)/src/lib/cc/libb10-cc.la
+libdhcp_user_chk_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
libdhcp_user_chk_unittests_LDADD += ${BOTAN_LIBS} ${BOTAN_RPATH}
libdhcp_user_chk_unittests_LDADD += $(GTEST_LDADD)
endif
diff --git a/src/hooks/dhcp/user_chk/tests/test_users_1.txt b/src/hooks/dhcp/user_chk/tests/test_users_1.txt
index 5fa718f..20ee232 100644
--- a/src/hooks/dhcp/user_chk/tests/test_users_1.txt
+++ b/src/hooks/dhcp/user_chk/tests/test_users_1.txt
@@ -1,2 +1,4 @@
{ "type" : "HW_ADDR", "id" : "01AC00F03344", "opt1" : "true" }
+{ "type" : "HW_ADDR", "id" : "01:AC:00:F0:33:45", "opt1" : "true" }
{ "type" : "DUID", "id" : "225060de0a0b", "opt1" : "true" }
+{ "type" : "DUID", "id" : "22:50:60:de:0a:0c", "opt1" : "true" }
diff --git a/src/hooks/dhcp/user_chk/tests/user_file_unittests.cc b/src/hooks/dhcp/user_chk/tests/user_file_unittests.cc
index 9507fab..bc98c9a 100644
--- a/src/hooks/dhcp/user_chk/tests/user_file_unittests.cc
+++ b/src/hooks/dhcp/user_chk/tests/user_file_unittests.cc
@@ -22,6 +22,7 @@
#include <gtest/gtest.h>
using namespace std;
+using namespace user_chk;
namespace {
@@ -128,7 +129,7 @@ TEST(UserFile, readFile) {
ASSERT_NO_THROW(user_file->open());
EXPECT_TRUE(user_file->isOpen());
- // File should contain two valid users. Read and verify each.
+ // File should contain four valid users. Read and verify each.
UserPtr user;
int i = 0;
do {
@@ -140,10 +141,22 @@ TEST(UserFile, readFile) {
EXPECT_EQ("true", user->getProperty("opt1"));
break;
case 1:
+ // File entry should have colons in id.
+ EXPECT_EQ(UserId::HW_ADDRESS, user->getUserId().getType());
+ EXPECT_EQ("01ac00f03345", user->getUserId().toText());
+ EXPECT_EQ("true", user->getProperty("opt1"));
+ break;
+ case 2:
EXPECT_EQ(UserId::DUID, user->getUserId().getType());
EXPECT_EQ("225060de0a0b", user->getUserId().toText());
EXPECT_EQ("true", user->getProperty("opt1"));
break;
+ case 3:
+ // File entry should have colons in id.
+ EXPECT_EQ(UserId::DUID, user->getUserId().getType());
+ EXPECT_EQ("225060de0a0c", user->getUserId().toText());
+ EXPECT_EQ("true", user->getProperty("opt1"));
+ break;
default:
// Third time around, we are at EOF User should be null.
ASSERT_FALSE(user);
diff --git a/src/hooks/dhcp/user_chk/tests/user_registry_unittests.cc b/src/hooks/dhcp/user_chk/tests/user_registry_unittests.cc
index 1f250c8..44e87c1 100644
--- a/src/hooks/dhcp/user_chk/tests/user_registry_unittests.cc
+++ b/src/hooks/dhcp/user_chk/tests/user_registry_unittests.cc
@@ -24,6 +24,7 @@
#include <gtest/gtest.h>
using namespace std;
+using namespace user_chk;
namespace {
diff --git a/src/hooks/dhcp/user_chk/tests/user_unittests.cc b/src/hooks/dhcp/user_chk/tests/user_unittests.cc
index 2ca156c..8d76732 100644
--- a/src/hooks/dhcp/user_chk/tests/user_unittests.cc
+++ b/src/hooks/dhcp/user_chk/tests/user_unittests.cc
@@ -21,6 +21,7 @@
#include <gtest/gtest.h>
using namespace std;
+using namespace user_chk;
namespace {
diff --git a/src/hooks/dhcp/user_chk/tests/userid_unittests.cc b/src/hooks/dhcp/user_chk/tests/userid_unittests.cc
index c31c4ae..cc96353 100644
--- a/src/hooks/dhcp/user_chk/tests/userid_unittests.cc
+++ b/src/hooks/dhcp/user_chk/tests/userid_unittests.cc
@@ -21,6 +21,7 @@
#include <gtest/gtest.h>
using namespace std;
+using namespace user_chk;
namespace {
@@ -77,6 +78,11 @@ TEST(UserIdTest, hwAddress_type) {
EXPECT_FALSE(*id == *id2);
EXPECT_TRUE(*id != *id2);
EXPECT_FALSE(*id < *id2);
+
+ // Verify that colon delimiters are ok.
+ ASSERT_NO_THROW(id2.reset(new UserId(UserId::HW_ADDRESS,
+ "01:FF:02:AC:03:0B:07:07")));
+ EXPECT_FALSE(*id == *id2);
}
/// @brief Test making and using DUID type UserIds
@@ -118,6 +124,11 @@ TEST(UserIdTest, duid_type) {
EXPECT_FALSE(*id == *id2);
EXPECT_TRUE(*id != *id2);
EXPECT_FALSE(*id < *id2);
+
+ // Verify that colon delimiters are ok.
+ ASSERT_NO_THROW(id2.reset(new UserId(UserId::DUID,
+ "01:FF:02:AC:03:0B:07:08")));
+ EXPECT_TRUE(*id == *id2);
}
/// @brief Tests that UserIds of different types compare correctly.
@@ -135,5 +146,4 @@ TEST(UserIdTest, mixed_type_compare) {
EXPECT_TRUE(*hw < *duid);
}
-
} // end of anonymous namespace
diff --git a/src/hooks/dhcp/user_chk/user.cc b/src/hooks/dhcp/user_chk/user.cc
index c7c18fa..2bf95cf 100644
--- a/src/hooks/dhcp/user_chk/user.cc
+++ b/src/hooks/dhcp/user_chk/user.cc
@@ -22,6 +22,8 @@
#include <iomanip>
#include <sstream>
+namespace user_chk {
+
//********************************* UserId ******************************
const char* UserId::HW_ADDRESS_STR = "HW_ADDR";
@@ -43,7 +45,14 @@ UserId::UserId(UserIdType id_type, const std::string & id_str) :
// Convert the id string to vector.
// Input is expected to be 2-digits per bytes, no delimiters.
std::vector<uint8_t> addr_bytes;
- isc::util::encode::decodeHex(id_str, addr_bytes);
+
+ // Strip out colon delimeters, decodeHex doesn't like them.
+ std::string clean_id_str = id_str;
+ std::string::iterator end_pos = std::remove(clean_id_str.begin(),
+ clean_id_str.end(), ':');
+ clean_id_str.erase(end_pos, clean_id_str.end());
+
+ isc::util::encode::decodeHex(clean_id_str, addr_bytes);
// Attempt to instantiate the appropriate id class to leverage validation.
switch (id_type) {
@@ -208,3 +217,5 @@ const UserId&
User::getUserId() const {
return (user_id_);
}
+
+} // namespace user_chk
diff --git a/src/hooks/dhcp/user_chk/user.h b/src/hooks/dhcp/user_chk/user.h
index 20167b6..b2d0f16 100644
--- a/src/hooks/dhcp/user_chk/user.h
+++ b/src/hooks/dhcp/user_chk/user.h
@@ -21,6 +21,8 @@
#include <stdint.h>
#include <vector>
+namespace user_chk {
+
/// @file user.h This file defines classes: UserId and User.
/// @brief These classes are used to describe and recognize DHCP lease
/// clients.
@@ -63,7 +65,7 @@ public:
///
/// @param id_type The type of user id contained in string.
/// The string is expected to contain an even number of hex digits
- /// without delimiters.
+ /// with or without colon (':') as a delimiter.
///
/// @param id a vector of unsigned bytes containing the id
///
@@ -246,4 +248,6 @@ private:
/// @brief Defines a smart pointer to a User.
typedef boost::shared_ptr<User> UserPtr;
+} // namespace user_chk
+
#endif
diff --git a/src/hooks/dhcp/user_chk/user_chk.h b/src/hooks/dhcp/user_chk/user_chk.h
new file mode 100644
index 0000000..1bc2fc3
--- /dev/null
+++ b/src/hooks/dhcp/user_chk/user_chk.h
@@ -0,0 +1,51 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+#ifndef USER_CHK_H
+#define USER_CHK_H
+
+#include <user_registry.h>
+#include <fstream>
+#include <string>
+
+using namespace std;
+using namespace user_chk;
+
+// The following constants are used throughout the library. They are defined
+// in load_unload.cc
+
+/// @brief Pointer to the registry instance.
+extern UserRegistryPtr user_registry;
+
+/// @brief Output filestream for recording user check outcomes.
+extern std::fstream user_chk_output;
+
+/// @brief User registry input file name.
+extern const char* registry_fname;
+
+/// @brief User check outcome file name.
+extern const char* user_chk_output_fname;
+
+/// @brief Text label of user id in the inbound query in callout context
+extern const char* query_user_id_label;
+
+/// @brief Text label of registered user pointer in callout context
+extern const char* registered_user_label;
+
+/// @brief Text id used to identify the default IPv4 user in the registry
+extern const char* default_user4_id_str;
+
+/// @brief Text id used to identify the default IPv6 user in the registry
+extern const char* default_user6_id_str;
+
+#endif
diff --git a/src/hooks/dhcp/user_chk/user_chk_log.cc b/src/hooks/dhcp/user_chk/user_chk_log.cc
index f104efb..7e4d87f 100644
--- a/src/hooks/dhcp/user_chk/user_chk_log.cc
+++ b/src/hooks/dhcp/user_chk/user_chk_log.cc
@@ -15,4 +15,8 @@
/// Defines the logger used by the user check hooks library.
#include <user_chk_log.h>
+namespace user_chk {
+
isc::log::Logger user_chk_logger("user_chk");
+
+} // namespace user_chk
diff --git a/src/hooks/dhcp/user_chk/user_data_source.h b/src/hooks/dhcp/user_chk/user_data_source.h
index a1842cf..5145e10 100644
--- a/src/hooks/dhcp/user_chk/user_data_source.h
+++ b/src/hooks/dhcp/user_chk/user_data_source.h
@@ -18,6 +18,8 @@
#include <exceptions/exceptions.h>
#include <user.h>
+namespace user_chk {
+
/// @brief Thrown if UserDataSource encounters an error
class UserDataSourceError : public isc::Exception {
public:
@@ -72,4 +74,6 @@ public:
/// @brief Defines a smart pointer to a UserDataSource.
typedef boost::shared_ptr<UserDataSource> UserDataSourcePtr;
+} // namespace user_chk
+
#endif
diff --git a/src/hooks/dhcp/user_chk/user_file.cc b/src/hooks/dhcp/user_chk/user_file.cc
index 4181dc4..3ffc99e 100644
--- a/src/hooks/dhcp/user_chk/user_file.cc
+++ b/src/hooks/dhcp/user_chk/user_file.cc
@@ -20,6 +20,8 @@
#include <errno.h>
#include <iostream>
+namespace user_chk {
+
UserFile::UserFile(const std::string& fname) : fname_(fname), file_() {
if (fname_.empty()) {
isc_throw(UserFileError, "file name cannot be blank");
@@ -157,3 +159,4 @@ UserFile::close() {
}
}
+} // namespace user_chk
diff --git a/src/hooks/dhcp/user_chk/user_file.h b/src/hooks/dhcp/user_chk/user_file.h
index b65862e..873dddf 100644
--- a/src/hooks/dhcp/user_chk/user_file.h
+++ b/src/hooks/dhcp/user_chk/user_file.h
@@ -25,6 +25,8 @@
using namespace std;
+namespace user_chk {
+
/// @brief Thrown a UserFile encounters an error.
/// Note that it derives from UserDataSourceError to comply with the interface.
class UserFileError : public UserDataSourceError {
@@ -44,7 +46,8 @@ public:
/// where:
///
/// <user_type> text label of the id type: "HW_ADDR" or "DUID"
-/// <user_id> the user's id as a string of hex digits without delimiters
+/// <user_id> the user's id as a string of hex digits with or without
+/// colons (':') as a delimiter
/// (options) zero or more string elements as name-value pairs, separated by
/// commas: "opt1" : "val1", "other_opt", "77" ...
///
@@ -132,4 +135,6 @@ private:
/// @brief Defines a smart pointer to a UserFile.
typedef boost::shared_ptr<UserFile> UserFilePtr;
+} // namespace user_chk
+
#endif
diff --git a/src/hooks/dhcp/user_chk/user_registry.cc b/src/hooks/dhcp/user_chk/user_registry.cc
index 44b98a5..80e7a93 100644
--- a/src/hooks/dhcp/user_chk/user_registry.cc
+++ b/src/hooks/dhcp/user_chk/user_registry.cc
@@ -15,6 +15,8 @@
#include <user_registry.h>
#include <user.h>
+namespace user_chk {
+
UserRegistry::UserRegistry() {
}
@@ -120,3 +122,4 @@ const UserDataSourcePtr& UserRegistry::getSource() {
return (source_);
}
+} // namespace user_chk
diff --git a/src/hooks/dhcp/user_chk/user_registry.h b/src/hooks/dhcp/user_chk/user_registry.h
index a0fb7dc..48dc32c 100644
--- a/src/hooks/dhcp/user_chk/user_registry.h
+++ b/src/hooks/dhcp/user_chk/user_registry.h
@@ -26,6 +26,8 @@
using namespace std;
+namespace user_chk {
+
/// @brief Thrown UserRegistry encounters an error
class UserRegistryError : public isc::Exception {
public:
@@ -125,4 +127,6 @@ private:
/// @brief Define a smart pointer to a UserRegistry.
typedef boost::shared_ptr<UserRegistry> UserRegistryPtr;
+} // namespace user_chk
+
#endif
More information about the bind10-changes
mailing list