BIND 10 master, updated. acc4ba201116d19be4f9d6389461088e6c89b3ea [master] Merge branch 'trac3203' (DHCP client classification, part 1)
BIND 10 source code commits
bind10-changes at lists.isc.org
Tue Jan 21 14:35:03 UTC 2014
The branch, master has been updated
via acc4ba201116d19be4f9d6389461088e6c89b3ea (commit)
via afea612c23143f81a4201e39ba793bc837c5c9f1 (commit)
via 4e3b085d9487815577d6b3319b51e51111321993 (commit)
via ab8c4956534aa2f488cd7bd657e9162fddd444be (commit)
via daa4b00d29427a0ba98f88bf7eb6cdecd39ada2e (commit)
via 71965bcfe5ce73408bddcef2b29e8ea9b459e5b5 (commit)
via ee836ac5bf89a79dd34065296cd40a1cab069038 (commit)
via 3dade343a9097f9c8c5f2f101b4216575db6234a (commit)
via b0c84e76de79f261883524f5e8d811cbc21071b7 (commit)
via 8840cfe8f5be3697f94f03df4e3b2c2d7b4421a1 (commit)
via e1f810f294bae8be142a9101326c066942665a34 (commit)
from c27219ff78ee2927a4ab44e4f2a0020f7310dec1 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit acc4ba201116d19be4f9d6389461088e6c89b3ea
Merge: c27219f afea612
Author: Tomek Mrugalski <tomasz at isc.org>
Date: Tue Jan 21 15:34:30 2014 +0100
[master] Merge branch 'trac3203' (DHCP client classification, part 1)
Conflicts:
src/bin/dhcp4/dhcp4_srv.cc
src/bin/dhcp4/tests/dhcp4_test_utils.h
src/bin/dhcp6/dhcp6_srv.cc
src/lib/dhcp/pkt4.cc
src/lib/dhcp/tests/pkt4_unittest.cc
-----------------------------------------------------------------------
Summary of changes:
ChangeLog | 10 ++
doc/devel/mainpage.dox | 2 +
src/bin/dhcp4/ctrl_dhcp4_srv.h | 2 +-
src/bin/dhcp4/dhcp4.dox | 30 ++++++
src/bin/dhcp4/dhcp4_messages.mes | 8 ++
src/bin/dhcp4/dhcp4_srv.cc | 97 +++++++++++++++++++
src/bin/dhcp4/dhcp4_srv.h | 21 +++-
src/bin/dhcp4/tests/dhcp4_srv_unittest.cc | 30 +++++-
src/bin/dhcp4/tests/dhcp4_test_utils.h | 11 ++-
src/bin/dhcp4/tests/wireshark.cc | 60 +++++++++++-
src/bin/dhcp6/ctrl_dhcp6_srv.h | 2 +-
src/bin/dhcp6/dhcp6.dox | 27 ++++++
src/bin/dhcp6/dhcp6_messages.mes | 4 +
src/bin/dhcp6/dhcp6_srv.cc | 37 +++++++
src/bin/dhcp6/dhcp6_srv.h | 10 ++
src/bin/dhcp6/tests/dhcp6_srv_unittest.cc | 29 ++++++
src/bin/dhcp6/tests/dhcp6_test_utils.h | 2 +
src/bin/dhcp6/tests/wireshark.cc | 150 ++++++++++++++++++++++++++++-
src/lib/dhcp/docsis3_option_defs.h | 11 ++-
src/lib/dhcp/libdhcp++.cc | 8 ++
src/lib/dhcp/option6_iaprefix.h | 3 +-
src/lib/dhcp/option_vendor.h | 7 +-
src/lib/dhcp/pkt4.cc | 10 ++
src/lib/dhcp/pkt4.h | 35 +++++++
src/lib/dhcp/pkt6.cc | 11 +++
src/lib/dhcp/pkt6.h | 33 +++++++
src/lib/dhcp/std_option_defs.h | 5 +-
src/lib/dhcp/tests/libdhcp++_unittest.cc | 46 ++++++++-
src/lib/dhcp/tests/pkt4_unittest.cc | 30 ++++++
src/lib/dhcp/tests/pkt6_unittest.cc | 31 ++++++
src/lib/dhcpsrv/dbaccess_parser.h | 2 +-
src/lib/dhcpsrv/lease_mgr.h | 2 +-
32 files changed, 742 insertions(+), 24 deletions(-)
-----------------------------------------------------------------------
diff --git a/ChangeLog b/ChangeLog
index a1784e1..83e6f3f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+732. [func] tomek
+ b10-dhcp4, b10-dhcp6: Support for simplified client classification
+ added. Incoming packets are now assigned to a client class based on
+ the content of the packet's user class option (DHCPv4) or vendor class
+ option (DHCPv6). Two classes (docsis3.0 and eRouter1.0) have class
+ specific behavior in b10-dhcp4. See DHCPv4 Client Classification and
+ DHCPv6 Client Classification in BIND10 Developer's Guide for details.
+ This is a first ticket in a series of planned at least three tickets.
+ (Trac #3203, git afea612c23143f81a4201e39ba793bc837c5c9f1)
+
731. [func] tmark
b10-dhcp4 now parses parameters which support DHCP-DDNS updates via
the DHCP-DDNS module, b10-dhcp-ddns. These parameters are part of new
diff --git a/doc/devel/mainpage.dox b/doc/devel/mainpage.dox
index c30ad35..f9c5d61 100644
--- a/doc/devel/mainpage.dox
+++ b/doc/devel/mainpage.dox
@@ -60,6 +60,7 @@
* - @subpage dhcpv4ConfigInherit
* - @subpage dhcpv4OptionsParse
* - @subpage dhcpv4DDNSIntegration
+ * - @subpage dhcpv4Classifier
* - @subpage dhcpv4Other
* - @subpage dhcp6
* - @subpage dhcpv6Session
@@ -67,6 +68,7 @@
* - @subpage dhcpv6ConfigInherit
* - @subpage dhcpv6DDNSIntegration
* - @subpage dhcpv6OptionsParse
+ * - @subpage dhcpv6Classifier
* - @subpage dhcpv6Other
* - @subpage libdhcp
* - @subpage libdhcpIntro
diff --git a/src/bin/dhcp4/ctrl_dhcp4_srv.h b/src/bin/dhcp4/ctrl_dhcp4_srv.h
index 526d987..ce2a753 100644
--- a/src/bin/dhcp4/ctrl_dhcp4_srv.h
+++ b/src/bin/dhcp4/ctrl_dhcp4_srv.h
@@ -107,7 +107,7 @@ protected:
/// various configuration values. Installing the dummy handler
/// that guarantees to return success causes initial configuration
/// to be stored for the session being created and that it can
- /// be later accessed with \ref isc::ConfigData::getFullConfig.
+ /// be later accessed with \ref isc::config::ConfigData::getFullConfig.
///
/// @param new_config new configuration.
///
diff --git a/src/bin/dhcp4/dhcp4.dox b/src/bin/dhcp4/dhcp4.dox
index 0d457c6..aa43ee3 100644
--- a/src/bin/dhcp4/dhcp4.dox
+++ b/src/bin/dhcp4/dhcp4.dox
@@ -155,6 +155,36 @@ The default behaviour is constituted by the set of constants defined in the
(upper part of) dhcp4_srv.cc file. Once the configuration is implemented,
these constants will be removed.
+ at section dhcpv4Classifier DHCPv4 Client Classification
+
+Kea DHCPv4 server currently supports simplified client classification. It is called
+"simplified", because the incoming packets are classified based on the content
+of the vendor class (60) option. More flexible classification is planned, but there
+are no specific development dates agreed.
+
+For each incoming packet, @ref isc::dhcp::Dhcpv4Srv::classifyPacket() method is called.
+It attempts to extract content of the vendor class option and interpret as a name
+of the class. For now, the code has been tested with two classes used in cable modem
+networks: eRouter1.0 and docsis3.0, but any other content of the vendor class option will
+be interpreted as a class name.
+
+In principle any given packet can belong to zero or more classes. As the current
+classifier is very modest, there's only one way to assign a class (based on vendor class
+option), the ability to assign more than one class to a packet is not yet exercised.
+Neverthless, there is such a possibility and it will be used in a near future. To
+check whether a packet belongs to given class, isc::dhcp::Pkt4::inClass method should
+be used.
+
+Currently there is a short code section that alternates packet processing depending on
+which class it belongs to. It is planned to move that capability to an external hook
+library. See ticket #3275. The class specific behavior is:
+
+- docsis3.0 packets have siaddr (next server) field set
+- docsis3.0 packets have file field set to the content of the boot-file-name option
+- eRouter1.0 packets have siaddr (next server) field cleared
+
+Aforementioned modifications are conducted in @ref isc::dhcp::Dhcpv4Srv::classSpecificProcessing.
+
@section dhcpv4Other Other DHCPv4 topics
For hooks API support in DHCPv4, see @ref dhcpv4Hooks.
diff --git a/src/bin/dhcp4/dhcp4_messages.mes b/src/bin/dhcp4/dhcp4_messages.mes
index 4fba57a..297acfd 100644
--- a/src/bin/dhcp4/dhcp4_messages.mes
+++ b/src/bin/dhcp4/dhcp4_messages.mes
@@ -32,6 +32,14 @@ This debug message is issued when the DHCP server was unable to process the
FQDN or Hostname option sent by a client. This is likely because the client's
name was malformed or due to internal server error.
+% DHCP4_CLASS_PROCESSING_FAILED client class specific processing failed
+This debug message means that the server processing that is unique for each
+client class has reported a failure. The response packet will not be sent.
+
+% DHCP4_CLASS_ASSIGNED client packet has been assigned to the following class(es): %1
+This debug message informs that incoming packet has been assigned to specified
+class or classes. This is a norma
+
% DHCP4_COMMAND_RECEIVED received command %1, arguments: %2
A debug message listing the command (and possible arguments) received
from the BIND 10 control system by the IPv4 DHCP server.
diff --git a/src/bin/dhcp4/dhcp4_srv.cc b/src/bin/dhcp4/dhcp4_srv.cc
index 22ab653..5fc5598 100644
--- a/src/bin/dhcp4/dhcp4_srv.cc
+++ b/src/bin/dhcp4/dhcp4_srv.cc
@@ -21,6 +21,7 @@
#include <dhcp/option_int.h>
#include <dhcp/option_int_array.h>
#include <dhcp/option_vendor.h>
+#include <dhcp/option_string.h>
#include <dhcp/pkt4.h>
#include <dhcp/docsis3_option_defs.h>
#include <dhcp4/dhcp4_log.h>
@@ -312,6 +313,9 @@ Dhcpv4Srv::run() {
callout_handle->getArgument("query4", query);
}
+ // Assign this packet to one or more classes if needed
+ classifyPacket(query);
+
try {
switch (query->getType()) {
case DHCPDISCOVER:
@@ -368,6 +372,18 @@ Dhcpv4Srv::run() {
continue;
}
+ // Let's do class specific processing. This is done before
+ // pkt4_send.
+ //
+ /// @todo: decide whether we want to add a new hook point for
+ /// doing class specific processing.
+ if (!classSpecificProcessing(query, rsp)) {
+ /// @todo add more verbosity here
+ LOG_DEBUG(dhcp4_logger, DBG_DHCP4_BASIC, DHCP4_CLASS_PROCESSING_FAILED);
+
+ continue;
+ }
+
// Specifies if server should do the packing
bool skip_pack = false;
@@ -1766,5 +1782,86 @@ Dhcpv4Srv::ifaceMgrSocket4ErrorHandler(const std::string& errmsg) {
LOG_WARN(dhcp4_logger, DHCP4_OPEN_SOCKET_FAIL).arg(errmsg);
}
+void Dhcpv4Srv::classifyPacket(const Pkt4Ptr& pkt) {
+ boost::shared_ptr<OptionString> vendor_class =
+ boost::dynamic_pointer_cast<OptionString>(pkt->getOption(DHO_VENDOR_CLASS_IDENTIFIER));
+
+ string classes = "";
+
+ if (!vendor_class) {
+ return;
+ }
+
+ // DOCSIS specific section
+
+ // Let's keep this as a series of checks. So far we're supporting only
+ // docsis3.0, but there are also docsis2.0, docsis1.1 and docsis1.0. We
+ // may come up with adding several classes, e.g. for docsis2.0 we would
+ // add classes docsis2.0, docsis1.1 and docsis1.0.
+
+ // Also we are using find, because we have at least one traffic capture
+ // where the user class was followed by a space ("docsis3.0 ").
+
+ // For now, the code is very simple, but it is expected to get much more
+ // complex soon. One specific case is that the vendor class is an option
+ // sent by the client, so we should not trust it. To confirm that the device
+ // is indeed a modem, John B. suggested to check whether chaddr field
+ // quals subscriber-id option that was inserted by the relay (CMTS).
+ // This kind of logic will appear here soon.
+ if (vendor_class->getValue().find(DOCSIS3_CLASS_MODEM) != std::string::npos) {
+ pkt->addClass(DOCSIS3_CLASS_MODEM);
+ classes += string(DOCSIS3_CLASS_MODEM) + " ";
+ } else
+ if (vendor_class->getValue().find(DOCSIS3_CLASS_EROUTER) != std::string::npos) {
+ pkt->addClass(DOCSIS3_CLASS_EROUTER);
+ classes += string(DOCSIS3_CLASS_EROUTER) + " ";
+ } else {
+ classes += vendor_class->getValue();
+ pkt->addClass(vendor_class->getValue());
+ }
+
+ if (!classes.empty()) {
+ LOG_DEBUG(dhcp4_logger, DBG_DHCP4_BASIC, DHCP4_CLASS_ASSIGNED)
+ .arg(classes);
+ }
+}
+
+bool Dhcpv4Srv::classSpecificProcessing(const Pkt4Ptr& query, const Pkt4Ptr& rsp) {
+
+ Subnet4Ptr subnet = selectSubnet(query);
+ if (!subnet) {
+ return (true);
+ }
+
+ if (query->inClass(DOCSIS3_CLASS_MODEM)) {
+
+ // Set next-server. This is TFTP server address. Cable modems will
+ // download their configuration from that server.
+ rsp->setSiaddr(subnet->getSiaddr());
+
+ // Now try to set up file field in DHCPv4 packet. We will just copy
+ // content of the boot-file option, which contains the same information.
+ Subnet::OptionDescriptor desc =
+ subnet->getOptionDescriptor("dhcp4", DHO_BOOT_FILE_NAME);
+
+ if (desc.option) {
+ boost::shared_ptr<OptionString> boot =
+ boost::dynamic_pointer_cast<OptionString>(desc.option);
+ if (boot) {
+ std::string filename = boot->getValue();
+ rsp->setFile((const uint8_t*)filename.c_str(), filename.size());
+ }
+ }
+ }
+
+ if (query->inClass(DOCSIS3_CLASS_EROUTER)) {
+
+ // Do not set TFTP server address for eRouter devices.
+ rsp->setSiaddr(IOAddress("0.0.0.0"));
+ }
+
+ return (true);
+}
+
} // namespace dhcp
} // namespace isc
diff --git a/src/bin/dhcp4/dhcp4_srv.h b/src/bin/dhcp4/dhcp4_srv.h
index 04e622b..ec63e7f 100644
--- a/src/bin/dhcp4/dhcp4_srv.h
+++ b/src/bin/dhcp4/dhcp4_srv.h
@@ -262,7 +262,7 @@ protected:
/// using separate options within their respective vendor-option spaces.
///
/// @param question DISCOVER or REQUEST message from a client.
- /// @param msg outgoing message (options will be added here)
+ /// @param answer outgoing message (options will be added here)
void appendRequestedVendorOptions(const Pkt4Ptr& question, Pkt4Ptr& answer);
/// @brief Assigns a lease and appends corresponding options
@@ -559,6 +559,25 @@ protected:
const std::string& option_space,
isc::dhcp::OptionCollection& options);
+ /// @brief Assigns incoming packet to zero or more classes.
+ ///
+ /// @note For now, the client classification is very simple. It just uses
+ /// content of the vendor-class-identifier option as a class. The resulting
+ /// class will be stored in packet (see @ref isc::dhcp::Pkt4::classes_ and
+ /// @ref isc::dhcp::Pkt4::inClass).
+ ///
+ /// @param pkt packet to be classified
+ void classifyPacket(const Pkt4Ptr& pkt);
+
+ /// @brief Performs packet processing specific to a class
+ ///
+ /// This processing is a likely candidate to be pushed into hooks.
+ ///
+ /// @param query incoming client's packet
+ /// @param rsp server's response
+ /// @return true if successful, false otherwise (will prevent sending response)
+ bool classSpecificProcessing(const Pkt4Ptr& query, const Pkt4Ptr& rsp);
+
private:
/// @brief Constructs netmask option based on subnet4
diff --git a/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc b/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
index 65a9ca5..e930ba0 100644
--- a/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
+++ b/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
@@ -3173,8 +3173,32 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, vendorOptionsDocsisDefinitions) {
ASSERT_EQ(0, rcode_);
}
+// Checks if client packets are classified properly
+TEST_F(Dhcpv4SrvTest, clientClassification) {
+
+ NakedDhcpv4Srv srv(0);
+
+ // Let's create a relayed DISCOVER. This particular relayed DISCOVER has
+ // vendor-class set to docsis3.0
+ Pkt4Ptr dis1;
+ ASSERT_NO_THROW(dis1 = captureRelayedDiscover());
+ ASSERT_NO_THROW(dis1->unpack());
+
+ srv.classifyPacket(dis1);
+
+ EXPECT_TRUE(dis1->inClass("docsis3.0"));
+ EXPECT_FALSE(dis1->inClass("eRouter1.0"));
+
+ // Let's create a relayed DISCOVER. This particular relayed DISCOVER has
+ // vendor-class set to eRouter1.0
+ Pkt4Ptr dis2;
+ ASSERT_NO_THROW(dis2 = captureRelayedDiscover2());
+ ASSERT_NO_THROW(dis2->unpack());
+
+ srv.classifyPacket(dis2);
+
+ EXPECT_TRUE(dis2->inClass("eRouter1.0"));
+ EXPECT_FALSE(dis2->inClass("docsis3.0"));
}
- /*I}; // end of isc::dhcp::test namespace
-}; // end of isc::dhcp namespace
-}; // end of isc namespace */
+}; // end of anonymous namespace
diff --git a/src/bin/dhcp4/tests/dhcp4_test_utils.h b/src/bin/dhcp4/tests/dhcp4_test_utils.h
index 65ae312..8a22117 100644
--- a/src/bin/dhcp4/tests/dhcp4_test_utils.h
+++ b/src/bin/dhcp4/tests/dhcp4_test_utils.h
@@ -222,7 +222,8 @@ public:
/// @brief returns captured DISCOVER that went through a relay
///
- /// See method code for a detailed explanation.
+ /// See method code for a detailed explanation. This is a discover from
+ /// docsis3.0 device (Cable Modem)
///
/// @return relayed DISCOVER
Pkt4Ptr captureRelayedDiscover();
@@ -253,6 +254,13 @@ public:
createPacketFromBuffer(const Pkt4Ptr& src_pkt,
Pkt4Ptr& dst_pkt);
+ /// @brief returns captured DISCOVER that went through a relay
+ ///
+ /// See method code for a detailed explanation. This is a discover from
+ /// eRouter1.0 device (CPE device integrated with cable modem)
+ ///
+ /// @return relayed DISCOVER
+ Pkt4Ptr captureRelayedDiscover2();
/// @brief generates a DHCPv4 packet based on provided hex string
///
@@ -443,6 +451,7 @@ public:
using Dhcpv4Srv::srvidToString;
using Dhcpv4Srv::unpackOptions;
using Dhcpv4Srv::name_change_reqs_;
+ using Dhcpv4Srv::classifyPacket;
};
}; // end of isc::dhcp::test namespace
diff --git a/src/bin/dhcp4/tests/wireshark.cc b/src/bin/dhcp4/tests/wireshark.cc
index 80b4737..c07bc1b 100644
--- a/src/bin/dhcp4/tests/wireshark.cc
+++ b/src/bin/dhcp4/tests/wireshark.cc
@@ -71,7 +71,10 @@ void Dhcpv4SrvTest::captureSetDefaultFields(const Pkt4Ptr& pkt) {
Pkt4Ptr Dhcpv4SrvTest::captureRelayedDiscover() {
-/* string exported from Wireshark:
+/* This is packet 1 from capture
+ dhcp-val/pcap/docsis-*-CG3000DCR-Registration-Filtered.cap
+
+string exported from Wireshark:
User Datagram Protocol, Src Port: bootps (67), Dst Port: bootps (67)
Source port: bootps (67)
@@ -98,7 +101,7 @@ Bootstrap Protocol
Magic cookie: DHCP
Option: (53) DHCP Message Type
Option: (55) Parameter Request List
- Option: (60) Vendor class identifier
+ Option: (60) Vendor class identifier (docsis3.0)
Option: (125) V-I Vendor-specific Information
- suboption 1 (Option Request): requesting option 2
- suboption 5 (Modem Caps): 117 bytes
@@ -129,6 +132,59 @@ Bootstrap Protocol
return (packetFromCapture(hex_string));
}
+Pkt4Ptr Dhcpv4SrvTest::captureRelayedDiscover2() {
+
+/* This is packet 5 from capture
+ dhcp-val/pcap/docsis-*-CG3000DCR-Registration-Filtered.cap
+
+string exported from Wireshark:
+
+User Datagram Protocol, Src Port: bootps (67), Dst Port: bootps (67)
+Bootstrap Protocol
+ Message type: Boot Request (1)
+ Hardware type: Ethernet (0x01)
+ Hardware address length: 6
+ Hops: 1
+ Transaction ID: 0x5d05478f
+ Seconds elapsed: 5
+ Bootp flags: 0x0000 (Unicast)
+ Client IP address: 0.0.0.0 (0.0.0.0)
+ Your (client) IP address: 0.0.0.0 (0.0.0.0)
+ Next server IP address: 0.0.0.0 (0.0.0.0)
+ Relay agent IP address: 10.254.226.1 (10.254.226.1)
+ Client MAC address: Netgear_b8:15:15 (20:e5:2a:b8:15:15)
+ Client hardware address padding: 00000000000000000000
+ Server host name not given
+ Boot file name not given
+ Magic cookie: DHCP
+ Option: (53) DHCP Message Type
+ Option: (55) Parameter Request List
+ Option: (43) Vendor-Specific Information
+ Option: (60) Vendor class identifier (eRouter1.0)
+ Option: (15) Domain Name
+ Option: (61) Client identifier
+ Option: (57) Maximum DHCP Message Size
+ Option: (82) Agent Information Option
+ Option: (255) End */
+
+ string hex_string =
+ "010106015d05478f000500000000000000000000000000000afee20120e52ab8151500"
+ "0000000000000000000000000000000000000000000000000000000000000000000000"
+ "0000000000000000000000000000000000000000000000000000000000000000000000"
+ "0000000000000000000000000000000000000000000000000000000000000000000000"
+ "0000000000000000000000000000000000000000000000000000000000000000000000"
+ "0000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000063825363350101370e"
+ "480102030406070c0f171a36337a2b63020745524f55544552030b45434d3a45524f55"
+ "544552040d324252323239553430303434430504312e3034060856312e33332e303307"
+ "07322e332e305232080630303039354209094347333030304443520a074e6574676561"
+ "720f0745524f555445523c0a65526f75746572312e300f14687364312e70612e636f6d"
+ "636173742e6e65742e3d0fff2ab815150003000120e52ab81515390205dc5219010420"
+ "000002020620e52ab8151409090000118b0401020300ff";
+
+ return (packetFromCapture(hex_string));
+}
+
}; // end of isc::dhcp::test namespace
}; // end of isc::dhcp namespace
}; // end of isc namespace
diff --git a/src/bin/dhcp6/ctrl_dhcp6_srv.h b/src/bin/dhcp6/ctrl_dhcp6_srv.h
index ffd43c3..2fb6cc9 100644
--- a/src/bin/dhcp6/ctrl_dhcp6_srv.h
+++ b/src/bin/dhcp6/ctrl_dhcp6_srv.h
@@ -105,7 +105,7 @@ protected:
/// various configuration values. Installing the dummy handler
/// that guarantees to return success causes initial configuration
/// to be stored for the session being created and that it can
- /// be later accessed with \ref isc::ConfigData::getFullConfig.
+ /// be later accessed with \ref isc::config::ConfigData::getFullConfig.
///
/// @param new_config new configuration.
///
diff --git a/src/bin/dhcp6/dhcp6.dox b/src/bin/dhcp6/dhcp6.dox
index c0284c7..c722472 100644
--- a/src/bin/dhcp6/dhcp6.dox
+++ b/src/bin/dhcp6/dhcp6.dox
@@ -190,6 +190,33 @@ implemented within the context of the server and it has access to all objects
which define its configuration (including dynamically created option
definitions).
+ at section dhcpv6Classifier DHCPv6 Client Classification
+
+Kea DHCPv6 server currently supports simplified client classification. It is called
+"simplified", because the incoming packets are classified based on the content
+of the vendor class (16) option. More flexible classification is planned, but there
+are no specific development dates agreed.
+
+For each incoming packet, @ref isc::dhcp::Dhcpv6Srv::classifyPacket() method is
+called. It attempts to extract content of the vendor class option and interprets
+as a name of the class. Although the RFC3315 says that the vendor class may
+contain more than one chunk of data, the existing code handles only one data
+block, because that is what actual devices use. For now, the code has been
+tested with two classes used in cable modem networks: eRouter1.0 and docsis3.0,
+but any other content of the vendor class option will be interpreted as a class
+name.
+
+In principle any given packet can belong to zero or more classes. As the current
+classifier is very modest, there's only one way to assign a class (based on vendor class
+option), the ability to assign more than one class to a packet is not yet exercised.
+Neverthless, there is such a possibility and it will be used in a near future. To
+check whether a packet belongs to given class, isc::dhcp::Pkt6::inClass method should
+be used.
+
+Currently there is no class behaviour coded in DHCPv6, hence no v6 equivalent of
+ at ref isc::dhcp::Dhcpv4Srv::classSpecificProcessing. Should any need for such a code arise,
+it will be conducted in an external hooks library.
+
@section dhcpv6Other Other DHCPv6 topics
For hooks API support in DHCPv6, see @ref dhcpv6Hooks.
diff --git a/src/bin/dhcp6/dhcp6_messages.mes b/src/bin/dhcp6/dhcp6_messages.mes
index 7e8b64e..46449d8 100644
--- a/src/bin/dhcp6/dhcp6_messages.mes
+++ b/src/bin/dhcp6/dhcp6_messages.mes
@@ -27,6 +27,10 @@ successfully established a session with the BIND 10 control channel.
This debug message is issued just before the IPv6 DHCP server attempts
to establish a session with the BIND 10 control channel.
+% DHCP6_CLASS_ASSIGNED client packet has been assigned to the following class(es): %1
+This debug message informs that incoming packet has been assigned to specified
+class or classes. This is a norma
+
% DHCP6_CLIENTID_MISSING mandatory client-id option is missing, message from %1 dropped
This error message indicates that the received message is being dropped
because it does not include the mandatory client-id option necessary for
diff --git a/src/bin/dhcp6/dhcp6_srv.cc b/src/bin/dhcp6/dhcp6_srv.cc
index 601a95c..3dffa37 100644
--- a/src/bin/dhcp6/dhcp6_srv.cc
+++ b/src/bin/dhcp6/dhcp6_srv.cc
@@ -316,6 +316,9 @@ bool Dhcpv6Srv::run() {
callout_handle->getArgument("query6", query);
}
+ // Assign this packet to a class, if possible
+ classifyPacket(query);
+
try {
NameChangeRequestPtr ncr;
switch (query->getType()) {
@@ -2423,5 +2426,39 @@ Dhcpv6Srv::ifaceMgrSocket6ErrorHandler(const std::string& errmsg) {
LOG_WARN(dhcp6_logger, DHCP6_OPEN_SOCKET_FAIL).arg(errmsg);
}
+void Dhcpv6Srv::classifyPacket(const Pkt6Ptr& pkt) {
+
+ boost::shared_ptr<OptionCustom> vclass =
+ boost::dynamic_pointer_cast<OptionCustom>(pkt->getOption(D6O_VENDOR_CLASS));
+
+ if (!vclass) {
+ return;
+ }
+
+ string classes = "";
+
+ // DOCSIS specific section
+ if (vclass->readString(VENDOR_CLASS_STRING_INDEX)
+ .find(DOCSIS3_CLASS_MODEM) != std::string::npos) {
+ pkt->addClass(DOCSIS3_CLASS_MODEM);
+ classes += string(DOCSIS3_CLASS_MODEM) + " ";
+ } else
+ if (vclass->readString(VENDOR_CLASS_STRING_INDEX)
+ .find(DOCSIS3_CLASS_EROUTER) != std::string::npos) {
+ pkt->addClass(DOCSIS3_CLASS_EROUTER);
+ classes += string(DOCSIS3_CLASS_EROUTER) + " ";
+ }else
+ {
+ // Otherwise use the string as is
+ classes += vclass->readString(VENDOR_CLASS_STRING_INDEX);
+ pkt->addClass(vclass->readString(VENDOR_CLASS_STRING_INDEX));
+ }
+
+ if (!classes.empty()) {
+ LOG_DEBUG(dhcp6_logger, DBG_DHCP6_BASIC, DHCP6_CLASS_ASSIGNED)
+ .arg(classes);
+ }
+}
+
};
};
diff --git a/src/bin/dhcp6/dhcp6_srv.h b/src/bin/dhcp6/dhcp6_srv.h
index 26a7d71..594d833 100644
--- a/src/bin/dhcp6/dhcp6_srv.h
+++ b/src/bin/dhcp6/dhcp6_srv.h
@@ -533,6 +533,16 @@ protected:
size_t* relay_msg_offset,
size_t* relay_msg_len);
+ /// @brief Assigns incoming packet to zero or more classes.
+ ///
+ /// @note For now, the client classification is very simple. It just uses
+ /// content of the vendor-class-identifier option as a class. The resulting
+ /// class will be stored in packet (see @ref isc::dhcp::Pkt6::classes_ and
+ /// @ref isc::dhcp::Pkt6::inClass).
+ ///
+ /// @param pkt packet to be classified
+ void classifyPacket(const Pkt6Ptr& pkt);
+
private:
/// @brief Implements the error handler for socket open failure.
diff --git a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
index 75c65d8..e8f5e79 100644
--- a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
+++ b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
@@ -1673,6 +1673,35 @@ TEST_F(Dhcpv6SrvTest, unpackOptions) {
EXPECT_EQ(0x0, option_bar->getValue());
}
+// Checks if client packets are classified properly
+TEST_F(Dhcpv6SrvTest, clientClassification) {
+
+ NakedDhcpv6Srv srv(0);
+
+ // Let's create a relayed SOLICIT. This particular relayed SOLICIT has
+ // vendor-class set to docsis3.0
+ Pkt6Ptr sol1;
+ ASSERT_NO_THROW(sol1 = captureDocsisRelayedSolicit());
+ ASSERT_NO_THROW(sol1->unpack());
+
+ srv.classifyPacket(sol1);
+
+ // It should belong to docsis3.0 class. It should not belong to eRouter1.0
+ EXPECT_TRUE(sol1->inClass("docsis3.0"));
+ EXPECT_FALSE(sol1->inClass("eRouter1.0"));
+
+ // Let's get a relayed SOLICIT. This particular relayed SOLICIT has
+ // vendor-class set to eRouter1.0
+ Pkt6Ptr sol2;
+ ASSERT_NO_THROW(sol2 = captureeRouterRelayedSolicit());
+ ASSERT_NO_THROW(sol2->unpack());
+
+ srv.classifyPacket(sol2);
+
+ EXPECT_TRUE(sol2->inClass("eRouter1.0"));
+ EXPECT_FALSE(sol2->inClass("docsis3.0"));
+}
+
/// @todo: Add more negative tests for processX(), e.g. extend sanityCheck() test
/// to call processX() methods.
diff --git a/src/bin/dhcp6/tests/dhcp6_test_utils.h b/src/bin/dhcp6/tests/dhcp6_test_utils.h
index 2b6241e..82ff984 100644
--- a/src/bin/dhcp6/tests/dhcp6_test_utils.h
+++ b/src/bin/dhcp6/tests/dhcp6_test_utils.h
@@ -112,6 +112,7 @@ public:
using Dhcpv6Srv::createStatusCode;
using Dhcpv6Srv::selectSubnet;
using Dhcpv6Srv::sanityCheck;
+ using Dhcpv6Srv::classifyPacket;
using Dhcpv6Srv::loadServerID;
using Dhcpv6Srv::writeServerID;
using Dhcpv6Srv::unpackOptions;
@@ -477,6 +478,7 @@ public:
Pkt6Ptr captureSimpleSolicit();
Pkt6Ptr captureRelayedSolicit();
Pkt6Ptr captureDocsisRelayedSolicit();
+ Pkt6Ptr captureeRouterRelayedSolicit();
/// @brief Auxiliary method that sets Pkt6 fields
///
diff --git a/src/bin/dhcp6/tests/wireshark.cc b/src/bin/dhcp6/tests/wireshark.cc
index 4e96548..cd29096 100644
--- a/src/bin/dhcp6/tests/wireshark.cc
+++ b/src/bin/dhcp6/tests/wireshark.cc
@@ -17,9 +17,9 @@
#include <string>
/// @file wireshark.cc
-///
+///
/// @brief contains packet captures imported from Wireshark
-///
+///
/// These are actual packets captured over wire. They are used in various
/// tests.
///
@@ -33,6 +33,10 @@
/// 6. Coding guidelines line restrictions apply, so wrap your code as necessary
/// 7. Make sure you decribe the capture appropriately
/// 8. Follow whatever rest of the methods are doing (set ports, ifaces etc.)
+/// 9. To easily copy packet description, click File... -> Extract packet
+/// dissections -> as plain text file...
+/// (Make sure that the packet is expanded in the view. The text file will
+/// contain whatever expansion level you have in the graphical tree.)
using namespace std;
@@ -81,7 +85,7 @@ Pkt6Ptr Dhcpv6SrvTest::captureRelayedSolicit() {
// - ORO (7)
// string exported from Wireshark
- string hex_string =
+ string hex_string =
"0c0500000000000000000000000000000000fc00000000000000000000000000000900"
"12000231350009002c010517100001000e0001000151b5e46208002758f1e80003000c"
"000000010000000000000000000600020007";
@@ -102,7 +106,7 @@ Pkt6Ptr Dhcpv6SrvTest::captureRelayedSolicit() {
Pkt6Ptr isc::test::Dhcpv6SrvTest::captureDocsisRelayedSolicit() {
// This is an actual DOCSIS packet
- // RELAY-FORW (12)
+ // RELAY-FORW (12)
// - Relay Message
// - SOLICIT (1)
// - client-id
@@ -132,7 +136,7 @@ Pkt6Ptr isc::test::Dhcpv6SrvTest::captureDocsisRelayedSolicit() {
// - Suboption 1026: Cable Modem MAC addr = 10:0d:7f:00:07:88
// string exported from Wireshark
- string hex_string =
+ string hex_string =
"0c002a0288fe00fe00015a8d09fffe7af955fe80000000000000120d7ffffe00078800"
"090189010d397f0001000a00030001100d7f000788000300287f000788000000000000"
"000000050018000000000000000000000000000000000000000000000000000e000000"
@@ -159,5 +163,141 @@ Pkt6Ptr isc::test::Dhcpv6SrvTest::captureDocsisRelayedSolicit() {
return (pkt);
}
+/// returns a buffer with relayed SOLICIT (from DOCSIS3.0 eRouter)
+Pkt6Ptr isc::test::Dhcpv6SrvTest::captureeRouterRelayedSolicit() {
+
+/* Packet description exported from wireshark:
+DHCPv6
+ Message type: Relay-forw (12)
+ Hopcount: 0
+ Link address: 2001:558:ffa8::1 (2001:558:ffa8::1)
+ Peer address: fe80::22e5:2aff:feb8:1515 (fe80::22e5:2aff:feb8:1515)
+ Relay Message
+ Option: Relay Message (9)
+ Length: 241
+ Value: 01a90044000e000000140000000600080011001700180019...
+ DHCPv6
+ Message type: Solicit (1)
+ Transaction ID: 0xa90044
+ Rapid Commit
+ Option: Rapid Commit (14)
+ Length: 0
+ Reconfigure Accept
+ Option: Reconfigure Accept (20)
+ Length: 0
+ Option Request
+ Option: Option Request (6)
+ Length: 8
+ Value: 0011001700180019
+ Requested Option code: Vendor-specific Information (17)
+ Requested Option code: DNS recursive name server (23)
+ Requested Option code: Domain Search List (24)
+ Requested Option code: Identity Association for Prefix Delegation (25)
+ Vendor Class
+ Option: Vendor Class (16)
+ Length: 16
+ Value: 0000118b000a65526f75746572312e30
+ Enterprise ID: Cable Television Laboratories, Inc. (4491)
+ vendor-class-data: eRouter1.0
+ Vendor-specific Information
+ Option: Vendor-specific Information (17)
+ Length: 112
+ Value: 0000118b0002000745524f555445520003000b45434d3a45...
+ Enterprise ID: Cable Television Laboratories, Inc. (4491)
+ Suboption: Device Type = (2)"EROUTER"
+ Suboption: Embedded Components = (3)"ECM:EROUTER"
+ Suboption: Serial Number = (4)"2BR229U40044C"
+ Suboption: Hardware Version = (5)"1.04"
+ Suboption: Software Version = (6)"V1.33.03"
+ Suboption: Boot ROM Version = (7)"2.3.0R2"
+ Suboption: Organization Unique Identifier = (8)"00095B"
+ Suboption: Model Number = (9)"CG3000DCR"
+ Suboption: Vendor Name = (10)"Netgear"
+ Client Identifier
+ Option: Client Identifier (1)
+ Length: 10
+ Value: 0003000120e52ab81515
+ DUID: 0003000120e52ab81515
+ DUID Type: link-layer address (3)
+ Hardware type: Ethernet (1)
+ Link-layer address: 20:e5:2a:b8:15:15
+ Identity Association for Prefix Delegation
+ Option: Identity Association for Prefix Delegation (25)
+ Length: 41
+ Value: 2ab815150000000000000000001a00190000000000000000...
+ IAID: 2ab81515
+ T1: 0
+ T2: 0
+ IA Prefix
+ Option: IA Prefix (26)
+ Length: 25
+ Value: 000000000000000038000000000000000000000000000000...
+ Preferred lifetime: 0
+ Valid lifetime: 0
+ Prefix length: 56
+ Prefix address: :: (::)
+ Identity Association for Non-temporary Address
+ Option: Identity Association for Non-temporary Address (3)
+ Length: 12
+ Value: 2ab815150000000000000000
+ IAID: 2ab81515
+ T1: 0
+ T2: 0
+ Elapsed time
+ Option: Elapsed time (8)
+ Length: 2
+ Value: 0000
+ Elapsed time: 0 ms
+ Vendor-specific Information
+ Option: Vendor-specific Information (17)
+ Length: 22
+ Value: 0000118b0402000620e52ab815140401000401020300
+ Enterprise ID: Cable Television Laboratories, Inc. (4491)
+ Suboption: CM MAC Address Option = (1026)20:e5:2a:b8:15:14
+ Suboption: CMTS Capabilities Option : (1025)
+ Interface-Id
+ Option: Interface-Id (18)
+ Length: 4
+ Value: 00000022
+ Interface-ID:
+ Remote Identifier
+ Option: Remote Identifier (37)
+ Length: 14
+ Value: 0000101300015c228d4110000122
+ Enterprise ID: Arris Interactive LLC (4115)
+ Remote-ID: 00015c228d4110000122
+ DHCP option 53
+ Option: Unknown (53)
+ Length: 10
+ Value: 0003000100015c228d3d
+ DUID: 0003000100015c228d3d
+ DUID Type: link-layer address (3)
+ Hardware type: Ethernet (1)
+ Link-layer address: 00:01:5c:22:8d:3d */
+
+ // string exported from Wireshark
+ string hex_string =
+ "0c0020010558ffa800000000000000000001fe8000000000000022e52afffeb8151500"
+ "0900f101a90044000e000000140000000600080011001700180019001000100000118b"
+ "000a65526f75746572312e30001100700000118b0002000745524f555445520003000b"
+ "45434d3a45524f555445520004000d3242523232395534303034344300050004312e30"
+ "340006000856312e33332e303300070007322e332e3052320008000630303039354200"
+ "090009434733303030444352000a00074e6574676561720001000a0003000120e52ab8"
+ "1515001900292ab815150000000000000000001a001900000000000000003800000000"
+ "0000000000000000000000000003000c2ab81515000000000000000000080002000000"
+ "1100160000118b0402000620e52ab81514040100040102030000120004000000220025"
+ "000e0000101300015c228d41100001220035000a0003000100015c228d3d";
+
+ std::vector<uint8_t> bin;
+
+ // Decode the hex string and store it in bin (which happens
+ // to be OptionBuffer format)
+ isc::util::encode::decodeHex(hex_string, bin);
+
+ Pkt6Ptr pkt(new Pkt6(&bin[0], bin.size()));
+ captureSetDefaultFields(pkt);
+ return (pkt);
+}
+
}; // end of isc::test namespace
}; // end of isc namespace
diff --git a/src/lib/dhcp/docsis3_option_defs.h b/src/lib/dhcp/docsis3_option_defs.h
index 19bdaa0..6031f8d 100644
--- a/src/lib/dhcp/docsis3_option_defs.h
+++ b/src/lib/dhcp/docsis3_option_defs.h
@@ -18,8 +18,8 @@
#include <dhcp/std_option_defs.h>
#include <dhcp/option_data_types.h>
-
-namespace {
+namespace isc {
+namespace dhcp {
#define VENDOR_ID_CABLE_LABS 4491
@@ -61,6 +61,11 @@ const OptionDefParams DOCSIS3_V6_DEFS[] = {
/// Number of option definitions defined.
const int DOCSIS3_V6_DEFS_SIZE = sizeof(DOCSIS3_V6_DEFS) / sizeof(OptionDefParams);
-}; // anonymous namespace
+/// The class as specified in vendor-class option by the devices
+extern const char* DOCSIS3_CLASS_EROUTER;
+extern const char* DOCSIS3_CLASS_MODEM;
+
+}; // isc::dhcp namespace
+}; // isc namespace
#endif // DOCSIS3_OPTION_DEFS_H
diff --git a/src/lib/dhcp/libdhcp++.cc b/src/lib/dhcp/libdhcp++.cc
index 4577c64..f6ba978 100644
--- a/src/lib/dhcp/libdhcp++.cc
+++ b/src/lib/dhcp/libdhcp++.cc
@@ -52,6 +52,14 @@ VendorOptionDefContainers LibDHCP::vendor4_defs_;
VendorOptionDefContainers LibDHCP::vendor6_defs_;
+// Those two vendor classes are used for cable modems:
+
+/// DOCSIS3.0 compatible cable modem
+const char* isc::dhcp::DOCSIS3_CLASS_MODEM = "docsis3.0";
+
+/// DOCSIS3.0 cable modem that has router built-in
+const char* isc::dhcp::DOCSIS3_CLASS_EROUTER = "eRouter1.0";
+
// Let's keep it in .cc file. Moving it to .h would require including optionDefParams
// definitions there
void initOptionSpace(OptionDefContainer& defs,
diff --git a/src/lib/dhcp/option6_iaprefix.h b/src/lib/dhcp/option6_iaprefix.h
index 0a790be..63ab9f7 100644
--- a/src/lib/dhcp/option6_iaprefix.h
+++ b/src/lib/dhcp/option6_iaprefix.h
@@ -90,7 +90,8 @@ public:
/// sets address in this option.
///
- /// @param addr address to be sent in this option
+ /// @param prefix prefix to be sent in this option
+ /// @param length prefix length
void setPrefix(const isc::asiolink::IOAddress& prefix,
uint8_t length) { addr_ = prefix; prefix_len_ = length; }
diff --git a/src/lib/dhcp/option_vendor.h b/src/lib/dhcp/option_vendor.h
index 5b43508..bb8395c 100644
--- a/src/lib/dhcp/option_vendor.h
+++ b/src/lib/dhcp/option_vendor.h
@@ -25,6 +25,11 @@
namespace isc {
namespace dhcp {
+/// Indexes for fields in vendor-class (17) DHCPv6 option
+const int VENDOR_CLASS_ENTERPRISE_ID_INDEX = 0;
+const int VENDOR_CLASS_DATA_LEN_INDEX = 1;
+const int VENDOR_CLASS_STRING_INDEX = 2;
+
/// @brief This class represents vendor-specific information option.
///
/// As specified in RFC3925, the option formatting is slightly different
@@ -72,7 +77,7 @@ public:
/// @brief Sets enterprise identifier
///
- /// @param value vendor identifier
+ /// @param vendor_id vendor identifier
void setVendorId(const uint32_t vendor_id) { vendor_id_ = vendor_id; }
/// @brief Returns enterprise identifier
diff --git a/src/lib/dhcp/pkt4.cc b/src/lib/dhcp/pkt4.cc
index c1864b2..b641a03 100644
--- a/src/lib/dhcp/pkt4.cc
+++ b/src/lib/dhcp/pkt4.cc
@@ -480,7 +480,17 @@ Pkt4::isRelayed() const {
<< static_cast<int>(getHops()) << ". Valid values"
" are: (giaddr = 0 and hops = 0) or (giaddr != 0 and"
"hops != 0)");
+}
+
+bool Pkt4::inClass(const std::string& client_class) {
+ return (classes_.find(client_class) != classes_.end());
+}
+void
+Pkt4::addClass(const std::string& client_class) {
+ if (classes_.find(client_class) == classes_.end()) {
+ classes_.insert(client_class);
+ }
}
} // end of namespace isc::dhcp
diff --git a/src/lib/dhcp/pkt4.h b/src/lib/dhcp/pkt4.h
index 3d9e0ab..485d28a 100644
--- a/src/lib/dhcp/pkt4.h
+++ b/src/lib/dhcp/pkt4.h
@@ -26,6 +26,7 @@
#include <iostream>
#include <vector>
+#include <set>
#include <time.h>
@@ -52,6 +53,9 @@ public:
/// to check whether client requested broadcast response.
const static uint16_t FLAG_BROADCAST_MASK = 0x8000;
+ /// Container for storing client classes
+ typedef std::set<std::string> Classes;
+
/// Constructor, used in replying to a message.
///
/// @param msg_type type of message (e.g. DHCPDISOVER=1)
@@ -550,6 +554,37 @@ public:
/// performance).
std::vector<uint8_t> data_;
+ /// @brief Checks whether a client belongs to a given class
+ ///
+ /// @param client_class name of the class
+ /// @return true if belongs
+ bool inClass(const std::string& client_class);
+
+ /// @brief Adds packet to a specified class
+ ///
+ /// A packet can be added to the same class repeatedly. Any additional
+ /// attempts to add to a class the packet already belongs to, will be
+ /// ignored silently.
+ ///
+ /// @note It is a matter of naming convention. Conceptually, the server
+ /// processes a stream of packets, with some packets belonging to given
+ /// classes. From that perspective, this method adds a packet to specifed
+ /// class. Implementation wise, it looks the opposite - the class name
+ /// is added to the packet. Perhaps the most appropriate name for this
+ /// method would be associateWithClass()? But that seems overly long,
+ /// so I decided to stick with addClass().
+ ///
+ /// @param client_class name of the class to be added
+ void addClass(const std::string& client_class);
+
+ /// @brief Classes this packet belongs to.
+ ///
+ /// This field is public, so the code outside of Pkt4 class can iterate over
+ /// existing classes. Having it public also solves the problem of returned
+ /// reference lifetime. It is preferred to use @ref inClass and @ref addClass
+ /// should be used to operate on this field.
+ Classes classes_;
+
private:
/// @brief Generic method that validates and sets HW address.
diff --git a/src/lib/dhcp/pkt6.cc b/src/lib/dhcp/pkt6.cc
index 296a389..fdbd18c 100644
--- a/src/lib/dhcp/pkt6.cc
+++ b/src/lib/dhcp/pkt6.cc
@@ -586,6 +586,17 @@ void Pkt6::copyRelayInfo(const Pkt6Ptr& question) {
}
}
+bool
+Pkt6::inClass(const std::string& client_class) {
+ return (classes_.find(client_class) != classes_.end());
+}
+
+void
+Pkt6::addClass(const std::string& client_class) {
+ if (classes_.find(client_class) == classes_.end()) {
+ classes_.insert(client_class);
+ }
+}
} // end of isc::dhcp namespace
} // end of isc namespace
diff --git a/src/lib/dhcp/pkt6.h b/src/lib/dhcp/pkt6.h
index 702c424..db80fb9 100644
--- a/src/lib/dhcp/pkt6.h
+++ b/src/lib/dhcp/pkt6.h
@@ -23,6 +23,7 @@
#include <boost/shared_ptr.hpp>
#include <iostream>
+#include <set>
#include <time.h>
@@ -47,6 +48,9 @@ public:
TCP = 1 // there are TCP DHCPv6 packets (bulk leasequery, failover)
};
+ /// Container for storing client classes
+ typedef std::set<std::string> Classes;
+
/// @brief defines relay search pattern
///
/// Defines order in which options are searched in a message that
@@ -426,6 +430,35 @@ public:
/// data format change etc.
OptionBuffer data_;
+ /// @brief Checks whether a client belongs to a given class
+ ///
+ /// @param client_class name of the class
+ /// @return true if belongs
+ bool inClass(const std::string& client_class);
+
+ /// @brief Adds packet to a specified class
+ ///
+ /// A packet can be added to the same class repeatedly. Any additional
+ /// attempts to add to a class the packet already belongs to, will be
+ /// ignored silently.
+ ///
+ /// @note It is a matter of naming convention. Conceptually, the server
+ /// processes a stream of packets, with some packets belonging to given
+ /// classes. From that perspective, this method adds a packet to specifed
+ /// class. Implementation wise, it looks the opposite - the class name
+ /// is added to the packet. Perhaps the most appropriate name for this
+ /// method would be associateWithClass()? But that seems overly long,
+ /// so I decided to stick with addClass().
+ ///
+ /// @param client_class name of the class to be added
+ void addClass(const std::string& client_class);
+
+ /// @brief Classes this packet belongs to.
+ ///
+ /// This field is public, so code can iterate over existing classes.
+ /// Having it public also solves the problem of returned reference lifetime.
+ Classes classes_;
+
protected:
/// Builds on wire packet for TCP transmission.
///
diff --git a/src/lib/dhcp/std_option_defs.h b/src/lib/dhcp/std_option_defs.h
index 9f64553..6611f19 100644
--- a/src/lib/dhcp/std_option_defs.h
+++ b/src/lib/dhcp/std_option_defs.h
@@ -160,7 +160,7 @@ const OptionDefParams OPTION_DEF_PARAMS4[] = {
{ "dhcp-rebinding-time", DHO_DHCP_REBINDING_TIME,
OPT_UINT32_TYPE, false, NO_RECORD_DEF, "" },
{ "vendor-class-identifier", DHO_VENDOR_CLASS_IDENTIFIER,
- OPT_BINARY_TYPE, false, NO_RECORD_DEF, "" },
+ OPT_STRING_TYPE, false, NO_RECORD_DEF, "" },
{ "dhcp-client-identifier", DHO_DHCP_CLIENT_IDENTIFIER,
OPT_BINARY_TYPE, false, NO_RECORD_DEF, "" },
{ "nwip-domain-name", DHO_NWIP_DOMAIN_NAME, OPT_STRING_TYPE, false, NO_RECORD_DEF, "" },
@@ -230,7 +230,8 @@ RECORD_DECL(REMOTE_ID_RECORDS, OPT_UINT32_TYPE, OPT_BINARY_TYPE);
// status-code
RECORD_DECL(STATUS_CODE_RECORDS, OPT_UINT16_TYPE, OPT_STRING_TYPE);
// vendor-class
-RECORD_DECL(VENDOR_CLASS_RECORDS, OPT_UINT32_TYPE, OPT_BINARY_TYPE);
+RECORD_DECL(VENDOR_CLASS_RECORDS, OPT_UINT32_TYPE, OPT_UINT16_TYPE,
+ OPT_STRING_TYPE);
/// Standard DHCPv6 option definitions.
///
diff --git a/src/lib/dhcp/tests/libdhcp++_unittest.cc b/src/lib/dhcp/tests/libdhcp++_unittest.cc
index ec9dd9f..4d645a4 100644
--- a/src/lib/dhcp/tests/libdhcp++_unittest.cc
+++ b/src/lib/dhcp/tests/libdhcp++_unittest.cc
@@ -30,6 +30,9 @@
#include <dhcp/option_string.h>
#include <dhcp/option_vendor.h>
#include <util/buffer.h>
+#include <util/encode/hex.h>
+
+#include <boost/pointer_cast.hpp>
#include <gtest/gtest.h>
@@ -890,7 +893,7 @@ TEST_F(LibDhcpTest, stdOptionDefs4) {
typeid(OptionInt<uint32_t>));
LibDhcpTest::testStdOptionDefs4(DHO_VENDOR_CLASS_IDENTIFIER, begin, end,
- typeid(Option));
+ typeid(OptionString));
LibDhcpTest::testStdOptionDefs4(DHO_DHCP_CLIENT_IDENTIFIER, begin, end,
typeid(Option));
@@ -1122,4 +1125,45 @@ TEST_F(LibDhcpTest, stdOptionDefs6) {
typeid(Option6AddrLst));
}
+// tests whether v6 vendor-class option can be parsed properly.
+TEST_F(LibDhcpTest, vendorClass6) {
+
+ isc::dhcp::OptionCollection options; // Will store parsed option here
+
+ // Exported from wireshark: vendor-class option with enterprise-id = 4491
+ // and a single data entry containing "eRouter1.0"
+ string vendor_class_hex = "001000100000118b000a65526f75746572312e30";
+ OptionBuffer bin;
+
+ // Decode the hex string and store it in bin (which happens
+ // to be OptionBuffer format)
+ isc::util::encode::decodeHex(vendor_class_hex, bin);
+
+ ASSERT_NO_THROW ({
+ LibDHCP::unpackOptions6(bin, "dhcp6", options);
+ });
+
+ EXPECT_EQ(options.size(), 1); // There should be 1 option.
+
+ // Option vendor-class should be there
+ ASSERT_FALSE(options.find(D6O_VENDOR_CLASS) == options.end());
+
+ // It should be of type OptionCustom
+ boost::shared_ptr<OptionCustom> vclass =
+ boost::dynamic_pointer_cast<OptionCustom> (options.begin()->second);
+ ASSERT_TRUE(vclass);
+
+ // Let's investigate if the option content is correct
+
+ // 3 fields expected: vendor-id, data-len and data
+ ASSERT_EQ(3, vclass->getDataFieldsNum());
+
+ EXPECT_EQ(4491, vclass->readInteger<uint32_t>
+ (VENDOR_CLASS_ENTERPRISE_ID_INDEX)); // vendor-id=4491
+ EXPECT_EQ(10, vclass->readInteger<uint16_t>
+ (VENDOR_CLASS_DATA_LEN_INDEX)); // data len = 10
+ EXPECT_EQ("eRouter1.0", vclass->readString
+ (VENDOR_CLASS_STRING_INDEX)); // data="eRouter1.0"
}
+
+} // end of anonymous space
diff --git a/src/lib/dhcp/tests/pkt4_unittest.cc b/src/lib/dhcp/tests/pkt4_unittest.cc
index edff356..bfebe77 100644
--- a/src/lib/dhcp/tests/pkt4_unittest.cc
+++ b/src/lib/dhcp/tests/pkt4_unittest.cc
@@ -17,6 +17,7 @@
#include <asiolink/io_address.h>
#include <dhcp/dhcp4.h>
#include <dhcp/libdhcp++.h>
+#include <dhcp/docsis3_option_defs.h>
#include <dhcp/option_string.h>
#include <dhcp/pkt4.h>
#include <exceptions/exceptions.h>
@@ -818,7 +819,36 @@ TEST_F(Pkt4Test, isRelayed) {
// should throw an exception.
pkt.setGiaddr(IOAddress("0.0.0.0"));
EXPECT_THROW(pkt.isRelayed(), isc::BadValue);
+}
+// Tests whether a packet can be assigned to a class and later
+// checked if it belongs to a given class
+TEST_F(Pkt4Test, clientClasses) {
+ Pkt4 pkt(DHCPOFFER, 1234);
+
+ // Default values (do not belong to any class)
+ EXPECT_FALSE(pkt.inClass(DOCSIS3_CLASS_EROUTER));
+ EXPECT_FALSE(pkt.inClass(DOCSIS3_CLASS_MODEM));
+ EXPECT_TRUE(pkt.classes_.empty());
+
+ // Add to the first class
+ pkt.addClass(DOCSIS3_CLASS_EROUTER);
+ EXPECT_TRUE(pkt.inClass(DOCSIS3_CLASS_EROUTER));
+ EXPECT_FALSE(pkt.inClass(DOCSIS3_CLASS_MODEM));
+ ASSERT_FALSE(pkt.classes_.empty());
+
+ // Add to a second class
+ pkt.addClass(DOCSIS3_CLASS_MODEM);
+ EXPECT_TRUE(pkt.inClass(DOCSIS3_CLASS_EROUTER));
+ EXPECT_TRUE(pkt.inClass(DOCSIS3_CLASS_MODEM));
+
+ // Check that it's ok to add to the same class repeatedly
+ EXPECT_NO_THROW(pkt.addClass("foo"));
+ EXPECT_NO_THROW(pkt.addClass("foo"));
+ EXPECT_NO_THROW(pkt.addClass("foo"));
+
+ // Check that the packet belongs to 'foo'
+ EXPECT_TRUE(pkt.inClass("foo"));
}
} // end of anonymous namespace
diff --git a/src/lib/dhcp/tests/pkt6_unittest.cc b/src/lib/dhcp/tests/pkt6_unittest.cc
index e18b545..17cb629 100644
--- a/src/lib/dhcp/tests/pkt6_unittest.cc
+++ b/src/lib/dhcp/tests/pkt6_unittest.cc
@@ -22,6 +22,7 @@
#include <dhcp/option_int.h>
#include <dhcp/option_int_array.h>
#include <dhcp/pkt6.h>
+#include <dhcp/docsis3_option_defs.h>
#include <util/range_utilities.h>
#include <boost/bind.hpp>
@@ -779,4 +780,34 @@ TEST_F(Pkt6Test, getAnyRelayOption) {
EXPECT_FALSE(opt);
}
+// Tests whether a packet can be assigned to a class and later
+// checked if it belongs to a given class
+TEST_F(Pkt6Test, clientClasses) {
+ Pkt6 pkt(DHCPV6_ADVERTISE, 1234);
+
+ // Default values (do not belong to any class)
+ EXPECT_FALSE(pkt.inClass(DOCSIS3_CLASS_EROUTER));
+ EXPECT_FALSE(pkt.inClass(DOCSIS3_CLASS_MODEM));
+ EXPECT_TRUE(pkt.classes_.empty());
+
+ // Add to the first class
+ pkt.addClass(DOCSIS3_CLASS_EROUTER);
+ EXPECT_TRUE(pkt.inClass(DOCSIS3_CLASS_EROUTER));
+ EXPECT_FALSE(pkt.inClass(DOCSIS3_CLASS_MODEM));
+ ASSERT_FALSE(pkt.classes_.empty());
+
+ // Add to a second class
+ pkt.addClass(DOCSIS3_CLASS_MODEM);
+ EXPECT_TRUE(pkt.inClass(DOCSIS3_CLASS_EROUTER));
+ EXPECT_TRUE(pkt.inClass(DOCSIS3_CLASS_MODEM));
+
+ // Check that it's ok to add to the same class repeatedly
+ EXPECT_NO_THROW(pkt.addClass("foo"));
+ EXPECT_NO_THROW(pkt.addClass("foo"));
+ EXPECT_NO_THROW(pkt.addClass("foo"));
+
+ // Check that the packet belongs to 'foo'
+ EXPECT_TRUE(pkt.inClass("foo"));
+}
+
}
diff --git a/src/lib/dhcpsrv/dbaccess_parser.h b/src/lib/dhcpsrv/dbaccess_parser.h
index 53e3f81..35d72ff 100644
--- a/src/lib/dhcpsrv/dbaccess_parser.h
+++ b/src/lib/dhcpsrv/dbaccess_parser.h
@@ -94,7 +94,7 @@ public:
///
/// Creates an instance of this parser.
///
- /// @param name Name of the parameter used to access the configuration.
+ /// @param param_name Name of the parameter used to access the configuration.
///
/// @return Pointer to a DbAccessParser. The caller is responsible for
/// destroying the parser after use.
diff --git a/src/lib/dhcpsrv/lease_mgr.h b/src/lib/dhcpsrv/lease_mgr.h
index 9355736..aab7b9e 100644
--- a/src/lib/dhcpsrv/lease_mgr.h
+++ b/src/lib/dhcpsrv/lease_mgr.h
@@ -214,7 +214,7 @@ public:
/// @param subnet_id A subnet identifier.
///
/// @return A pointer to the lease or NULL if the lease is not found.
- virtual Lease4Ptr getLease4(const ClientId& clientid, const HWAddr& hwaddr,
+ virtual Lease4Ptr getLease4(const ClientId& client_id, const HWAddr& hwaddr,
SubnetID subnet_id) const = 0;
/// @brief Returns existing IPv4 lease for specified client-id
More information about the bind10-changes
mailing list