diff --git a/ChangeLog b/ChangeLog index b3956c5ebe36abc199efa453e735e3dd1abe4142..7db72f495b6265e7ab4d1f1dbce0afd14a1b7f6f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +1621. [func] fdupont + Both kea-dhcp4 and kea-dhcp6 now support a special class, 'DROP'. + When the class is defined, inbound client packets that match the + class's match expression will be dropped without further processing. + Each such drop is logged at DEBUG level and accounted for in + drop statistics. + (Gitlab #606,!375, git bfa5b2c50324e9d2339daa8309774f49a5e7bf3c) + 1620. [func] franek, razvan Kea statistics improvements: Support for storing more than one sample. diff --git a/doc/guide/classify.xml b/doc/guide/classify.xml index 4755913c89ce7e7960e20c7bd06bd11101f984cd..83f51fdd2eb36bd8900a9f8cc9245d42481df482 100644 --- a/doc/guide/classify.xml +++ b/doc/guide/classify.xml @@ -90,6 +90,11 @@ resort) definition. + When the incoming packet belongs the special class, "DROP", it is + dropped and an informational message is logged with the packet + information. + + A subnet is chosen, possibly based on the class information when some subnets are reserved. More precisely: when choosing a subnet, the server iterates over all of the subnets that are @@ -111,10 +116,11 @@ request") evaluation - are processed in the order they are defined in the configuration; the boolean expression is evaluated and, if it returns true ("match"), the incoming packet is associated - with the class. After a subnet is selected, the server determines whether there is a reservation - for a given client. Therefore, it + with the class. After a subnet is selected, the server determines + whether there is a reservation for a given client. Therefore, it is not possible to use KNOWN/UNKNOWN classes to select a shared - network or a subnet. + network or a subnet, nor to make the DROP class dependent of + KNOWN/UNKNOWN classes. If needed, addresses and prefixes from pools are assigned, diff --git a/doc/guide/dhcp4-srv.xml b/doc/guide/dhcp4-srv.xml index 1e9b5384823436a31f60df3176efbc16fbe6a1ac..880e1e621a414acceb2be9f77a1d9a1cb3653494 100644 --- a/doc/guide/dhcp4-srv.xml +++ b/doc/guide/dhcp4-srv.xml @@ -2534,7 +2534,8 @@ It is merely echoed by the server. The first step is to assess an incoming packet and assign it to zero or more classes. The second step is to choose a subnet, possibly based on the - class information. + class information. When the incoming packet is in the special + class, "DROP", it is dropped and a debug message logged. The next step is to evaluate class expressions depending on the built-in "KNOWN"/"UNKNOWN" classes after host reservation lookup, using them for pool selection and assigning classes from host reservations. diff --git a/doc/guide/dhcp6-srv.xml b/doc/guide/dhcp6-srv.xml index c17bd8859110112b3a8587cbf42a2570238fc7a8..538ed188e694f6407cdea1f9dadfea3833a77707 100644 --- a/doc/guide/dhcp6-srv.xml +++ b/doc/guide/dhcp6-srv.xml @@ -2429,7 +2429,8 @@ should include options from the new option space: The first step is to assess an incoming packet and assign it to zero or more classes. Next, a subnet is chosen, possibly based on the - class information. + class information. When the incoming packet is in the special + class, "DROP", it is dropped and a debug message logged. After that, class expressions are evaluated depending on the built-in "KNOWN"/"UNKNOWN" classes after host reservation lookup, using them for pool/pd-pool selection and assigning classes from host diff --git a/src/bin/dhcp4/dhcp4_messages.cc b/src/bin/dhcp4/dhcp4_messages.cc index 44c0f7538a01b25a6939a6b73e565f7f04c68516..25604b128a30891fe1ddb6fa533f264f4f00936e 100644 --- a/src/bin/dhcp4/dhcp4_messages.cc +++ b/src/bin/dhcp4/dhcp4_messages.cc @@ -1,4 +1,4 @@ -// File created from ../../../src/bin/dhcp4/dhcp4_messages.mes on Thu Jun 27 2019 19:56 +// File created from ../../../src/bin/dhcp4/dhcp4_messages.mes on Tue Jul 16 2019 11:03 #include #include @@ -93,6 +93,7 @@ extern const isc::log::MessageID DHCP4_PACKET_DROP_0006 = "DHCP4_PACKET_DROP_000 extern const isc::log::MessageID DHCP4_PACKET_DROP_0007 = "DHCP4_PACKET_DROP_0007"; extern const isc::log::MessageID DHCP4_PACKET_DROP_0008 = "DHCP4_PACKET_DROP_0008"; extern const isc::log::MessageID DHCP4_PACKET_DROP_0009 = "DHCP4_PACKET_DROP_0009"; +extern const isc::log::MessageID DHCP4_PACKET_DROP_0010 = "DHCP4_PACKET_DROP_0010"; extern const isc::log::MessageID DHCP4_PACKET_NAK_0001 = "DHCP4_PACKET_NAK_0001"; extern const isc::log::MessageID DHCP4_PACKET_NAK_0002 = "DHCP4_PACKET_NAK_0002"; extern const isc::log::MessageID DHCP4_PACKET_NAK_0003 = "DHCP4_PACKET_NAK_0003"; @@ -228,6 +229,7 @@ const char* values[] = { "DHCP4_PACKET_DROP_0007", "%1: failed to process packet: %2", "DHCP4_PACKET_DROP_0008", "%1: DHCP service is globally disabled", "DHCP4_PACKET_DROP_0009", "%1: Option 53 missing (no DHCP message type), is this a BOOTP packet?", + "DHCP4_PACKET_DROP_0010", "dropped as member of the special class 'DROP': %1", "DHCP4_PACKET_NAK_0001", "%1: failed to select a subnet for incoming packet, src %2, type %3", "DHCP4_PACKET_NAK_0002", "%1: invalid address %2 requested by INIT-REBOOT", "DHCP4_PACKET_NAK_0003", "%1: failed to advertise a lease, client sent ciaddr %2, requested-ip-address %3", diff --git a/src/bin/dhcp4/dhcp4_messages.h b/src/bin/dhcp4/dhcp4_messages.h index b8c7ff185c1f236eaf1d4ba8218fa6aea7181699..e749541dfa92ccb400d598182c146e7825e0e09e 100644 --- a/src/bin/dhcp4/dhcp4_messages.h +++ b/src/bin/dhcp4/dhcp4_messages.h @@ -1,4 +1,4 @@ -// File created from ../../../src/bin/dhcp4/dhcp4_messages.mes on Thu Jun 27 2019 19:56 +// File created from ../../../src/bin/dhcp4/dhcp4_messages.mes on Tue Jul 16 2019 11:03 #ifndef DHCP4_MESSAGES_H #define DHCP4_MESSAGES_H @@ -94,6 +94,7 @@ extern const isc::log::MessageID DHCP4_PACKET_DROP_0006; extern const isc::log::MessageID DHCP4_PACKET_DROP_0007; extern const isc::log::MessageID DHCP4_PACKET_DROP_0008; extern const isc::log::MessageID DHCP4_PACKET_DROP_0009; +extern const isc::log::MessageID DHCP4_PACKET_DROP_0010; extern const isc::log::MessageID DHCP4_PACKET_NAK_0001; extern const isc::log::MessageID DHCP4_PACKET_NAK_0002; extern const isc::log::MessageID DHCP4_PACKET_NAK_0003; diff --git a/src/bin/dhcp4/dhcp4_messages.mes b/src/bin/dhcp4/dhcp4_messages.mes index 40a02f109b8208299b560097fd247ab258397c62..b530ad191fa20a84696e86a3cb476e6e14af8cbe 100644 --- a/src/bin/dhcp4/dhcp4_messages.mes +++ b/src/bin/dhcp4/dhcp4_messages.mes @@ -509,6 +509,10 @@ This debug message is issued when a packet is dropped because it did contain option 53 and thus has no DHCP message type. The most likely explanation is that it was BOOTP packet. +% DHCP4_PACKET_DROP_0010 dropped as member of the special class 'DROP': %1 +This debug message is emitted when an incoming packet was classified +into the special class 'DROP' and dropped. The packet details are displayed. + % DHCP4_PACKET_NAK_0001 %1: failed to select a subnet for incoming packet, src %2, type %3 This error message is output when a packet was received from a subnet for which the DHCPv4 server has not been configured. The most probable diff --git a/src/bin/dhcp4/dhcp4_srv.cc b/src/bin/dhcp4/dhcp4_srv.cc index d434691c732c5a7dc98d4e414e11c552c14bb0f1..c406508c7a0bda58ef2c9c873fc0510d3c7c2eb4 100644 --- a/src/bin/dhcp4/dhcp4_srv.cc +++ b/src/bin/dhcp4/dhcp4_srv.cc @@ -1006,6 +1006,15 @@ Dhcpv4Srv::processPacket(Pkt4Ptr& query, Pkt4Ptr& rsp, bool allow_packet_park) { callout_handle->getArgument("query4", query); } + // Check the DROP special class. + if (query->inClass("DROP")) { + LOG_DEBUG(packet4_logger, DBGLVL_TRACE_BASIC, DHCP4_PACKET_DROP_0010) + .arg(query->toText()); + isc::stats::StatsMgr::instance().addValue("pkt4-receive-drop", + static_cast(1)); + return; + } + AllocEngine::ClientContext4Ptr ctx; try { diff --git a/src/bin/dhcp4/tests/classify_unittest.cc b/src/bin/dhcp4/tests/classify_unittest.cc index 713fd8f4dfb4fc1325aec5920d6bf63bbd9447bd..644b42702079d4fa93fde30619326b8bd9e06062 100644 --- a/src/bin/dhcp4/tests/classify_unittest.cc +++ b/src/bin/dhcp4/tests/classify_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2016-2018 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2016-2019 Internet Systems Consortium, Inc. ("ISC") // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -75,6 +76,12 @@ namespace { /// option[93].hex == 0x0009 aka telephones /// option[93].hex == 0x0007 aka computers /// +/// - Configuration 5: +/// - Used for the DROP class +/// - 1 subnet: 10.0.0.0/24 +/// - 1 pool: 10.0.0.10-10.0.0.100 +/// - the following class defined: option[93].hex == 0x0009, DROP +/// const char* CONFIGS[] = { // Configuration 0 "{ \"interfaces-config\": {" @@ -275,6 +282,22 @@ const char* CONFIGS[] = { " } ]" "}", + // Configuration 5 + "{ \"interfaces-config\": {" + " \"interfaces\": [ \"*\" ]" + "}," + "\"valid-lifetime\": 600," + "\"client-classes\": [" + "{" + " \"name\": \"DROP\"," + " \"test\": \"option[93].hex == 0x0009\"" + "}]," + "\"subnet4\": [ { " + " \"subnet\": \"10.0.0.0/24\", " + " \"id\": 1," + " \"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ]" + " } ]" + "}" }; /// @brief Test fixture class for testing classification. @@ -1107,4 +1130,39 @@ TEST_F(ClassifyTest, precedenceNetwork) { EXPECT_EQ("10.0.0.3", addrs[0].toText()); } +// This test checks the handling for the DROP special class. +TEST_F(ClassifyTest, dropClass) { + Dhcp4Client client(Dhcp4Client::SELECTING); + + // Configure DHCP server. + configure(CONFIGS[5], *client.getServer()); + + // Send the discover. + client.doDiscover(); + + // No option: no drop. + EXPECT_TRUE(client.getContext().response_); + + // Retry with an option matching the DROP class. + Dhcp4Client client2(Dhcp4Client::SELECTING); + + // Add the pxe option. + OptionPtr pxe(new OptionInt(Option::V4, 93, 0x0009)); + client2.addExtraOption(pxe); + + // Send the discover. + client2.doDiscover(); + + // Option, dropped. + EXPECT_FALSE(client2.getContext().response_); + + // There should also be pkt4-receive-drop stat bumped up. + stats::StatsMgr& mgr = stats::StatsMgr::instance(); + stats::ObservationPtr drop_stat = mgr.getObservation("pkt4-receive-drop"); + + // This statistic must be present and must be set to 1. + ASSERT_TRUE(drop_stat); + EXPECT_EQ(1, drop_stat->getInteger().first); +} + } // end of anonymous namespace diff --git a/src/bin/dhcp6/dhcp6_messages.cc b/src/bin/dhcp6/dhcp6_messages.cc index eaab2c015f3ffd11b1acd69d80f40e45d2403c5b..d4e7fc7c72c416c7d1b29219d4628f11215d721c 100644 --- a/src/bin/dhcp6/dhcp6_messages.cc +++ b/src/bin/dhcp6/dhcp6_messages.cc @@ -1,4 +1,4 @@ -// File created from ../../../src/bin/dhcp6/dhcp6_messages.mes on Thu Jun 27 2019 20:26 +// File created from ../../../src/bin/dhcp6/dhcp6_messages.mes on Tue Jul 16 2019 11:03 #include #include @@ -88,6 +88,7 @@ extern const isc::log::MessageID DHCP6_NO_SOCKETS_OPEN = "DHCP6_NO_SOCKETS_OPEN" extern const isc::log::MessageID DHCP6_OPEN_SOCKET = "DHCP6_OPEN_SOCKET"; extern const isc::log::MessageID DHCP6_OPEN_SOCKET_FAIL = "DHCP6_OPEN_SOCKET_FAIL"; extern const isc::log::MessageID DHCP6_PACKET_DROP_DHCP_DISABLED = "DHCP6_PACKET_DROP_DHCP_DISABLED"; +extern const isc::log::MessageID DHCP6_PACKET_DROP_DROP_CLASS = "DHCP6_PACKET_DROP_DROP_CLASS"; extern const isc::log::MessageID DHCP6_PACKET_DROP_PARSE_FAIL = "DHCP6_PACKET_DROP_PARSE_FAIL"; extern const isc::log::MessageID DHCP6_PACKET_DROP_SERVERID_MISMATCH = "DHCP6_PACKET_DROP_SERVERID_MISMATCH"; extern const isc::log::MessageID DHCP6_PACKET_DROP_UNICAST = "DHCP6_PACKET_DROP_UNICAST"; @@ -230,6 +231,7 @@ const char* values[] = { "DHCP6_OPEN_SOCKET", "opening service sockets on port %1", "DHCP6_OPEN_SOCKET_FAIL", "failed to open socket: %1", "DHCP6_PACKET_DROP_DHCP_DISABLED", "%1: DHCP service is globally disabled", + "DHCP6_PACKET_DROP_DROP_CLASS", "dropped as member of the special class 'DROP': %1", "DHCP6_PACKET_DROP_PARSE_FAIL", "failed to parse packet from %1 to %2, received over interface %3, reason: %4", "DHCP6_PACKET_DROP_SERVERID_MISMATCH", "%1: dropping packet with server identifier: %2, server is using: %3", "DHCP6_PACKET_DROP_UNICAST", "%1: dropping unicast %2 packet as this packet should be sent to multicast", diff --git a/src/bin/dhcp6/dhcp6_messages.h b/src/bin/dhcp6/dhcp6_messages.h index cc328c9d6a798add3a652a5bfa3e94506d65b1e8..ccb62f0f7ff783eaaf812d332a269b3064c9fed2 100644 --- a/src/bin/dhcp6/dhcp6_messages.h +++ b/src/bin/dhcp6/dhcp6_messages.h @@ -1,4 +1,4 @@ -// File created from ../../../src/bin/dhcp6/dhcp6_messages.mes on Thu Jun 27 2019 20:26 +// File created from ../../../src/bin/dhcp6/dhcp6_messages.mes on Tue Jul 16 2019 11:03 #ifndef DHCP6_MESSAGES_H #define DHCP6_MESSAGES_H @@ -89,6 +89,7 @@ extern const isc::log::MessageID DHCP6_NO_SOCKETS_OPEN; extern const isc::log::MessageID DHCP6_OPEN_SOCKET; extern const isc::log::MessageID DHCP6_OPEN_SOCKET_FAIL; extern const isc::log::MessageID DHCP6_PACKET_DROP_DHCP_DISABLED; +extern const isc::log::MessageID DHCP6_PACKET_DROP_DROP_CLASS; extern const isc::log::MessageID DHCP6_PACKET_DROP_PARSE_FAIL; extern const isc::log::MessageID DHCP6_PACKET_DROP_SERVERID_MISMATCH; extern const isc::log::MessageID DHCP6_PACKET_DROP_UNICAST; diff --git a/src/bin/dhcp6/dhcp6_messages.mes b/src/bin/dhcp6/dhcp6_messages.mes index 0ba507f94b09a6a1f81f70f0eb8c779307ab3568..c980593f617bee4e2c2b942d87b89c9e57a05304 100644 --- a/src/bin/dhcp6/dhcp6_messages.mes +++ b/src/bin/dhcp6/dhcp6_messages.mes @@ -505,6 +505,10 @@ has been temporarily disabled. This affects all received DHCP packets. The service may be enabled by the "dhcp-enable" control command or automatically after a specified amount of time since receiving "dhcp-disable" command. +% DHCP6_PACKET_DROP_DROP_CLASS dropped as member of the special class 'DROP': %1 +This debug message is emitted when an incoming packet was classified +into the special class 'DROP' and dropped. The packet details are displayed. + % DHCP6_PACKET_DROP_PARSE_FAIL failed to parse packet from %1 to %2, received over interface %3, reason: %4 The DHCPv6 server has received a packet that it is unable to interpret. The reason why the packet is invalid is included in the message. diff --git a/src/bin/dhcp6/dhcp6_srv.cc b/src/bin/dhcp6/dhcp6_srv.cc index 64bb286dd0d94c8770e451b22fdb99e0d406eeb6..c07ab6f311d97fb3eb054bd868e2ee4b1aaa0167 100644 --- a/src/bin/dhcp6/dhcp6_srv.cc +++ b/src/bin/dhcp6/dhcp6_srv.cc @@ -683,6 +683,15 @@ Dhcpv6Srv::processPacket(Pkt6Ptr& query, Pkt6Ptr& rsp) { return; } + // Check the DROP special class. + if (query->inClass("DROP")) { + LOG_DEBUG(packet6_logger, DBG_DHCP6_BASIC, DHCP6_PACKET_DROP_DROP_CLASS) + .arg(query->toText()); + StatsMgr::instance().addValue("pkt6-receive-drop", + static_cast(1)); + return; + } + if (query->getType() == DHCPV6_DHCPV4_QUERY) { // This call never throws. Should this change, this section must be // enclosed in try-catch. diff --git a/src/bin/dhcp6/tests/classify_unittests.cc b/src/bin/dhcp6/tests/classify_unittests.cc index 9620874853349d116198018c320e05c1eebae131..b19372a16d8c3d42e0619dfaa01516cce0bde149 100644 --- a/src/bin/dhcp6/tests/classify_unittests.cc +++ b/src/bin/dhcp6/tests/classify_unittests.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2016-2018 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2016-2019 Internet Systems Consortium, Inc. ("ISC") // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -67,6 +68,12 @@ namespace { /// option 1234 'foo' aka telephones /// option 1234 'bar' aka computers /// +/// - Configuration 3: +/// - Used for the DROP class +/// - 1 subnet: 2001:db8:1::/48 +/// - 2 pool: 2001:db8:1:1::/64 +/// - the following class defined: option 1234 'foo', DROP +/// const char* CONFIGS[] = { // Configuration 0 "{ \"interfaces-config\": {" @@ -249,8 +256,38 @@ const char* CONFIGS[] = { " \"prefix-len\": 48, \"delegated-len\": 64," " \"client-class\": \"server2_and_computers\" } ]" " } ]," - "\"valid-lifetime\": 4000 }" + "\"valid-lifetime\": 4000 }", + // Configuration 3 + "{ \"interfaces-config\": {" + " \"interfaces\": [ \"*\" ]" + "}," + "\"preferred-lifetime\": 3000," + "\"rebind-timer\": 2000, " + "\"renew-timer\": 1000, " + "\"option-def\": [ " + "{" + " \"name\": \"host-name\"," + " \"code\": 1234," + " \"type\": \"string\"" + "}," + "{" + " \"name\": \"ipv6-forwarding\"," + " \"code\": 2345," + " \"type\": \"boolean\"" + "} ]," + "\"client-classes\": [" + "{" + " \"name\": \"DROP\"," + " \"test\": \"option[host-name].text == 'foo'\"" + "}" + "]," + "\"subnet6\": [ " + "{ \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ], " + " \"subnet\": \"2001:db8:1::/48\", " + " \"interface\": \"eth1\"" + " } ]," + "\"valid-lifetime\": 4000 }" }; /// @brief Test fixture class for testing client classification by the @@ -2042,4 +2079,43 @@ TEST_F(ClassifyTest, pDserver2Computer) { EXPECT_EQ("2001:db8:4::", lease_client.addr_.toText()); } +// This test checks the handling for the DROP special class. +TEST_F(ClassifyTest, dropClass) { + Dhcp6Client client; + client.setDUID("01:02:03:05"); + client.setInterface("eth1"); + client.requestAddress(); + + // Configure DHCP server. + ASSERT_NO_THROW(configure(CONFIGS[3], *client.getServer())); + + // Send a message to the server. + ASSERT_NO_THROW(client.doSolicit(true)); + + // No option: no drop. + EXPECT_TRUE(client.getContext().response_); + + // Retry with an option matching the DROP class. + Dhcp6Client client2; + + // Add the host-name option. + OptionStringPtr hostname(new OptionString(Option::V6, 1234, "foo")); + ASSERT_TRUE(hostname); + client2.addExtraOption(hostname); + + // Send a message to the server. + ASSERT_NO_THROW(client2.doSolicit(true)); + + // Option, dropped. + EXPECT_FALSE(client2.getContext().response_); + + // There should also be pkt6-receive-drop stat bumped up. + stats::StatsMgr& mgr = stats::StatsMgr::instance(); + stats::ObservationPtr drop_stat = mgr.getObservation("pkt6-receive-drop"); + + // This statistic must be present and must be set to 1. + ASSERT_TRUE(drop_stat); + EXPECT_EQ(1, drop_stat->getInteger().first); +} + } // end of anonymous namespace diff --git a/src/lib/dhcpsrv/client_class_def.cc b/src/lib/dhcpsrv/client_class_def.cc index 2a9a52098a74e949a5f29e96b2f6dd340cb0e656..bb18d8c328584c5aa42163fee8ce23e77b095412 100644 --- a/src/lib/dhcpsrv/client_class_def.cc +++ b/src/lib/dhcpsrv/client_class_def.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2015-2018 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2015-2019 Internet Systems Consortium, Inc. ("ISC") // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -341,6 +341,10 @@ ClientClassDictionary::toElement() const { std::list builtinNames = { + // DROP is not in this list because it is special but not built-in. + // In fact DROP is set from an expression as callouts can drop + // directly the incoming packet. The expression must not depend on + // KNOWN/UNKNOWN which are set far after the drop point. "ALL", "KNOWN", "UNKNOWN" }; diff --git a/src/lib/dhcpsrv/client_class_def.h b/src/lib/dhcpsrv/client_class_def.h index 94e03c2c56af907fc173b296c8bbae13c804ba02..88425d0a97b69aca149e47571fcaf7506e682532 100644 --- a/src/lib/dhcpsrv/client_class_def.h +++ b/src/lib/dhcpsrv/client_class_def.h @@ -1,4 +1,4 @@ -// Copyright (C) 2015-2018 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2015-2019 Internet Systems Consortium, Inc. ("ISC") // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -382,7 +382,7 @@ private: typedef boost::shared_ptr ClientClassDictionaryPtr; /// @brief List of built-in client class names. -/// i.e. ALL, KNOWN and UNKNOWN. +/// i.e. ALL, KNOWN and UNKNOWN but not DROP. extern std::list builtinNames; /// @brief List of built-in client class prefixes diff --git a/src/lib/dhcpsrv/parsers/client_class_def_parser.cc b/src/lib/dhcpsrv/parsers/client_class_def_parser.cc index 7177a4b7c6dc5fbcc94d8ce5640d697d70f9d510..4be5e85dcf99c07b962894b6b8ac2c845c0b0ee4 100644 --- a/src/lib/dhcpsrv/parsers/client_class_def_parser.cc +++ b/src/lib/dhcpsrv/parsers/client_class_def_parser.cc @@ -203,6 +203,32 @@ ClientClassDefParser::parse(ClientClassDictionaryPtr& class_dictionary, } + // Sanity checks on built-in classes + for (auto bn : builtinNames) { + if (name == bn) { + if (required) { + isc_throw(DhcpConfigError, "built-in class '" << name + << "' only-if-required flag must be false"); + } + if (!test.empty()) { + isc_throw(DhcpConfigError, "built-in class '" << name + << "' test expression must be empty"); + } + } + } + + // Sanity checks on DROP + if (name == "DROP") { + if (required) { + isc_throw(DhcpConfigError, "special class '" << name + << "' only-if-required flag must be false"); + } + if (depend_on_known) { + isc_throw(DhcpConfigError, "special class '" << name + << "' must not depend on 'KNOWN'/'UNKNOWN' classes"); + } + } + // Add the client class definition try { class_dictionary->addClass(name, match_expr, test, required, 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 0f249311500b9546587458a6c3855f96183c0dd0..813d46d75aee3fa76939fe634ac5b50d73caffd1 100644 --- a/src/lib/dhcpsrv/tests/client_class_def_parser_unittest.cc +++ b/src/lib/dhcpsrv/tests/client_class_def_parser_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2015-2018 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2015-2019 Internet Systems Consortium, Inc. ("ISC") // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -1185,4 +1185,110 @@ TEST_F(ClientClassDefListParserTest, dependOnKnown) { EXPECT_TRUE(cclass->getDependOnKnown()); } +// Verifies that a built-in class can't be required or evaluated. +TEST_F(ClientClassDefListParserTest, builtinCheckError) { + std::string cfg_text = + "[ \n" + " { \n" + " \"name\": \"ALL\" \n" + " } \n" + "] \n"; + + EXPECT_NO_THROW(parseClientClassDefList(cfg_text, AF_INET6)); + + cfg_text = + "[ \n" + " { \n" + " \"name\": \"ALL\", \n" + " \"only-if-required\": true \n" + " } \n" + "] \n"; + + EXPECT_THROW(parseClientClassDefList(cfg_text, AF_INET), DhcpConfigError); + + cfg_text = + "[ \n" + " { \n" + " \"name\": \"ALL\", \n" + " \"test\": \"'aa' == 'aa'\" \n" + " } \n" + "] \n"; + + EXPECT_THROW(parseClientClassDefList(cfg_text, AF_INET6), DhcpConfigError); + + cfg_text = + "[ \n" + " { \n" + " \"name\": \"KNOWN\", \n" + " \"only-if-required\": true \n" + " } \n" + "] \n"; + + EXPECT_THROW(parseClientClassDefList(cfg_text, AF_INET), DhcpConfigError); + + cfg_text = + "[ \n" + " { \n" + " \"name\": \"KNOWN\", \n" + " \"test\": \"'aa' == 'aa'\" \n" + " } \n" + "] \n"; + + EXPECT_THROW(parseClientClassDefList(cfg_text, AF_INET6), DhcpConfigError); + + cfg_text = + "[ \n" + " { \n" + " \"name\": \"UNKNOWN\", \n" + " \"only-if-required\": true \n" + " } \n" + "] \n"; + + EXPECT_THROW(parseClientClassDefList(cfg_text, AF_INET), DhcpConfigError); + + cfg_text = + "[ \n" + " { \n" + " \"name\": \"UNKNOWN\", \n" + " \"test\": \"'aa' == 'aa'\" \n" + " } \n" + "] \n"; + + EXPECT_THROW(parseClientClassDefList(cfg_text, AF_INET6), DhcpConfigError); +} + +// Verifies that the special DROP class can't be required or +// dependent on KNOWN/UNKNOWN +TEST_F(ClientClassDefListParserTest, dropCheckError) { + std::string cfg_text = + "[ \n" + " { \n" + " \"name\": \"DROP\", \n" + " \"test\": \"option[123].text == 'abc'\" \n" + " } \n" + "] \n"; + + EXPECT_NO_THROW(parseClientClassDefList(cfg_text, AF_INET6)); + + cfg_text = + "[ \n" + " { \n" + " \"name\": \"DROP\", \n" + " \"only-if-required\": true \n" + " } \n" + "] \n"; + + EXPECT_THROW(parseClientClassDefList(cfg_text, AF_INET), DhcpConfigError); + + cfg_text = + "[ \n" + " { \n" + " \"name\": \"DROP\", \n" + " \"test\": \"member('KNOWN')\" \n" + " } \n" + "] \n"; + + EXPECT_THROW(parseClientClassDefList(cfg_text, AF_INET6), DhcpConfigError); +} + } // end of anonymous namespace