Commit f3e7cffe authored by Thomas Markwalder's avatar Thomas Markwalder

[4095] Implemented new classes for storing client class definitions

src/lib/dhcpsrv/client_class_def.cc
src/lib/dhcpsrv/client_class_def.h
src/lib/dhcpsrv/tests/client_class_def_unittest.cc
    New files that define client class storage classes and tests

src/lib/dhcp/classify.h
    Amended commentary to reflect how these classes are now being used.

src/lib/dhcp/option.h
    Added typedefs:
        boost::shared_ptr<Option> OptionPtr;
        boost::shared_ptr<OptionCollection> OptionCollectionPtr;

src/lib/dhcpsrv/Makefile.am
    Added entries for new files

src/lib/dhcpsrv/tests/Makefile.am
    Added entries for new file

src/lib/eval/token.h b/src/lib/eval/token.h
    Added typedef:
        boost::shared_ptr<Expression> ExpressionPtr;
parent b8cfb9d2
......@@ -20,11 +20,14 @@
/// @file classify.h
///
/// @brief Defines basic elements of client classification.
/// @brief Defines elements for storing the names of client classes
///
/// This file defines common elements used for client classification.
/// It is simple for now, but the complexity involved in client
/// classification is expected to grow significantly.
/// This file defines common elements used to track the client classes
/// that may be associated with a given packet. In order to minimize the
/// exposure of the DHCP library to server side concepts such as client
/// classification the classes herein provide a mechanism to maintain lists
/// of class names, rather than the classes they represent. It is the
/// upper layers' perogative to use these names as they see fit.
///
/// @todo This file should be moved to dhcpsrv eventually as the classification
/// is server side concept. Client has no notion of classifying incoming server
......@@ -36,10 +39,10 @@ namespace isc {
namespace dhcp {
/// Definition of a single class.
/// @brief Defines a single class name.
typedef std::string ClientClass;
/// @brief Container for storing client classes
/// @brief Container for storing client class names
///
/// Depending on how you look at it, this is either a little more than just
/// a set of strings or a client classifier that performs access control.
......
......@@ -47,6 +47,8 @@ typedef boost::shared_ptr<Option> OptionPtr;
/// A collection of DHCP (v4 or v6) options
typedef std::multimap<unsigned int, OptionPtr> OptionCollection;
/// A poitner to an OptionCollection
typedef boost::shared_ptr<OptionCollection> OptionCollectionPtr;
/// @brief This type describes a callback function to parse options from buffer.
///
......
......@@ -88,6 +88,7 @@ libkea_dhcpsrv_la_SOURCES += cfg_subnets4.cc cfg_subnets4.h
libkea_dhcpsrv_la_SOURCES += cfg_subnets6.cc cfg_subnets6.h
libkea_dhcpsrv_la_SOURCES += cfg_mac_source.cc cfg_mac_source.h
libkea_dhcpsrv_la_SOURCES += cfgmgr.cc cfgmgr.h
libkea_dhcpsrv_la_SOURCES += client_class_def.cc client_class_def.h
libkea_dhcpsrv_la_SOURCES += csv_lease_file4.cc csv_lease_file4.h
libkea_dhcpsrv_la_SOURCES += csv_lease_file6.cc csv_lease_file6.h
libkea_dhcpsrv_la_SOURCES += d2_client_cfg.cc d2_client_cfg.h
......
// Copyright (C) 2015 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.
#include "client_class_def.h"
namespace isc {
namespace dhcp {
//********** ClientClassDef ******************//
ClientClassDef::ClientClassDef(const std::string& name,
const ExpressionPtr& match_expr,
const OptionCollectionPtr& options)
: name_(name), match_expr_(match_expr), options_(options) {
// Name can't be blank
if (name_.empty()) {
isc_throw(BadValue, "ClientClassDef name cannot be empty");
}
// @todo Does it make sense for a class to NOT have match expression?
// For classes without options, make sure we have an empty collection
if (!options_) {
options_.reset(new OptionCollection());
}
}
ClientClassDef::~ClientClassDef() {
}
std::string
ClientClassDef::getName() const {
return (name_);
}
void
ClientClassDef::setName(const std::string& name) {
name_ = name;
}
const ExpressionPtr&
ClientClassDef::getMatchExpr() const {
return (match_expr_);
}
void
ClientClassDef::setMatchExpr(const ExpressionPtr& match_expr) {
match_expr_ = match_expr;
}
const OptionCollectionPtr&
ClientClassDef::getOptions() const {
return (options_);
}
void
ClientClassDef::setOptions(const OptionCollectionPtr& options) {
options_ = options;
}
OptionPtr
ClientClassDef::findOption(uint16_t option_code) const {
if (options_) {
isc::dhcp::OptionCollection::iterator it = options_->find(option_code);
if (it != options_->end()) {
return ((*it).second);
}
}
return (OptionPtr());
}
std::ostream& operator<<(std::ostream& os, const ClientClassDef& x) {
os << "ClientClassDef:" << x.getName();
return (os);
}
//********** ClientClassDictionary ******************//
ClientClassDictionary::ClientClassDictionary()
: classes_(new ClientClassDefMap()) {
}
ClientClassDictionary::~ClientClassDictionary() {
}
void
ClientClassDictionary::addClass(const std::string& name,
const ExpressionPtr& match_expr,
const OptionCollectionPtr& options) {
ClientClassDefPtr cclass(new ClientClassDef(name, match_expr, options));
addClass(cclass);
}
void
ClientClassDictionary::addClass(ClientClassDefPtr& class_def) {
if (!class_def) {
isc_throw(BadValue, "ClientClassDictionary::addClass "
" - class definition cannot be null");
}
if (findClass(class_def->getName())) {
isc_throw(DuplicateClientClassDef, "Client Class: "
<< class_def->getName() << " has already been defined");
}
(*classes_)[class_def->getName()] = class_def;
}
ClientClassDefPtr
ClientClassDictionary::findClass(const std::string& name) const {
ClientClassDefMap::iterator it = classes_->find(name);
if (it != classes_->end()) {
return (*it).second;
}
return(ClientClassDefPtr());
}
void
ClientClassDictionary::removeClass(const std::string& name) {
classes_->erase(name);
}
const ClientClassDefMapPtr&
ClientClassDictionary::getClasses() const {
return (classes_);
}
} // namespace isc::dhcp
} // namespace isc
// Copyright (C) 2015 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 CLIENT_CLASS_DEF_H
#define CLIENT_CLASS_DEF_H
#include <dhcp/option.h>
#include <eval/token.h>
#include <exceptions/exceptions.h>
#include <string>
/// @file client_class_def.h
///
/// @brief Defines classes for storing client class definitions
///
/// The file defines the class, ClientClassDef, which houses the
/// information for single client class such as the class name, the
/// logical expression used to identify members of the class, and options
/// that may be attributed to class members.
///
/// In addition it defines a continer class, ClientClassDictionary, which
/// is houses class definitions keyed by class name.
///
namespace isc {
namespace dhcp {
/// @brief Error that occurs when an attempt is made to add a duplicate class
/// to a class dictionary.
class DuplicateClientClassDef : public isc::Exception {
public:
DuplicateClientClassDef(const char* file, size_t line, const char* what)
: isc::Exception(file, line, what) {}
};
/// @brief Embodies a single client class definition
class ClientClassDef {
public:
/// @brief Constructor
///
/// @param name Name to assign to this class
/// @param match_expr Expression the class will use to determine membership
/// @param options Collection of options members should be given
ClientClassDef(const std::string& name, const ExpressionPtr& match_expr,
const OptionCollectionPtr& options = OptionCollectionPtr());
/// @brief Destructor
virtual ~ClientClassDef();
/// @brief Fetches the class's name
std::string getName() const;
/// @brief Sets the class's name
///
/// @param name the name to assign the class
void setName(const std::string& name);
/// @brief Fetches the class's match expression
const ExpressionPtr& getMatchExpr() const;
/// @brief Sets the class's match expression
///
/// @param match_expr the expression to assign the class
void setMatchExpr(const ExpressionPtr& match_expr);
/// @brief Fetches the class's option collection
const OptionCollectionPtr& getOptions() const;
/// @brief Sets the class's option collection
///
/// @param options the option collection to assign the class
void setOptions(const OptionCollectionPtr& options);
/// @brief Fetches an option from the class's collection by code
///
/// @param option_code Option code value of the desired option
/// @return A pointer to the option if found, otherwise an
/// empty pointer
OptionPtr findOption(uint16_t option_code) const;
/// @brief Provides a convenient text representation of the class
friend std::ostream& operator<<(std::ostream& os, const ClientClassDef& x);
private:
/// @brief Unique text identifier by which this class is known.
std::string name_;
/// @brief The logical expression which deteremines membership in
/// this class.
ExpressionPtr match_expr_;
/// @brief The collection of options members should be given
/// Currently this is a multimap, not sure we need/want that complexity
OptionCollectionPtr options_;
};
/// @brief a pointer to an ClientClassDef
typedef boost::shared_ptr<ClientClassDef> ClientClassDefPtr;
/// @brief Defines a map of ClientClassDefes, keyed by the class name.
typedef std::map<std::string,ClientClassDefPtr> ClientClassDefMap;
/// @brief Defines a pointer to a ClientClassDictionary
typedef boost::shared_ptr<ClientClassDefMap> ClientClassDefMapPtr;
/// @brief Maintains a list of ClientClassDefes
class ClientClassDictionary {
public:
/// @brief Constructor
ClientClassDictionary();
/// @brief Destructor
~ClientClassDictionary();
/// @brief Adds a new class to the list
///
/// @param name Name to assign to this class
/// @param match_expr Expression the class will use to determine membership
/// @param options Collection of options members should be given
///
/// @throw DuplicateClientClassDef if class already exists within the
/// dictionary. See @ref dhcp::ClientClassDef::ClientClassDef() for
/// others.
void addClass(const std::string& name, const ExpressionPtr& match_expr,
const OptionCollectionPtr& options);
/// @brief Adds a new class to the list
///
/// @param class_def pointer to class definition to add
///
/// @throw DuplicateClientClassDef if class already exists within the
/// dictionary, BadValue if the pointer is empty.
void addClass(ClientClassDefPtr& class_def);
/// @brief Fetches the class definition for a given class name
///
/// @param name the name of the desired class
///
/// @return ClientClassDefPtr to the desired class if found, or
/// an empty pointer if not.
ClientClassDefPtr findClass(const std::string& name) const;
/// @brief Removes a given class definition from the dictionary
///
/// Removes the class defintion from the map if it exists, otherwise
/// no harm, no foul.
///
/// @param name the name of the class to remove
void removeClass(const std::string& name);
/// @brief Fetches the dictionary's map of classes
///
/// @return ClientClassDefMapPtr to the map of classes
const ClientClassDefMapPtr& getClasses() const;
private:
/// @brief Map of the class definitions
ClientClassDefMapPtr classes_;
};
/// @brief Defines a pointer to a ClientClassDictionary
typedef boost::shared_ptr<ClientClassDictionary> ClientClassDictionaryPtr;
} // namespace isc::dhcp
} // namespace isc
#endif // CLIENT_CLASS_DEF_H
......@@ -75,6 +75,7 @@ libdhcpsrv_unittests_SOURCES += cfg_rsoo_unittest.cc
libdhcpsrv_unittests_SOURCES += cfg_subnets4_unittest.cc
libdhcpsrv_unittests_SOURCES += cfg_subnets6_unittest.cc
libdhcpsrv_unittests_SOURCES += cfgmgr_unittest.cc
libdhcpsrv_unittests_SOURCES += client_class_def_unittest.cc
libdhcpsrv_unittests_SOURCES += csv_lease_file4_unittest.cc
libdhcpsrv_unittests_SOURCES += csv_lease_file6_unittest.cc
libdhcpsrv_unittests_SOURCES += d2_client_unittest.cc
......
// Copyright (C) 2015 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.
#include <config.h>
#include <dhcpsrv/client_class_def.h>
#include <exceptions/exceptions.h>
#include <boost/scoped_ptr.hpp>
#include <gtest/gtest.h>
using namespace std;
using namespace isc::dhcp;
using namespace isc::util;
using namespace isc;
namespace {
TEST(ClientClassDef, construction) {
boost::scoped_ptr<ClientClassDef> cclass;
std::string name = "class1";
ExpressionPtr expr;
OptionCollectionPtr options;
// Classes cannot have blank names
ASSERT_THROW(cclass.reset(new ClientClassDef("", expr, options)), BadValue);
// Verify we can create a class with a name, expression, and no options
ASSERT_NO_THROW(cclass.reset(new ClientClassDef(name, expr)));
EXPECT_EQ(name, cclass->getName());
ASSERT_FALSE(cclass->getMatchExpr());
// Verify we get an empty collection of options
options = cclass->getOptions();
ASSERT_TRUE(options);
EXPECT_EQ(0, options->size());
}
TEST(ClientClassDef, optionsBasics) {
boost::scoped_ptr<ClientClassDef> cclass;
std::string name = "class1";
ExpressionPtr expr;
OptionCollectionPtr options;
OptionPtr opt;
// First construct the class with empty option pointer
ASSERT_NO_THROW(cclass.reset(new ClientClassDef(name, expr, options)));
// We should get back a collection with no entries,
// not an empty collection pointer
options = cclass->getOptions();
ASSERT_TRUE(options);
EXPECT_EQ(0, options->size());
// We should not be able find an option
opt = cclass->findOption(17);
ASSERT_FALSE(opt);
// Create an option container with two options
options.reset(new OptionCollection());
EXPECT_NO_THROW(opt.reset(new Option(Option::V4, 17)));
options->insert(make_pair(17, opt));
EXPECT_NO_THROW(opt.reset(new Option(Option::V4, 18)));
options->insert(make_pair(18, opt));
// Now remake the client class with options
ASSERT_NO_THROW(cclass.reset(new ClientClassDef(name, expr, options)));
options = cclass->getOptions();
ASSERT_TRUE(options);
EXPECT_EQ(2, options->size());
// We should be able to find option 17
opt = cclass->findOption(17);
ASSERT_TRUE(opt);
EXPECT_EQ(17, opt->getType());
// We should be able to find option 18
opt = cclass->findOption(18);
ASSERT_TRUE(opt);
EXPECT_EQ(18, opt->getType());
// We should not be able to find option 90
opt = cclass->findOption(90);
ASSERT_FALSE(opt);
}
TEST(ClientClassDictionary, basics) {
ClientClassDictionaryPtr dictionary;
ClientClassDefPtr cclass;
ExpressionPtr expr;
OptionCollectionPtr options;
// Verify constructor doesn't throw
ASSERT_NO_THROW(dictionary.reset(new ClientClassDictionary()));
// Verify we can fetch a pointer the map of classes and
// that we start with no classes defined
const ClientClassDefMapPtr classes = dictionary->getClasses();
ASSERT_TRUE(classes);
EXPECT_EQ(0, classes->size());
// Verify that we can add classes with both addClass variants
// First addClass(name, expression, options)
ASSERT_NO_THROW(dictionary->addClass("cc1", expr, options));
ASSERT_NO_THROW(dictionary->addClass("cc2", expr, options));
// Verify duplicate add attempt throws
ASSERT_THROW(dictionary->addClass("cc2", expr, options),
DuplicateClientClassDef);
// Verify that you cannot add a class with no name.
ASSERT_THROW(dictionary->addClass("", expr, options), BadValue);
// Now with addClass(class pointer)
ASSERT_NO_THROW(cclass.reset(new ClientClassDef("cc3", expr, options)));
ASSERT_NO_THROW(dictionary->addClass(cclass));
// Verify duplicate add attempt throws
ASSERT_THROW(dictionary->addClass(cclass), DuplicateClientClassDef);
// Verify that you cannot add emtpy class pointer
cclass.reset();
ASSERT_THROW(dictionary->addClass(cclass), BadValue);
// Map should show 3 entries.
EXPECT_EQ(3, classes->size());
// Verify we can find them all.
ASSERT_NO_THROW(cclass = dictionary->findClass("cc1"));
ASSERT_TRUE(cclass);
EXPECT_EQ("cc1", cclass->getName());
ASSERT_NO_THROW(cclass = dictionary->findClass("cc2"));
ASSERT_TRUE(cclass);
EXPECT_EQ("cc2", cclass->getName());
ASSERT_NO_THROW(cclass = dictionary->findClass("cc3"));
ASSERT_TRUE(cclass);
EXPECT_EQ("cc3", cclass->getName());
// Verify the looking for non-existant returns empty pointer
ASSERT_NO_THROW(cclass = dictionary->findClass("bogus"));
EXPECT_FALSE(cclass);
// Verify that we can remove a class
ASSERT_NO_THROW(dictionary->removeClass("cc3"));
EXPECT_EQ(2, classes->size());
// Shouldn't be able to find anymore
ASSERT_NO_THROW(cclass = dictionary->findClass("cc3"));
EXPECT_FALSE(cclass);
// Verify that we can attempt to remove a non-existant class
// without harm.
ASSERT_NO_THROW(dictionary->removeClass("cc3"));
EXPECT_EQ(2, classes->size());
}
} // end of anonymous namespace
......@@ -35,6 +35,8 @@ typedef boost::shared_ptr<Token> TokenPtr;
/// [2] = == operator (TokenEqual object)
typedef std::vector<TokenPtr> Expression;
typedef boost::shared_ptr<Expression> ExpressionPtr;
/// Evaluated values are stored as a stack of strings
typedef std::stack<std::string> ValueStack;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment