"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.
{
// 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",
"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.
}
},
{
- // 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",
</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
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,
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>
</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>
<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>
built-in, i.e., beginning by "VENDOR_CLASS_",
"AFTER__" (for the to come "after" hook) and
"EXTERNAL_" or equal to "ALL", "KNOWN",
- etc.
+ "UNKNOWN"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 "KNOWN" class is deferred
after the host reservation lookup (i.e. when the "KNOWN"
- belonging is determined).
+ or "UNKNOWN" partition is determined).
</para></listitem>
<listitem><para>
<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.
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.
</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>
<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.
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.
</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>
// 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)
// 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);
}
/// @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:
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();
string config = "{ \"interfaces-config\": {"
" \"interfaces\": [ \"*\" ]"
"},"
- "\"client-classes\": [ {"
- " \"name\": \"unknown\", "
- " \"test\": \"not member('KNOWN')\" "
- "} ],"
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
"\"subnet4\": [ "
" \"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 }";
// 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() {
// 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);
}
/// @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.
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);
std::string config = "{ \"interfaces-config\": {"
" \"interfaces\": [ \"*\" ]"
"},"
- "\"client-classes\": [ {"
- " \"name\": \"unknown\", "
- " \"test\": \"not member('KNOWN')\" "
- "} ],"
"\"preferred-lifetime\": 3000,"
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
" }, "
" { "
" \"pool\": \"2001:db8:2::/64\", "
- " \"client-class\": \"unknown\" "
+ " \"client-class\": \"UNKNOWN\" "
" } "
" ], "
" \"subnet\": \"2001:db8:2::/40\", "
std::list<std::string>
builtinNames = {
- "ALL", "KNOWN"
+ "ALL", "KNOWN", "UNKNOWN"
};
std::list<std::string>
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);
// 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;
}
/// 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_;
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
/// 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.
" { \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";
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;
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