Commit 116962dd authored by Francis Dupont's avatar Francis Dupont

[5549a] Code, examples and doc updated

parent f079ed3f
......@@ -44,12 +44,6 @@
"re-detect": true
},
// Define the unknown client class.
"client-classes": [ {
"name": "unknown",
"test": "not member('KNOWN')"
} ],
// Option 43 last resort definition can make well-formed messages
// to be rejected because they use not compatible "raw" value,
// and different vendors may define different sub-options.
......@@ -152,14 +146,16 @@
{
// This subnet is divided in two pools for unknown and
// known (i.e. which have a reservation) clients.
// The built-in KNOWN class is set or not at host reservation
// lookup and client classes depending on it are evaluated.
// The built-in KNOWN and UNKNOWN classes are set or not
// at host reservation lookup (KNOWN if this returns something,
// UNKNOWN if this finds nothing) and client classes depending
// on it are evaluated.
// This happens after subnet selection and before address
// allocation from pools.
"pools": [
{
"pool": "192.0.8.100 - 192.0.8.200",
"client-class": "unknown"
"client-class": "UNKNOWN"
},
{
"pool": "192.0.9.100 - 192.0.9.200",
......
......@@ -23,12 +23,6 @@
"re-detect": true
},
// Define the unknown client class.
"client-classes": [ {
"name": "unknown",
"test": "not member('KNOWN')"
} ],
// We need to specify the the database used to store leases. As of
// September 2016, four database backends are supported: MySQL,
// PostgreSQL, Cassandra, and the in-memory database, Memfile.
......@@ -147,16 +141,18 @@
}
},
{
// This subnet is divided in two pools for unknown and
// known (i.e. which have a reservation) clients.
// The built-in KNOWN class is set or not at host reservation
// lookup and client classes depending on it are evaluated.
// This happens after subnet selection and before address
// allocation from pools.
// This subnet is divided in two pools for unknown and
// known (i.e. which have a reservation) clients.
// The built-in KNOWN and UNKNOWN classes are set or not
// at host reservation lookup (KNOWN if this returns something,
// UNKNOWN if this finds nothing) and client classes depending
// on it are evaluated.
// This happens after subnet selection and before address/prefix
// allocation from [pd]pools.
"pools": [
{
"pool": "2001:db8:8::/64",
"client-class": "unknown"
"client-class": "UNKNOWN"
},
{
"pool": "2001:db8:9::/64",
......
......@@ -78,10 +78,11 @@
</para></listitem>
<listitem><para>
Classes with matching expressions and not marked for later ("on
request" or depending on the KNOWN builtin class) evaluation are
processed in the order they are defined in the configuration:
the boolean expression is evaluated and when it returns true
("match") the incoming packet is associated to the class.
request" or depending on the KNOWN/UNKNOWN builtin classes)
evaluation are processed in the order they are defined in the
configuration: the boolean expression is evaluated and when it
returns true ("match") the incoming packet is associated to the
class.
</para></listitem>
<listitem><para>
If a private or code 43 DHCPv4 option is received, decoding it
......@@ -98,18 +99,19 @@
that has a class which matches one of the packet's classes.
</para></listitem>
<listitem><para>
Host reservations are looked for. If an identifier from the incoming
packet matches a host reservation in the subnet or shared network,
the packet is associated with the KNOWN builtin class and all classes
of the host reservation.
Host reservations are looked for. If an identifier from the
incoming packet matches a host reservation in the subnet or
shared network, the packet is associated with either the KNOWN
or the UNKNOWN builtin classes and all classes of the host
reservation.
</para></listitem>
<listitem><para>
Classes with matching expressions using directly or indirectly
the KNOWN builtin class and not marked for later ("on request")
evaluation are processed in the order they are defined in the
configuration: the boolean expression is evaluated and when it
returns true ("match") the incoming packet is associated to the
class.
the KNOWN/UNKNOWN builtin classes and not marked for later ("on
request") evaluation are processed in the order they are defined
in the configuration: the boolean expression is evaluated and
when it returns true ("match") the incoming packet is associated
to the class.
</para></listitem>
<listitem><para>
If needed, addresses and prefixes from pools are assigned,
......@@ -189,11 +191,12 @@
begin with all capital letters.
</para>
<para>Currently recognized builtin class names are ALL and KNOWN
and prefixes VENDOR_CLASS_, AFTER_ and EXTERNAL_. The AFTER_ prefix
is a provision for a not yet written hook, the EXTERNAL_ prefix
can be freely used: builtin classes are implicitly defined so
never raise warnings if they do not appear in the configuration.
<para>Currently recognized builtin class names are ALL, KNOWN
and UNKNOWN, and prefixes VENDOR_CLASS_, AFTER_ and
EXTERNAL_. The AFTER_ prefix is a provision for a not yet
written hook, the EXTERNAL_ prefix can be freely used: builtin
classes are implicitly defined so never raise warnings if they
do not appear in the configuration.
</para>
</section>
......@@ -236,11 +239,12 @@
</para>
<para>
Dependencies between classes are checked too: for instance forward
dependencies are rejected when the configuration is parsed:
an expression can only depend on already defined classes (including
builtin classes) and which are evaluated in a previous or the
same evaluation phase. This does not apply to the KNOWN class.
Dependencies between classes are checked too: for instance
forward dependencies are rejected when the configuration is
parsed: an expression can only depend on already defined classes
(including builtin classes) and which are evaluated in a
previous or the same evaluation phase. This does not apply to
the KNOWN or UNKNOWN classes.
</para>
<para>
......@@ -326,7 +330,7 @@
<entry>Unknown client</entry>
<entry>unknown</entry>
<entry>not member('KNOWN')</entry>
<entry>If there is a hostreservation for the client
<entry>If there is a host reservation for the client
"false" else "true"</entry>
</row>
<row>
......@@ -585,13 +589,13 @@
built-in, i.e., beginning by &quot;VENDOR_CLASS_&quot;,
&quot;AFTER__&quot; (for the to come "after" hook) and
&quot;EXTERNAL_&quot; or equal to &quot;ALL&quot;, &quot;KNOWN&quot;,
etc.
&quot;UNKNOWN&quot;etc.
</para>
<para>"known" and "unknown" are short hands for "member('KNOWN')" and
"not member('KNOWN')". Note the evaluation of any expression using
directly or indirectly the &quot;KNOWN&quot; class is deferred
after the host reservation lookup (i.e. when the &quot;KNOWN&quot;
belonging is determined).
or &quot;UNKNOWN&quot; partition is determined).
</para></listitem>
<listitem><para>
......
......@@ -2240,7 +2240,8 @@ It is merely echoed by the server
<para>
In a similar way a pool can be constrained to serve only known clients,
i.e. clients which have a reservation, using the build-n "KNOWN" class.
i.e. clients which have a reservation, using the build-n "KNOWN" or
"UNKNOWN" classes.
One can assign addresses to registered clients without giving a
different address per reservations, for instance when there is
not enough available addresses.
......@@ -2253,8 +2254,8 @@ It is merely echoed by the server
The second step is to choose a subnet, possibly based on the
class information.
The next step is to evaluate class expressions depending on the
built-in "KNOWN" class after host reservation lookup, and to
assign classes from host reservations.
built-in "KNOWN"/"UNKNOWN" classes after host reservation lookup,
using them for pool selection and to assign classes from host reservations.
After the list of required classes is built and each class of the list
has its expression evaluated: when it returns true the packet is added
as a member of the class.
......@@ -3623,9 +3624,9 @@ It is merely echoed by the server
</screen>
<para>Static class assignments, as shown above, can be used in conjunction
with classification using expressions. The "KNOWN" builtin class is
added to the packet and any class depending on it directly or indirectly
and not only-if-required is evaluated.
with classification using expressions. The "KNOWN" or "UNKNOWN" builtin
class is added to the packet and any class depending on it directly or
indirectly and not only-if-required is evaluated.
</para>
<note>
......
......@@ -2238,7 +2238,8 @@ should include options from the isc option space:
<para>
In a similar way a pool can be constrained to serve only known clients,
i.e. clients which have a reservation, using the build-n "KNOWN" class.
i.e. clients which have a reservation, using the build-n "KNOWN" or
"UNKNOWN" classes.
One can assign addresses to registered clients without giving a
different address per reservations, for instance when there is
not enough available addresses.
......@@ -2251,8 +2252,9 @@ should include options from the isc option space:
The second step is to choose a subnet, possibly based on the
class information.
The next step is to evaluate class expressions depending on the
built-in "KNOWN" class after host reservation lookup, and to
assign classes from host reservations.
built-in "KNOWN"/"UNKNOWN" classes after host reservation lookup,
using them for pool/pd-pool selection and to assign classes from host
reservations.
After the list of required classes is built and each class of the list
has its expression evaluated: when it returns true the packet is added
as a member of the class.
......@@ -3282,9 +3284,9 @@ should include options from the isc option space:
</screen>
<para>Static class assignments, as shown above, can be used in conjunction
with classification using expressions. The "KNOWN" builtin class is
added to the packet and any class depending on it directly or indirectly
and not only-if-required is evaluated.
with classification using expressions. The "KNOWN" or "UNKNOWN" builtin
class is added to the packet and any class depending on it directly or
indirectly and not only-if-required is evaluated.
</para>
<note>
......
......@@ -160,17 +160,19 @@ Dhcpv4Exchange::Dhcpv4Exchange(const AllocEnginePtr& alloc_engine,
// Check for static reservations.
alloc_engine->findReservation(*context_);
// Set known builtin class if something was found.
if (!context_->hosts_.empty()) {
query->addClass("KNOWN");
}
}
}
// Perform second pass of classification.
Dhcpv4Srv::evaluateClasses(query, true);
// Set KNOWN builtin class if something was found, UNKNOWN if not.
if (!context_->hosts_.empty()) {
query->addClass("KNOWN");
} else {
query->addClass("UNKNOWN");
}
// Perform second pass of classification.
Dhcpv4Srv::evaluateClasses(query, true);
const ClientClasses& classes = query_->getClasses();
if (!classes.empty()) {
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_BASIC, DHCP4_CLASS_ASSIGNED)
......@@ -3197,7 +3199,7 @@ void Dhcpv4Srv::classifyPacket(const Pkt4Ptr& pkt) {
// First: built-in vendor class processing.
classifyByVendor(pkt);
// Run match expressions on classes not depending on KNOWN.
// Run match expressions on classes not depending on KNOWN/UNKNOWN.
evaluateClasses(pkt, false);
}
......
......@@ -857,8 +857,8 @@ public:
/// @note Second part of the classification.
///
/// @param pkt packet to be classified.
/// @param depend_on_known if false classes depending on the KNOWN
/// class are skipped, if true only these classes are evaluated.
/// @param depend_on_known if false classes depending on the KNOWN or
/// UNKNOWN classes are skipped, if true only these classes are evaluated.
static void evaluateClasses(const Pkt4Ptr& pkt, bool depend_on_known);
protected:
......
......@@ -2466,7 +2466,7 @@ TEST_F(Dhcpv4SrvTest, clientPoolClassify) {
EXPECT_FALSE(offer->getYiaddr().isV4Zero());
}
// Checks if the KNOWN built-in class is indeed used for pool selection.
// Checks if the [UN]KNOWN built-in classes is indeed used for pool selection.
TEST_F(Dhcpv4SrvTest, clientPoolClassifyKnown) {
IfaceMgrTestConfig test_config(true);
IfaceMgr::instance().openSockets4();
......@@ -2478,10 +2478,6 @@ TEST_F(Dhcpv4SrvTest, clientPoolClassifyKnown) {
string config = "{ \"interfaces-config\": {"
" \"interfaces\": [ \"*\" ]"
"},"
"\"client-classes\": [ {"
" \"name\": \"unknown\", "
" \"test\": \"not member('KNOWN')\" "
"} ],"
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
"\"subnet4\": [ "
......@@ -2489,7 +2485,7 @@ TEST_F(Dhcpv4SrvTest, clientPoolClassifyKnown) {
" \"pool\": \"192.0.2.1 - 192.0.2.100\", "
" \"client-class\": \"KNOWN\" }, "
" { \"pool\": \"192.0.3.1 - 192.0.3.100\", "
" \"client-class\": \"unknown\" } ], "
" \"client-class\": \"UNKNOWN\" } ], "
" \"subnet\": \"192.0.0.0/16\" } "
"],"
"\"valid-lifetime\": 4000 }";
......
......@@ -378,15 +378,17 @@ Dhcpv6Srv::initContext(const Pkt6Ptr& pkt,
// Find host reservations using specified identifiers.
alloc_engine_->findReservation(ctx);
}
// Set known builtin class if something was found.
if (!ctx.hosts_.empty()) {
pkt->addClass("KNOWN");
}
// Perform second pass of classification.
evaluateClasses(pkt, true);
// Set KNOWN builtin class if something was found, UNKNOWN if not.
if (!ctx.hosts_.empty()) {
pkt->addClass("KNOWN");
} else {
pkt->addClass("UNKNOWN");
}
// Perform second pass of classification.
evaluateClasses(pkt, true);
}
bool Dhcpv6Srv::run() {
......@@ -3272,7 +3274,7 @@ void Dhcpv6Srv::classifyPacket(const Pkt6Ptr& pkt) {
// First: built-in vendor class processing
classifyByVendor(pkt, classes);
// Run match expressions on classes not depending on KNOWN.
// Run match expressions on classes not depending on KNOWN/UNKNOWN.
evaluateClasses(pkt, false);
}
......
......@@ -679,8 +679,8 @@ protected:
/// @note Second part of the classification.
///
/// @param pkt packet to be classified.
/// @param depend_on_known if false classes depending on the KNOWN
/// class are skipped, if true only these classes are evaluated.
/// @param depend_on_known if false classes depending on the KNOWN or
/// UNKNOWN classes are skipped, if true only these classes are evaluated.
void evaluateClasses(const Pkt6Ptr& pkt, bool depend_on_known);
/// @brief Assigns classes retrieved from host reservation database.
......
......@@ -1085,7 +1085,7 @@ TEST_F(ClassifyTest, clientClassifyPool) {
EXPECT_TRUE(ia_na3->getOption(D6O_IAADDR));
}
// Checks if the KNOWN built-in class is indeed used for pool selection.
// Checks if the [UN]KNOWN built-in classes is indeed used for pool selection.
TEST_F(ClassifyTest, clientClassifyPoolKnown) {
IfaceMgrTestConfig test_config(true);
......@@ -1096,10 +1096,6 @@ TEST_F(ClassifyTest, clientClassifyPoolKnown) {
std::string config = "{ \"interfaces-config\": {"
" \"interfaces\": [ \"*\" ]"
"},"
"\"client-classes\": [ {"
" \"name\": \"unknown\", "
" \"test\": \"not member('KNOWN')\" "
"} ],"
"\"preferred-lifetime\": 3000,"
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
......@@ -1111,7 +1107,7 @@ TEST_F(ClassifyTest, clientClassifyPoolKnown) {
" }, "
" { "
" \"pool\": \"2001:db8:2::/64\", "
" \"client-class\": \"unknown\" "
" \"client-class\": \"UNKNOWN\" "
" } "
" ], "
" \"subnet\": \"2001:db8:2::/40\", "
......
......@@ -314,7 +314,7 @@ ClientClassDictionary::toElement() const {
std::list<std::string>
builtinNames = {
"ALL", "KNOWN"
"ALL", "KNOWN", "UNKNOWN"
};
std::list<std::string>
......@@ -351,8 +351,8 @@ isClientClassDefined(ClientClassDictionaryPtr& class_dictionary,
const ClientClass& client_class) {
// First check built-in classes
if (isClientClassBuiltIn(client_class)) {
// Check direct dependency on KNOWN
if (client_class == "KNOWN") {
// Check direct dependency on [UN]KNOWN
if ((client_class == "KNOWN") || (client_class == "UNKNOWN")) {
depend_on_known = true;
}
return (true);
......@@ -361,7 +361,7 @@ isClientClassDefined(ClientClassDictionaryPtr& class_dictionary,
// Second check already defined, i.e. in the dictionary
ClientClassDefPtr def = class_dictionary->findClass(client_class);
if (def) {
// Check indirect dependency on KNOWN
// Check indirect dependency on [UN]KNOWN
if (def->getDependOnKnown()) {
depend_on_known = true;
}
......
......@@ -211,7 +211,7 @@ private:
/// two other conditions stand the expression is evaluated later when
/// the host reservation membership was determined.
/// This flag is set to true during the match expression parsing if
/// direct or indirect dependency on the builtin KNOWN class is
/// direct or indirect dependency on the builtin [UN]KNOWN classes is
/// detected.
bool depend_on_known_;
......@@ -365,7 +365,7 @@ private:
typedef boost::shared_ptr<ClientClassDictionary> ClientClassDictionaryPtr;
/// @brief List of built-in client class names.
/// i.e. ALL and KNOWN.
/// i.e. ALL, KNOWN and UNKNOWN.
extern std::list<std::string> builtinNames;
/// @brief List of built-in client class prefixes
......@@ -383,7 +383,8 @@ bool isClientClassBuiltIn(const ClientClass& client_class);
/// i.e. is built-in or in the dictionary,
///
/// The reference to depend on known flag is set to true if the class
/// is KNOWN (direct dependency) or has this flag set (indirect dependency).
/// is KNOWN or UNKNOWN (direct dependency) or has this flag set
/// (indirect dependency).
///
/// @param class_dictionary A class dictionary where to look for.
/// @param depend_on_known A reference to depend on known flag.
......
......@@ -989,6 +989,10 @@ TEST_F(ClientClassDefListParserTest, dependOnKnown) {
" { \n"
" \"name\": \"delta\", \n"
" \"test\": \"member('beta') and member('gamma')\" \n"
" }, \n"
" { \n"
" \"name\": \"zeta\", \n"
" \"test\": \"not member('UNKNOWN') and member('alpha')\" \n"
" } \n"
"] \n";
......@@ -997,8 +1001,8 @@ TEST_F(ClientClassDefListParserTest, dependOnKnown) {
EXPECT_NO_THROW(dictionary = parseClientClassDefList(cfg_text, AF_INET6));
ASSERT_TRUE(dictionary);
// We should have four classes in the dictionary.
EXPECT_EQ(4, dictionary->getClasses()->size());
// We should have five classes in the dictionary.
EXPECT_EQ(5, dictionary->getClasses()->size());
// Check alpha.
ClientClassDefPtr cclass;
......@@ -1024,6 +1028,13 @@ TEST_F(ClientClassDefListParserTest, dependOnKnown) {
ASSERT_TRUE(cclass);
EXPECT_EQ("delta", cclass->getName());
EXPECT_TRUE(cclass->getDependOnKnown());
// Check that zeta which directly depends on UNKNOWN.
// (and yes I know that I skipped epsilon)
ASSERT_NO_THROW(cclass = dictionary->findClass("zeta"));
ASSERT_TRUE(cclass);
EXPECT_EQ("zeta", cclass->getName());
EXPECT_TRUE(cclass->getDependOnKnown());
}
} // end of anonymous namespace
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