]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[5549a] Code, examples and doc updated
authorFrancis Dupont <fdupont@isc.org>
Thu, 7 Jun 2018 11:59:55 +0000 (13:59 +0200)
committerFrancis Dupont <fdupont@isc.org>
Thu, 7 Jun 2018 11:59:55 +0000 (13:59 +0200)
14 files changed:
doc/examples/kea4/advanced.json
doc/examples/kea6/advanced.json
doc/guide/classify.xml
doc/guide/dhcp4-srv.xml
doc/guide/dhcp6-srv.xml
src/bin/dhcp4/dhcp4_srv.cc
src/bin/dhcp4/dhcp4_srv.h
src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
src/bin/dhcp6/dhcp6_srv.cc
src/bin/dhcp6/dhcp6_srv.h
src/bin/dhcp6/tests/classify_unittests.cc
src/lib/dhcpsrv/client_class_def.cc
src/lib/dhcpsrv/client_class_def.h
src/lib/dhcpsrv/tests/client_class_def_parser_unittest.cc

index 894b0489ab3979bfdf5aab8a926e798fc727656b..6065a68d3e304101b612d90c515c0e7d6018edf5 100644 (file)
         "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",
index aa587de7edfdb39300be88493eb2ea00abb0c963..9705c6e1a76c2c61be22abfc09cc8c8c8ca381dd 100644 (file)
         "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",
index 23efe97e0315911f419c13ccf5c16f86c98f68fe..e61229deb629eb12826e0df1ff3d56169f7ecbc3 100644 (file)
       </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 &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>
index ff3b15cb5119ad788804508b40d26baf63bf3a71..c680e95861f65bf085753fc3b80ce93d65f4c6c9 100644 (file)
@@ -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>
index 76ceec3d1da2cee3347bd78ce7a7b84d00856d21..731d48dd6ce3f64bfca988256e4ae282315c0764 100644 (file)
@@ -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>
index cd2aba14bed96256c387f2c5304aa87272efc43d..0c67f0d603555dc84195bcd02ef91d1a089311ea 100644 (file)
@@ -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);
 }
 
index 03d7b7905603914f31e9f2a9190bacfa917583ec..1ea0e28b09442786584e62267a0af340650cc4e9 100644 (file)
@@ -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:
index 7a31d989ce04c86cd14bd2cd820b2d18b5c8631d..b41561471c8aa6c8e22d4667f74522880adbec90 100644 (file)
@@ -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 }";
index f19300798584297ef65a254711186ab328f59cc0..79f6f836e6feeb36e1f4545bff897483fd56dffc 100644 (file)
@@ -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);
 }
 
index bb7cfac47468e48008d84ee2c696127f0245f3d8..9bd50cfbf06e18b0a9c3d3eb40096e50a0b2820f 100644 (file)
@@ -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.
index 771329f829e28500df870f94b39665166dfa5b1c..b8b2254d970351495859839f856d3db22ba36174 100644 (file)
@@ -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\", "
index 95acbbadace57001b3de6d04627ad553351b532d..df70c4b88f03e67d3cac15c1d19b5917e45b2208 100644 (file)
@@ -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;
         }
index 9a1cbd0ab8d323f1e99567177f401a0e5f24d21c..70f3459ba782182dd7c6251066ecf6d1c0d3a270 100644 (file)
@@ -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.
index a1b1989aacb1503175537bcf3c1e8273714b34b5..83324806fa817266a9496b35d7facc71e8f200d3 100644 (file)
@@ -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