2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-08-29 13:07:50 +00:00

[4626] Changes after review

- Dhcp4Srv::vendorClassSpecificProcessing removed
 - User's Guide updated
 - disabled obsolete test (will need to be rewritten to take advantage
   of new classification code)
 - classes definitions now use strings for server-name and filename
 - unused configuration removed from unit-tests
 - textFixedFields is now documented
 - Unit-tests now have short descriptions
This commit is contained in:
Tomek Mrugalski 2016-08-25 16:44:55 +02:00
parent 07e080c8cb
commit ada652bab5
11 changed files with 136 additions and 244 deletions

View File

@ -1745,12 +1745,11 @@ It is merely echoed by the server
</para> </para>
<para> <para>
For clients that belong to the VENDOR_CLASS_docsis3.0 class, the siaddr Note: Kea 1.0 and earlier versions performed special actions for
field is set to the value of next-server (if specified in a subnet). If clients that were in VENDOR_CLASS_docsis3.0. This is no longer the
there is a boot-file-name option specified, its value is also set in the case in Kea 1.1 and newer. In those newer versions the old behavior
file field in the DHCPv4 packet. For eRouter1.0 class, the siaddr is can be achieved by defining VENDOR_CLASS_docsis3.0 and setting
always set to 0.0.0.0. That capability is expected to be moved to its next-server and boot-file-name values appropriately.
an external hook library that will be dedicated to cable modems.
</para> </para>
<para> <para>

View File

@ -233,11 +233,6 @@ information. The second and third argument contains the packet name
and type respectively. The fourth argument contains detailed packet and type respectively. The fourth argument contains detailed packet
information. information.
% DHCP4_DISCOVER_CLASS_PROCESSING_FAILED %1: client class specific processing failed for DHCPDISCOVER
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.
The argument holds the client and transaction identification information.
% DHCP4_DYNAMIC_RECONFIGURATION initiate server reconfiguration using file: %1, after receiving SIGHUP signal % DHCP4_DYNAMIC_RECONFIGURATION initiate server reconfiguration using file: %1, after receiving SIGHUP signal
This is the info message logged when the DHCPv4 server starts reconfiguration This is the info message logged when the DHCPv4 server starts reconfiguration
as a result of receiving SIGHUP signal. as a result of receiving SIGHUP signal.
@ -314,12 +309,6 @@ will be only able to offer global options - no addresses will be assigned.
The argument specifies the client and transaction identification The argument specifies the client and transaction identification
information. information.
% DHCP4_INFORM_CLASS_PROCESSING_FAILED %1: client class specific processing failed for DHCPINFORM
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.
The argument specifies the client and the transaction identification
information.
% DHCP4_INFORM_DIRECT_REPLY %1: DHCPACK in reply to the DHCPINFORM will be sent directly to %2 over %3 % DHCP4_INFORM_DIRECT_REPLY %1: DHCPACK in reply to the DHCPINFORM will be sent directly to %2 over %3
This debug message is issued when the DHCPACK will be sent directly to the This debug message is issued when the DHCPACK will be sent directly to the
client, rather than via a relay. The first argument contains the client client, rather than via a relay. The first argument contains the client
@ -615,12 +604,6 @@ and the lease. The first argument includes the client and the
transaction identification information. The second argument specifies transaction identification information. The second argument specifies
the leased address. the leased address.
% DHCP4_REQUEST_CLASS_PROCESSING_FAILED %1: client class specific processing failed for DHCPREQUEST
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.
The argument contains the client and transaction identification
information.
% DHCP4_RESPONSE_DATA %1: responding with packet %2 (type %3), packet details: %4 % DHCP4_RESPONSE_DATA %1: responding with packet %2 (type %3), packet details: %4
A debug message including the detailed data about the packet being sent A debug message including the detailed data about the packet being sent
to the client. The first argument contains the client and the transaction to the client. The first argument contains the client and the transaction

View File

