From: Francis Dupont Date: Thu, 7 Jun 2018 11:59:55 +0000 (+0200) Subject: [5549a] Code, examples and doc updated X-Git-Tag: 136-add-global-host-reservation-examples_base~4^2~2^2~2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=116962ddc50c480172e51dace22baf06ed2be28e;p=thirdparty%2Fkea.git [5549a] Code, examples and doc updated --- diff --git a/doc/examples/kea4/advanced.json b/doc/examples/kea4/advanced.json index 894b0489ab..6065a68d3e 100644 --- a/doc/examples/kea4/advanced.json +++ b/doc/examples/kea4/advanced.json @@ -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", diff --git a/doc/examples/kea6/advanced.json b/doc/examples/kea6/advanced.json index aa587de7ed..9705c6e1a7 100644 --- a/doc/examples/kea6/advanced.json +++ b/doc/examples/kea6/advanced.json @@ -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", diff --git a/doc/guide/classify.xml b/doc/guide/classify.xml index 23efe97e03..e61229deb6 100644 --- a/doc/guide/classify.xml +++ b/doc/guide/classify.xml @@ -78,10 +78,11 @@ 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. 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. - 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. 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. If needed, addresses and prefixes from pools are assigned, @@ -189,11 +191,12 @@ begin with all capital letters. - 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. + 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. @@ -236,11 +239,12 @@ - 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. @@ -326,7 +330,7 @@ Unknown client unknown not member('KNOWN') - If there is a hostreservation for the client + If there is a host reservation for the client "false" else "true" @@ -585,13 +589,13 @@ 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. "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). diff --git a/doc/guide/dhcp4-srv.xml b/doc/guide/dhcp4-srv.xml index ff3b15cb51..c680e95861 100644 --- a/doc/guide/dhcp4-srv.xml +++ b/doc/guide/dhcp4-srv.xml @@ -2240,7 +2240,8 @@ It is merely echoed by the server 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 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. diff --git a/doc/guide/dhcp6-srv.xml b/doc/guide/dhcp6-srv.xml index 76ceec3d1d..731d48dd6c 100644 --- a/doc/guide/dhcp6-srv.xml +++ b/doc/guide/dhcp6-srv.xml @@ -2238,7 +2238,8 @@ should include options from the isc option space: 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: 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. diff --git a/src/bin/dhcp4/dhcp4_srv.cc b/src/bin/dhcp4/dhcp4_srv.cc index cd2aba14be..0c67f0d603 100644 --- a/src/bin/dhcp4/dhcp4_srv.cc +++ b/src/bin/dhcp4/dhcp4_srv.cc @@ -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); } diff --git a/src/bin/dhcp4/dhcp4_srv.h b/src/bin/dhcp4/dhcp4_srv.h index 03d7b79056..1ea0e28b09 100644 --- a/src/bin/dhcp4/dhcp4_srv.h +++ b/src/bin/dhcp4/dhcp4_srv.h @@ -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: diff --git a/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc b/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc index 7a31d989ce..b41561471c 100644 --- a/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc +++ b/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc @@ -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 }"; diff --git a/src/bin/dhcp6/dhcp6_srv.cc b/src/bin/dhcp6/dhcp6_srv.cc index f193007985..79f6f836e6 100644 --- a/src/bin/dhcp6/dhcp6_srv.cc +++ b/src/bin/dhcp6/dhcp6_srv.cc @@ -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); } diff --git a/src/bin/dhcp6/dhcp6_srv.h b/src/bin/dhcp6/dhcp6_srv.h index bb7cfac474..9bd50cfbf0 100644 --- a/src/bin/dhcp6/dhcp6_srv.h +++ b/src/bin/dhcp6/dhcp6_srv.h @@ -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. diff --git a/src/bin/dhcp6/tests/classify_unittests.cc b/src/bin/dhcp6/tests/classify_unittests.cc index 771329f829..b8b2254d97 100644 --- a/src/bin/dhcp6/tests/classify_unittests.cc +++ b/src/bin/dhcp6/tests/classify_unittests.cc @@ -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\", " diff --git a/src/lib/dhcpsrv/client_class_def.cc b/src/lib/dhcpsrv/client_class_def.cc index 95acbbadac..df70c4b88f 100644 --- a/src/lib/dhcpsrv/client_class_def.cc +++ b/src/lib/dhcpsrv/client_class_def.cc @@ -314,7 +314,7 @@ ClientClassDictionary::toElement() const { std::list builtinNames = { - "ALL", "KNOWN" + "ALL", "KNOWN", "UNKNOWN" }; std::list @@ -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; } diff --git a/src/lib/dhcpsrv/client_class_def.h b/src/lib/dhcpsrv/client_class_def.h index 9a1cbd0ab8..70f3459ba7 100644 --- a/src/lib/dhcpsrv/client_class_def.h +++ b/src/lib/dhcpsrv/client_class_def.h @@ -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 ClientClassDictionaryPtr; /// @brief List of built-in client class names. -/// i.e. ALL and KNOWN. +/// i.e. ALL, KNOWN and UNKNOWN. extern std::list 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. diff --git a/src/lib/dhcpsrv/tests/client_class_def_parser_unittest.cc b/src/lib/dhcpsrv/tests/client_class_def_parser_unittest.cc index a1b1989aac..83324806fa 100644 --- a/src/lib/dhcpsrv/tests/client_class_def_parser_unittest.cc +++ b/src/lib/dhcpsrv/tests/client_class_def_parser_unittest.cc @@ -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