@ -1964,14 +1964,26 @@ Dhcpv4Srv::setFixedFields(Dhcpv4Exchange& ex) {
response->setSiaddr(next_server); response->setSiaddr(next_server);
} }
const vector<uint8_t>& sname = cl->second->getSname(); const string& sname = cl->second->getSname();
if (!sname.empty()) { if (!sname.empty()) {
response->setSname(&sname[0], sname.size()); // Converting string to (const uint8_t*, size_t len) format is
// tricky. reineterpret_cast is not the most elegant solution,
// but it does avoid us making unnecessary copy. We will convert
// sname and file fields in Pkt4 to string one day and live
// will be easier.
response->setSname(reinterpret_cast<const uint8_t*>(sname.c_str()),
sname.size());
} }
const vector<uint8_t>& filename = cl->second->getFilename(); const string& filename = cl->second->getFilename();
if (!filename.empty()) { if (!filename.empty()) {
response->setFile(&filename[0], filename.size()); // Converting string to (const uint8_t*, size_t len) format is
// tricky. reineterpret_cast is not the most elegant solution,
// but it does avoid us making unnecessary copy. We will convert
// sname and file fields in Pkt4 to string one day and live
// will be easier.
response->setFile(reinterpret_cast<const uint8_t*>(filename.c_str()),
filename.size());
} }
} }
@ -2021,8 +2033,8 @@ Dhcpv4Srv::processDiscover(Pkt4Ptr& discover) {
// them we append them for him. // them we append them for him.
appendBasicOptions(ex); appendBasicOptions(ex);
// See if the class mandates setting any fixed fields (siaddr, sname, // Set fixed fields (siaddr, sname, filename) if defined in
// filename). // the reservation, class or subnet specific configuration.
setFixedFields(ex); setFixedFields(ex);
} else { } else {
@ -2037,14 +2049,6 @@ Dhcpv4Srv::processDiscover(Pkt4Ptr& discover) {
appendServerID(ex); appendServerID(ex);
/// @todo: decide whether we want to add a new hook point for
/// doing class specific processing.
if (!vendorClassSpecificProcessing(ex)) {
/// @todo add more verbosity here
LOG_DEBUG(options4_logger, DBG_DHCP4_DETAIL, DHCP4_DISCOVER_CLASS_PROCESSING_FAILED)
.arg(discover->getLabel());
}
return (ex.getResponse()); return (ex.getResponse());
} }
@ -2081,8 +2085,8 @@ Dhcpv4Srv::processRequest(Pkt4Ptr& request) {
// them we append them for him. // them we append them for him.
appendBasicOptions(ex); appendBasicOptions(ex);
// See if the class mandates setting any fixed fields (siaddr, sname, // Set fixed fields (siaddr, sname, filename) if defined in
// filename). // the reservation, class or subnet specific configuration.
setFixedFields(ex); setFixedFields(ex);
} }
@ -2092,14 +2096,6 @@ Dhcpv4Srv::processRequest(Pkt4Ptr& request) {
appendServerID(ex); appendServerID(ex);
/// @todo: decide whether we want to add a new hook point for
/// doing class specific processing.
if (!vendorClassSpecificProcessing(ex)) {
/// @todo add more verbosity here
LOG_DEBUG(options4_logger, DBG_DHCP4_DETAIL, DHCP4_REQUEST_CLASS_PROCESSING_FAILED)
.arg(ex.getQuery()->getLabel());
}
return (ex.getResponse()); return (ex.getResponse());
} }
@ -2369,8 +2365,8 @@ Dhcpv4Srv::processInform(Pkt4Ptr& inform) {
appendBasicOptions(ex); appendBasicOptions(ex);
adjustIfaceData(ex); adjustIfaceData(ex);
// See if the class mandates setting any fixed fields (siaddr, sname, // Set fixed fields (siaddr, sname, filename) if defined in
// filename). // the reservation, class or subnet specific configuration.
setFixedFields(ex); setFixedFields(ex);
// There are cases for the DHCPINFORM that the server receives it via // There are cases for the DHCPINFORM that the server receives it via
@ -2391,14 +2387,6 @@ Dhcpv4Srv::processInform(Pkt4Ptr& inform) {
// The DHCPACK must contain server id. // The DHCPACK must contain server id.
appendServerID(ex); appendServerID(ex);
/// @todo: decide whether we want to add a new hook point for
/// doing class specific processing.
if (!vendorClassSpecificProcessing(ex)) {
LOG_DEBUG(options4_logger, DBG_DHCP4_DETAIL,
DHCP4_INFORM_CLASS_PROCESSING_FAILED)
.arg(inform->getLabel());
}
return (ex.getResponse()); return (ex.getResponse());
} }
@ -2622,17 +2610,8 @@ void Dhcpv4Srv::classifyByVendor(const Pkt4Ptr& pkt, std::string& classes) {
// is indeed a modem, John B. suggested to check whether chaddr field // is indeed a modem, John B. suggested to check whether chaddr field
// quals subscriber-id option that was inserted by the relay (CMTS). // quals subscriber-id option that was inserted by the relay (CMTS).
// This kind of logic will appear here soon. // This kind of logic will appear here soon.
if (vendor_class->getValue().find(DOCSIS3_CLASS_MODEM) != std::string::npos) { pkt->addClass(VENDOR_CLASS_PREFIX + vendor_class->getValue());
pkt->addClass(VENDOR_CLASS_PREFIX + DOCSIS3_CLASS_MODEM); classes += VENDOR_CLASS_PREFIX + vendor_class->getValue();
classes += string(VENDOR_CLASS_PREFIX + DOCSIS3_CLASS_MODEM) + " ";
} else
if (vendor_class->getValue().find(DOCSIS3_CLASS_EROUTER) != std::string::npos) {
pkt->addClass(VENDOR_CLASS_PREFIX + DOCSIS3_CLASS_EROUTER);
classes += string(VENDOR_CLASS_PREFIX + DOCSIS3_CLASS_EROUTER) + " ";
} else {
pkt->addClass(VENDOR_CLASS_PREFIX + vendor_class->getValue());
classes += VENDOR_CLASS_PREFIX + vendor_class->getValue();
}
} }
void Dhcpv4Srv::classifyPacket(const Pkt4Ptr& pkt) { void Dhcpv4Srv::classifyPacket(const Pkt4Ptr& pkt) {
@ -2687,52 +2666,6 @@ void Dhcpv4Srv::classifyPacket(const Pkt4Ptr& pkt) {
} }
} }
bool
Dhcpv4Srv::vendorClassSpecificProcessing(const Dhcpv4Exchange& ex) {
Subnet4Ptr subnet = ex.getContext()->subnet_;
Pkt4Ptr query = ex.getQuery();
Pkt4Ptr rsp = ex.getResponse();
// If any of those is missing, there is nothing to do.
if (!subnet || !query || !rsp) {
return (true);
}
if (query->inClass(VENDOR_CLASS_PREFIX + 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.
const CfgOptionList& co_list = ex.getCfgOptionList();
for (CfgOptionList::const_iterator copts = co_list.begin();
copts != co_list.end(); ++copts) {
OptionDescriptor desc = (*copts)->get("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());
break;
}
}
}
}
if (query->inClass(VENDOR_CLASS_PREFIX + DOCSIS3_CLASS_EROUTER)) {
// Do not set TFTP server address for eRouter devices.
rsp->setSiaddr(IOAddress::IPV4_ZERO_ADDRESS());
}
return (true);
}
void void
Dhcpv4Srv::startD2() { Dhcpv4Srv::startD2() {
D2ClientMgr& d2_mgr = CfgMgr::instance().getD2ClientMgr(); D2ClientMgr& d2_mgr = CfgMgr::instance().getD2ClientMgr();

View File

@ -517,8 +517,10 @@ protected:
/// ///
/// If the incoming packets belongs to a class and that class defines /// If the incoming packets belongs to a class and that class defines
/// next-server, server-hostname or boot-file-name, we need to set the /// next-server, server-hostname or boot-file-name, we need to set the
/// siaddr, sname or filename fields in the outgoing packet. This /// siaddr, sname or filename fields in the outgoing packet. Also, those
/// is what this method does. /// values can be defined for subnet or in reservations. The values
/// defined in reservation takes precedence over class values, which
/// in turn take precedence over subnet values.
/// ///
/// @param ex DHCPv4 exchange holding the client's message and the server's /// @param ex DHCPv4 exchange holding the client's message and the server's
/// response to be adjusted. /// response to be adjusted.
@ -763,18 +765,6 @@ protected:
/// @param pkt packet to be classified /// @param pkt packet to be classified
void classifyPacket(const Pkt4Ptr& pkt); void classifyPacket(const Pkt4Ptr& pkt);
/// @brief Performs packet processing specific to a vendor class
///
/// If the selected subnet, query or response in the @c ex object is NULL
/// this method returns immediately and returns true.
///
/// @note This processing is a likely candidate to be pushed into hooks.
///
/// @param ex The exchange holding both the client's message and the
/// server's response.
/// @return true if successful, false otherwise (will prevent sending response)
bool vendorClassSpecificProcessing(const Dhcpv4Exchange& ex);
/// @brief Allocation Engine. /// @brief Allocation Engine.
/// Pointer to the allocation engine that we are currently using /// Pointer to the allocation engine that we are currently using
/// It must be a pointer, because we will support changing engines /// It must be a pointer, because we will support changing engines

View File

@ -20,54 +20,19 @@ using namespace std;
namespace { namespace {
/// @brief Set of JSON configurations used throughout the DORA tests. /// @brief Set of JSON configurations used throughout the classify tests.
/// ///
/// - Configuration 0: /// - Configuration 0:
/// - Used for testing direct traffic /// - Used for testing direct traffic
/// - 1 subnet: 10.0.0.0/24 /// - 1 subnet: 10.0.0.0/24
/// - 1 pool: 10.0.0.10-10.0.0.100 /// - 1 pool: 10.0.0.10-10.0.0.100
/// - Router option present: 10.0.0.200 and 10.0.0.201 /// - the following classes defined:
/// - Domain Name Server option present: 10.0.0.202, 10.0.0.203.
/// - Log Servers option present: 192.0.2.200 and 192.0.2.201
/// - Quotes Servers option present: 192.0.2.202, 192.0.2.203.
/// - no classes
///
/// - Configuration 1:
/// - The same as configuration 0, but has the following classes defined:
/// option[93].hex == 0x0009, next-server set to 1.2.3.4 /// option[93].hex == 0x0009, next-server set to 1.2.3.4
/// option[93].hex == 0x0007, set server-hostname to deneb /// option[93].hex == 0x0007, set server-hostname to deneb
/// option[93].hex == 0x0006, set boot-file-name to pxelinux.0 /// option[93].hex == 0x0006, set boot-file-name to pxelinux.0
/// option[93].hex == 0x0001, set boot-file-name to ipxe.efi /// option[93].hex == 0x0001, set boot-file-name to ipxe.efi
const char* CONFIGS[] = { const char* CONFIGS[] = {
// Configuration 0 // Configuration 0
"{ \"interfaces-config\": {"
" \"interfaces\": [ \"*\" ]"
"},"
"\"valid-lifetime\": 600,"
"\"subnet4\": [ { "
" \"subnet\": \"10.0.0.0/24\", "
" \"id\": 1,"
" \"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ],"
" \"option-data\": [ {"
" \"name\": \"routers\","
" \"data\": \"10.0.0.200,10.0.0.201\""
" },"
" {"
" \"name\": \"domain-name-servers\","
" \"data\": \"10.0.0.202,10.0.0.203\""
" },"
" {"
" \"name\": \"log-servers\","
" \"data\": \"10.0.0.200,10.0.0.201\""
" },"
" {"
" \"name\": \"cookie-servers\","
" \"data\": \"10.0.0.202,10.0.0.203\""
" } ]"
" } ]"
"}",
// Configuration 6
"{ \"interfaces-config\": {" "{ \"interfaces-config\": {"
" \"interfaces\": [ \"*\" ]" " \"interfaces\": [ \"*\" ]"
"}," "},"
@ -124,6 +89,19 @@ public:
~ClassifyTest() { ~ClassifyTest() {
} }
/// @brief Does client exchanges and checks if fixed fields have expected values.
///
/// Depending on the value of msgtype (allowed types: DHCPDISCOVER, DHCPREQUEST or
/// DHCPINFORM), this method sets up the server, then conducts specified exchange
/// and then checks if the response contains expected values of next-server, sname
/// and filename fields.
///
/// @param config server configuration to be used
/// @param msgtype DHCPDISCOVER, DHCPREQUEST or DHCPINFORM
/// @param extra_opt option to include in client messages (optional)
/// @param exp_next_server expected value of the next-server field
/// @param exp_sname expected value of the sname field
/// @param exp_filename expected value of the filename field
void void
testFixedFields(const char* config, uint8_t msgtype, const OptionPtr& extra_opt, testFixedFields(const char* config, uint8_t msgtype, const OptionPtr& extra_opt,
const std::string& exp_next_server, const std::string& exp_sname, const std::string& exp_next_server, const std::string& exp_sname,
@ -181,17 +159,17 @@ public:
// This test checks that an incoming DISCOVER that does not match any classes // This test checks that an incoming DISCOVER that does not match any classes
// will get the fixed fields empty. // will get the fixed fields empty.
TEST_F(ClassifyTest, fixedFieldsDiscoverNoClasses) { TEST_F(ClassifyTest, fixedFieldsDiscoverNoClasses) {
testFixedFields(CONFIGS[1], DHCPDISCOVER, OptionPtr(), "0.0.0.0", "", ""); testFixedFields(CONFIGS[0], DHCPDISCOVER, OptionPtr(), "0.0.0.0", "", "");
} }
// This test checks that an incoming REQUEST that does not match any classes // This test checks that an incoming REQUEST that does not match any classes
// will get the fixed fields empty. // will get the fixed fields empty.
TEST_F(ClassifyTest, fixedFieldsRequestNoClasses) { TEST_F(ClassifyTest, fixedFieldsRequestNoClasses) {
testFixedFields(CONFIGS[1], DHCPREQUEST, OptionPtr(), "0.0.0.0", "", ""); testFixedFields(CONFIGS[0], DHCPREQUEST, OptionPtr(), "0.0.0.0", "", "");
} }
// This test checks that an incoming INFORM that does not match any classes // This test checks that an incoming INFORM that does not match any classes
// will get the fixed fields empty. // will get the fixed fields empty.
TEST_F(ClassifyTest, fixedFieldsInformNoClasses) { TEST_F(ClassifyTest, fixedFieldsInformNoClasses) {
testFixedFields(CONFIGS[1], DHCPINFORM, OptionPtr(), "0.0.0.0", "", ""); testFixedFields(CONFIGS[0], DHCPINFORM, OptionPtr(), "0.0.0.0", "", "");
} }
@ -200,21 +178,21 @@ TEST_F(ClassifyTest, fixedFieldsInformNoClasses) {
TEST_F(ClassifyTest, fixedFieldsDiscoverNextServer) { TEST_F(ClassifyTest, fixedFieldsDiscoverNextServer) {
OptionPtr pxe(new OptionInt<uint16_t>(Option::V4, 93, 0x0009)); OptionPtr pxe(new OptionInt<uint16_t>(Option::V4, 93, 0x0009));
testFixedFields(CONFIGS[1], DHCPDISCOVER, pxe, "1.2.3.4", "", ""); testFixedFields(CONFIGS[0], DHCPDISCOVER, pxe, "1.2.3.4", "", "");
} }
// This test checks that an incoming REQUEST that does match a class that has // This test checks that an incoming REQUEST that does match a class that has
// next-server specified will result in a response that has the next-server set. // next-server specified will result in a response that has the next-server set.
TEST_F(ClassifyTest, fixedFieldsRequestNextServer) { TEST_F(ClassifyTest, fixedFieldsRequestNextServer) {
OptionPtr pxe(new OptionInt<uint16_t>(Option::V4, 93, 0x0009)); OptionPtr pxe(new OptionInt<uint16_t>(Option::V4, 93, 0x0009));
testFixedFields(CONFIGS[1], DHCPREQUEST, pxe, "1.2.3.4", "", ""); testFixedFields(CONFIGS[0], DHCPREQUEST, pxe, "1.2.3.4", "", "");
} }
// This test checks that an incoming INFORM that does match a class that has // This test checks that an incoming INFORM that does match a class that has
// next-server specified will result in a response that has the next-server set. // next-server specified will result in a response that has the next-server set.
TEST_F(ClassifyTest, fixedFieldsInformNextServer) { TEST_F(ClassifyTest, fixedFieldsInformNextServer) {
OptionPtr pxe(new OptionInt<uint16_t>(Option::V4, 93, 0x0009)); OptionPtr pxe(new OptionInt<uint16_t>(Option::V4, 93, 0x0009));
testFixedFields(CONFIGS[1], DHCPINFORM, pxe, "1.2.3.4", "", ""); testFixedFields(CONFIGS[0], DHCPINFORM, pxe, "1.2.3.4", "", "");
} }
@ -223,21 +201,21 @@ TEST_F(ClassifyTest, fixedFieldsInformNextServer) {
TEST_F(ClassifyTest, fixedFieldsDiscoverHostname) { TEST_F(ClassifyTest, fixedFieldsDiscoverHostname) {
OptionPtr pxe(new OptionInt<uint16_t>(Option::V4, 93, 0x0007)); OptionPtr pxe(new OptionInt<uint16_t>(Option::V4, 93, 0x0007));
testFixedFields(CONFIGS[1], DHCPDISCOVER, pxe, "0.0.0.0", "deneb", ""); testFixedFields(CONFIGS[0], DHCPDISCOVER, pxe, "0.0.0.0", "deneb", "");
} }
// This test checks that an incoming REQUEST that does match a class that has // This test checks that an incoming REQUEST that does match a class that has
// server-hostname specified will result in a response that has the sname field set. // server-hostname specified will result in a response that has the sname field set.
TEST_F(ClassifyTest, fixedFieldsRequestHostname) { TEST_F(ClassifyTest, fixedFieldsRequestHostname) {
OptionPtr pxe(new OptionInt<uint16_t>(Option::V4, 93, 0x0007)); OptionPtr pxe(new OptionInt<uint16_t>(Option::V4, 93, 0x0007));
testFixedFields(CONFIGS[1], DHCPREQUEST, pxe, "0.0.0.0", "deneb", ""); testFixedFields(CONFIGS[0], DHCPREQUEST, pxe, "0.0.0.0", "deneb", "");
} }
// This test checks that an incoming INFORM that does match a class that has // This test checks that an incoming INFORM that does match a class that has
// server-hostname specified will result in a response that has the sname field set. // server-hostname specified will result in a response that has the sname field set.
TEST_F(ClassifyTest, fixedFieldsInformHostname) { TEST_F(ClassifyTest, fixedFieldsInformHostname) {
OptionPtr pxe(new OptionInt<uint16_t>(Option::V4, 93, 0x0007)); OptionPtr pxe(new OptionInt<uint16_t>(Option::V4, 93, 0x0007));
testFixedFields(CONFIGS[1], DHCPINFORM, pxe, "0.0.0.0", "deneb", ""); testFixedFields(CONFIGS[0], DHCPINFORM, pxe, "0.0.0.0", "deneb", "");
} }
@ -246,21 +224,21 @@ TEST_F(ClassifyTest, fixedFieldsInformHostname) {
TEST_F(ClassifyTest, fixedFieldsDiscoverFile1) { TEST_F(ClassifyTest, fixedFieldsDiscoverFile1) {
OptionPtr pxe(new OptionInt<uint16_t>(Option::V4, 93, 0x0006)); OptionPtr pxe(new OptionInt<uint16_t>(Option::V4, 93, 0x0006));
testFixedFields(CONFIGS[1], DHCPDISCOVER, pxe, "0.0.0.0", "", "pxelinux.0"); testFixedFields(CONFIGS[0], DHCPDISCOVER, pxe, "0.0.0.0", "", "pxelinux.0");
} }
// This test checks that an incoming REQUEST that does match a class that has // This test checks that an incoming REQUEST that does match a class that has
// boot-file-name specified will result in a response that has the filename field set. // boot-file-name specified will result in a response that has the filename field set.
TEST_F(ClassifyTest, fixedFieldsRequestFile1) { TEST_F(ClassifyTest, fixedFieldsRequestFile1) {
OptionPtr pxe(new OptionInt<uint16_t>(Option::V4, 93, 0x0006)); OptionPtr pxe(new OptionInt<uint16_t>(Option::V4, 93, 0x0006));
testFixedFields(CONFIGS[1], DHCPREQUEST, pxe, "0.0.0.0", "", "pxelinux.0"); testFixedFields(CONFIGS[0], DHCPREQUEST, pxe, "0.0.0.0", "", "pxelinux.0");
} }
// This test checks that an incoming INFORM that does match a class that has // This test checks that an incoming INFORM that does match a class that has
// boot-file-name specified will result in a response that has the filename field set. // boot-file-name specified will result in a response that has the filename field set.
TEST_F(ClassifyTest, fixedFieldsInformFile1) { TEST_F(ClassifyTest, fixedFieldsInformFile1) {
OptionPtr pxe(new OptionInt<uint16_t>(Option::V4, 93, 0x0006)); OptionPtr pxe(new OptionInt<uint16_t>(Option::V4, 93, 0x0006));
testFixedFields(CONFIGS[1], DHCPDISCOVER, pxe, "0.0.0.0", "", "pxelinux.0"); testFixedFields(CONFIGS[0], DHCPDISCOVER, pxe, "0.0.0.0", "", "pxelinux.0");
} }
@ -269,21 +247,21 @@ TEST_F(ClassifyTest, fixedFieldsInformFile1) {
TEST_F(ClassifyTest, fixedFieldsDiscoverFile2) { TEST_F(ClassifyTest, fixedFieldsDiscoverFile2) {
OptionPtr pxe(new OptionInt<uint16_t>(Option::V4, 93, 0x0001)); OptionPtr pxe(new OptionInt<uint16_t>(Option::V4, 93, 0x0001));
testFixedFields(CONFIGS[1], DHCPDISCOVER, pxe, "0.0.0.0", "", "ipxe.efi"); testFixedFields(CONFIGS[0], DHCPDISCOVER, pxe, "0.0.0.0", "", "ipxe.efi");
} }
// This test checks that an incoming REQUEST that does match a different class that has // This test checks that an incoming REQUEST that does match a different class that has
// boot-file-name specified will result in a response that has the filename field set. // boot-file-name specified will result in a response that has the filename field set.
TEST_F(ClassifyTest, fixedFieldsRequestFile2) { TEST_F(ClassifyTest, fixedFieldsRequestFile2) {
OptionPtr pxe(new OptionInt<uint16_t>(Option::V4, 93, 0x0001)); OptionPtr pxe(new OptionInt<uint16_t>(Option::V4, 93, 0x0001));
testFixedFields(CONFIGS[1], DHCPREQUEST, pxe, "0.0.0.0", "", "ipxe.efi"); testFixedFields(CONFIGS[0], DHCPREQUEST, pxe, "0.0.0.0", "", "ipxe.efi");
} }
// This test checks that an incoming INFORM that does match a different class that has // This test checks that an incoming INFORM that does match a different class that has
// boot-file-name specified will result in a response that has the filename field set. // boot-file-name specified will result in a response that has the filename field set.
TEST_F(ClassifyTest, fixedFieldsInformFile2) { TEST_F(ClassifyTest, fixedFieldsInformFile2) {
OptionPtr pxe(new OptionInt<uint16_t>(Option::V4, 93, 0x0001)); OptionPtr pxe(new OptionInt<uint16_t>(Option::V4, 93, 0x0001));
testFixedFields(CONFIGS[1], DHCPINFORM, pxe, "0.0.0.0", "", "ipxe.efi"); testFixedFields(CONFIGS[0], DHCPINFORM, pxe, "0.0.0.0", "", "ipxe.efi");
} }

View File

@ -1486,8 +1486,13 @@ TEST_F(Dhcpv4SrvTest, vendorOptionsDocsisDefinitions) {
ASSERT_EQ(0, rcode_); ASSERT_EQ(0, rcode_);
} }
// Checks if DOCSIS client packets are classified properly /// Checks if DOCSIS client packets are classified properly
TEST_F(Dhcpv4SrvTest, docsisClientClassification) { ///
/// @todo: With the change in #4626 the vendorClassSpecificProcessing
/// code was removed and replaced with generic classification. One day
/// we should rewrite this test to use classes. It would check that the
/// classification system can be used for docsis packets.
TEST_F(Dhcpv4SrvTest, DISABLED_docsisClientClassification) {
NakedDhcpv4Srv srv(0); NakedDhcpv4Srv srv(0);

View File

@ -1,4 +1,4 @@
// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC") // Copyright (C) 2015-2016 Internet Systems Consortium, Inc. ("ISC")
// //
// This Source Code Form is subject to the terms of the Mozilla Public // 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 // License, v. 2.0. If a copy of the MPL was not distributed with this
@ -16,7 +16,7 @@ ClientClassDef::ClientClassDef(const std::string& name,
const ExpressionPtr& match_expr, const ExpressionPtr& match_expr,
const CfgOptionPtr& cfg_option) const CfgOptionPtr& cfg_option)
: name_(name), match_expr_(match_expr), cfg_option_(cfg_option), : name_(name), match_expr_(match_expr), cfg_option_(cfg_option),
next_server_(asiolink::IOAddress("0.0.0.0")) { next_server_(asiolink::IOAddress::IPV4_ZERO_ADDRESS()) {
// Name can't be blank // Name can't be blank
if (name_.empty()) { if (name_.empty()) {
@ -34,7 +34,8 @@ ClientClassDef::ClientClassDef(const std::string& name,
ClientClassDef::ClientClassDef(const ClientClassDef& rhs) ClientClassDef::ClientClassDef(const ClientClassDef& rhs)
: name_(rhs.name_), match_expr_(ExpressionPtr()), : name_(rhs.name_), match_expr_(ExpressionPtr()),
cfg_option_(new CfgOption()), next_server_(asiolink::IOAddress("0.0.0.0")) { cfg_option_(new CfgOption()),
next_server_(asiolink::IOAddress::IPV4_ZERO_ADDRESS()) {
if (rhs.match_expr_) { if (rhs.match_expr_) {
match_expr_.reset(new Expression()); match_expr_.reset(new Expression());
@ -124,8 +125,8 @@ ClientClassDictionary::addClass(const std::string& name,
const ExpressionPtr& match_expr, const ExpressionPtr& match_expr,
const CfgOptionPtr& cfg_option, const CfgOptionPtr& cfg_option,
asiolink::IOAddress next_server, asiolink::IOAddress next_server,
const std::vector<uint8_t>& sname, const std::string& sname,
const std::vector<uint8_t>& filename) { const std::string& filename) {
ClientClassDefPtr cclass(new ClientClassDef(name, match_expr, cfg_option)); ClientClassDefPtr cclass(new ClientClassDef(name, match_expr, cfg_option));
cclass->setNextServer(next_server); cclass->setNextServer(next_server);
cclass->setSname(sname); cclass->setSname(sname);

View File

@ -1,4 +1,4 @@
// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC") // Copyright (C) 2015-2016 Internet Systems Consortium, Inc. ("ISC")
// //
// This Source Code Form is subject to the terms of the Mozilla Public // 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 // License, v. 2.0. If a copy of the MPL was not distributed with this
@ -108,7 +108,7 @@ public:
/// @brief returns next-server value /// @brief returns next-server value
/// @return next-server value /// @return next-server value
asiolink::IOAddress getNextServer() const { const asiolink::IOAddress& getNextServer() const {
return (next_server_); return (next_server_);
} }
@ -122,26 +122,26 @@ public:
/// @brief sets the server-name value /// @brief sets the server-name value
/// ///
/// @param sname the value to be set /// @param sname the value to be set
void setSname(const std::vector<uint8_t>& sname) { void setSname(const std::string& sname) {
sname_ = sname; sname_ = sname;
} }
/// @brief sets the boot-file-name value /// @brief sets the boot-file-name value
/// ///
/// @param filename the value to be set /// @param filename the value to be set
void setFilename(const std::vector<uint8_t>& filename) { void setFilename(const std::string& filename) {
filename_ = filename; filename_ = filename;
} }
/// @brief returns server-hostname value /// @brief returns server-hostname value
/// @return the vector that contains server-hostname (may be empty if not defined) /// @return the vector that contains server-hostname (may be empty if not defined)
const std::vector<uint8_t>& getSname() const { const std::string& getSname() const {
return (sname_); return (sname_);
} }
/// @brief returns boot-file-name value /// @brief returns boot-file-name value
/// @return the vector that contains boot-file-name (may be empty if not defined) /// @return the vector that contains boot-file-name (may be empty if not defined)
const std::vector<uint8_t>& getFilename() const { const std::string& getFilename() const {
return (filename_); return (filename_);
} }
@ -165,13 +165,13 @@ private:
/// If set by the server-hostname parameter, this value will be /// If set by the server-hostname parameter, this value will be
/// set in the sname field of the DHCPv4 packet. /// set in the sname field of the DHCPv4 packet.
/// This can be up to 64 octets long. /// This can be up to 64 octets long.
std::vector<uint8_t> sname_; std::string sname_;
/// @brief boot-file-name /// @brief boot-file-name
/// If set by the boot-file-name parameter, this value will be /// If set by the boot-file-name parameter, this value will be
/// set in the file field of the DHCPv4 packet. /// set in the file field of the DHCPv4 packet.
/// This can be up to 128 octets long. /// This can be up to 128 octets long.
std::vector<uint8_t> filename_; std::string filename_;
}; };
@ -214,8 +214,8 @@ public:
void addClass(const std::string& name, const ExpressionPtr& match_expr, void addClass(const std::string& name, const ExpressionPtr& match_expr,
const CfgOptionPtr& options, const CfgOptionPtr& options,
asiolink::IOAddress next_server = asiolink::IOAddress("0.0.0.0"), asiolink::IOAddress next_server = asiolink::IOAddress("0.0.0.0"),
const std::vector<uint8_t>& sname = std::vector<uint8_t>(), const std::string& sname = std::string(),
const std::vector<uint8_t>& filename = std::vector<uint8_t>()); const std::string& filename = std::string());
/// @brief Adds a new class to the list /// @brief Adds a new class to the list
/// ///

View File

@ -119,43 +119,42 @@ ClientClassDefParser::build(ConstElementPtr class_def_cfg) {
std::string name; std::string name;
try { try {
name = string_values_->getParam("name"); name = string_values_->getParam("name");
} catch (const std::exception& ex) {
isc_throw(DhcpConfigError, ex.what() << " ("
<< class_def_cfg->getPosition() << ")");
}
// Let's parse the next-server field // Let's parse the next-server field
IOAddress next_server("0.0.0.0"); IOAddress next_server("0.0.0.0");
string next_server_txt = string_values_->getOptionalParam("next-server", "0.0.0.0"); string next_server_txt = string_values_->getOptionalParam("next-server", "0.0.0.0");
try { try {
next_server = IOAddress(next_server_txt); next_server = IOAddress(next_server_txt);
} catch (const IOError& ex) { } catch (const IOError& ex) {
isc_throw(DhcpConfigError, "Invalid next-server value specified: '" isc_throw(DhcpConfigError, "Invalid next-server value specified: '"
<< next_server_txt << "'"); << next_server_txt);
} }
if (next_server.getFamily() != AF_INET) {
isc_throw(DhcpConfigError, "Invalid next-server value: '"
<< next_server_txt << "', must be IPv4 address");
}
// Let's try to parse sname if (next_server.getFamily() != AF_INET) {
string sname_txt = string_values_->getOptionalParam("server-hostname", ""); isc_throw(DhcpConfigError, "Invalid next-server value: '"
if (sname_txt.length() > Pkt4::MAX_SNAME_LEN) { << next_server_txt << "', must be IPv4 address");
isc_throw(DhcpConfigError, "server-hostname must be at most " }
<< Pkt4::MAX_SNAME_LEN << " bytes long, it is "
<< sname_txt.length());
}
std::vector<uint8_t> sname(sname_txt.begin(), sname_txt.end());
string file_txt = string_values_->getOptionalParam("boot-file-name", ""); if (next_server.isV4Bcast()) {
if (file_txt.length() > Pkt4::MAX_FILE_LEN) { isc_throw(DhcpConfigError, "Invalid next-server value: '"
isc_throw(DhcpConfigError, "boot-file-name must be at most " << next_server_txt << "', must not be a broadcast");
<< Pkt4::MAX_FILE_LEN << " bytes long, it is " }
<< file_txt.length());
} // Let's try to parse sname
std::vector<uint8_t> filename(file_txt.begin(), file_txt.end()); string sname = string_values_->getOptionalParam("server-hostname", "");
if (sname.length() >= Pkt4::MAX_SNAME_LEN) {
isc_throw(DhcpConfigError, "server-hostname must be at most "
<< Pkt4::MAX_SNAME_LEN - 1 << " bytes long, it is "
<< sname.length());
}
string filename = string_values_->getOptionalParam("boot-file-name", "");
if (filename.length() > Pkt4::MAX_FILE_LEN) {
isc_throw(DhcpConfigError, "boot-file-name must be at most "
<< Pkt4::MAX_FILE_LEN - 1 << " bytes long, it is "
<< filename.length());
}
try {
// an OptionCollectionPtr // an OptionCollectionPtr
class_dictionary_->addClass(name, match_expr_, options_, next_server, class_dictionary_->addClass(name, match_expr_, options_, next_server,
sname, filename); sname, filename);

View File

@ -1,4 +1,4 @@
// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC") // Copyright (C) 2015-2016 Internet Systems Consortium, Inc. ("ISC")
// //
// This Source Code Form is subject to the terms of the Mozilla Public // 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 // License, v. 2.0. If a copy of the MPL was not distributed with this
@ -585,6 +585,8 @@ TEST_F(ClientClassDefListParserTest, invalidClass) {
DhcpConfigError); DhcpConfigError);
} }
// Test verifies that without any class specified, the fixed fields have their
// default, empty value.
TEST_F(ClientClassDefParserTest, noFixedFields) { TEST_F(ClientClassDefParserTest, noFixedFields) {
std::string cfg_text = std::string cfg_text =
@ -610,7 +612,8 @@ TEST_F(ClientClassDefParserTest, noFixedFields) {
EXPECT_EQ(0, cclass->getFilename().size()); EXPECT_EQ(0, cclass->getFilename().size());
} }
// Test verifies that it is possible to define next-server field and it
// is actually set in the class properly.
TEST_F(ClientClassDefParserTest, nextServer) { TEST_F(ClientClassDefParserTest, nextServer) {
std::string cfg_text = std::string cfg_text =
@ -637,6 +640,7 @@ TEST_F(ClientClassDefParserTest, nextServer) {
EXPECT_EQ(0, cclass->getFilename().size()); EXPECT_EQ(0, cclass->getFilename().size());
} }
// Test verifies that the parser rejects bogus next-server value.
TEST_F(ClientClassDefParserTest, nextServerBogus) { TEST_F(ClientClassDefParserTest, nextServerBogus) {
std::string bogus_v6 = std::string bogus_v6 =
@ -666,6 +670,8 @@ TEST_F(ClientClassDefParserTest, nextServerBogus) {
EXPECT_THROW(parseClientClassDef(bogus_junk, Option::V4), DhcpConfigError); EXPECT_THROW(parseClientClassDef(bogus_junk, Option::V4), DhcpConfigError);
} }
// Test verifies that it is possible to define server-hostname field and it
// is actually set in the class properly.
TEST_F(ClientClassDefParserTest, serverName) { TEST_F(ClientClassDefParserTest, serverName) {
std::string cfg_text = std::string cfg_text =
@ -687,12 +693,12 @@ TEST_F(ClientClassDefParserTest, serverName) {
ASSERT_TRUE(cclass); ASSERT_TRUE(cclass);
// And it should not have any fixed fields set // And it should not have any fixed fields set
std::string exp_txt("hal9000"); std::string exp_sname("hal9000");
std::vector<uint8_t> exp_sname(exp_txt.begin(), exp_txt.end());
EXPECT_EQ(exp_sname, cclass->getSname()); EXPECT_EQ(exp_sname, cclass->getSname());
} }
// Test verifies that the parser rejects bogus server-hostname value.
TEST_F(ClientClassDefParserTest, serverNameInvalid) { TEST_F(ClientClassDefParserTest, serverNameInvalid) {
std::string cfg_too_long = std::string cfg_too_long =
@ -712,6 +718,8 @@ TEST_F(ClientClassDefParserTest, serverNameInvalid) {
} }
// Test verifies that it is possible to define boot-file-name field and it
// is actually set in the class properly.
TEST_F(ClientClassDefParserTest, filename) { TEST_F(ClientClassDefParserTest, filename) {
std::string cfg_text = std::string cfg_text =
@ -733,12 +741,11 @@ TEST_F(ClientClassDefParserTest, filename) {
ASSERT_TRUE(cclass); ASSERT_TRUE(cclass);
// And it should not have any fixed fields set // And it should not have any fixed fields set
std::string exp_txt("ipxe.efi"); std::string exp_filename("ipxe.efi");
std::vector<uint8_t> exp_filename(exp_txt.begin(), exp_txt.end());
EXPECT_EQ(exp_filename, cclass->getFilename()); EXPECT_EQ(exp_filename, cclass->getFilename());
} }
// Test verifies that the parser rejects bogus boot-file-name value.
TEST_F(ClientClassDefParserTest, filenameBogus) { TEST_F(ClientClassDefParserTest, filenameBogus) {
// boot-file-name is allowed up to 128 bytes, this one is 129. // boot-file-name is allowed up to 128 bytes, this one is 129.

View File

@ -316,7 +316,7 @@ TEST(ClientClassDef, fixedFieldsDefaults) {
ASSERT_NO_THROW(cclass.reset(new ClientClassDef(name, expr))); ASSERT_NO_THROW(cclass.reset(new ClientClassDef(name, expr)));
// Let's checks that it doesn't return any nonsense // Let's checks that it doesn't return any nonsense
vector<uint8_t> empty; string empty;
ASSERT_EQ(IOAddress("0.0.0.0"), cclass->getNextServer()); ASSERT_EQ(IOAddress("0.0.0.0"), cclass->getNextServer());
EXPECT_EQ(empty, cclass->getSname()); EXPECT_EQ(empty, cclass->getSname());
EXPECT_EQ(empty, cclass->getFilename()); EXPECT_EQ(empty, cclass->getFilename());
@ -338,11 +338,8 @@ TEST(ClientClassDef, fixedFieldsBasics) {
ASSERT_NO_THROW(cclass.reset(new ClientClassDef(name, expr))); ASSERT_NO_THROW(cclass.reset(new ClientClassDef(name, expr)));
string sname_txt = "This is a very long string that can be a server name"; string sname = "This is a very long string that can be a server name";
vector<uint8_t> sname(sname_txt.begin(), sname_txt.end()); string filename = "this-is-a-slightly-longish-name-of-a-file.txt";
string filename_txt = "this-is-a-slightly-longish-name-of-a-file.txt";
vector<uint8_t> filename(sname_txt.begin(), sname_txt.end());
cclass->setNextServer(IOAddress("1.2.3.4")); cclass->setNextServer(IOAddress("1.2.3.4"));
cclass->setSname(sname); cclass->setSname(sname);