</para>
<para>
- For clients that belong to the VENDOR_CLASS_docsis3.0 class, the siaddr
- field is set to the value of next-server (if specified in a subnet). If
- there is a boot-file-name option specified, its value is also set in the
- file field in the DHCPv4 packet. For eRouter1.0 class, the siaddr is
- always set to 0.0.0.0. That capability is expected to be moved to
- an external hook library that will be dedicated to cable modems.
+ Note: Kea 1.0 and earlier versions performed special actions for
+ clients that were in VENDOR_CLASS_docsis3.0. This is no longer the
+ case in Kea 1.1 and newer. In those newer versions the old behavior
+ can be achieved by defining VENDOR_CLASS_docsis3.0 and setting
+ its next-server and boot-file-name values appropriately.
</para>
<para>
and type respectively. The fourth argument contains detailed packet
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
This is the info message logged when the DHCPv4 server starts reconfiguration
as a result of receiving SIGHUP signal.
The argument specifies the client and transaction identification
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
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
transaction identification information. The second argument specifies
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
A debug message including the detailed data about the packet being sent
to the client. The first argument contains the client and the transaction
response->setSiaddr(next_server);
}
- const vector<uint8_t>& sname = cl->second->getSname();
+ const string& sname = cl->second->getSname();
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()) {
- 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());
}
}
// them we append them for him.
appendBasicOptions(ex);
- // See if the class mandates setting any fixed fields (siaddr, sname,
- // filename).
+ // Set fixed fields (siaddr, sname, filename) if defined in
+ // the reservation, class or subnet specific configuration.
setFixedFields(ex);
} else {
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());
}
// them we append them for him.
appendBasicOptions(ex);
- // See if the class mandates setting any fixed fields (siaddr, sname,
- // filename).
+ // Set fixed fields (siaddr, sname, filename) if defined in
+ // the reservation, class or subnet specific configuration.
setFixedFields(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());
}
appendBasicOptions(ex);
adjustIfaceData(ex);
- // See if the class mandates setting any fixed fields (siaddr, sname,
- // filename).
+ // Set fixed fields (siaddr, sname, filename) if defined in
+ // the reservation, class or subnet specific configuration.
setFixedFields(ex);
// There are cases for the DHCPINFORM that the server receives it via
// The DHCPACK must contain server id.
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());
}
// 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(VENDOR_CLASS_PREFIX + DOCSIS3_CLASS_MODEM);
- 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();
- }
+ pkt->addClass(VENDOR_CLASS_PREFIX + vendor_class->getValue());
+ classes += VENDOR_CLASS_PREFIX + vendor_class->getValue();
}
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
Dhcpv4Srv::startD2() {
D2ClientMgr& d2_mgr = CfgMgr::instance().getD2ClientMgr();
///
/// 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
- /// siaddr, sname or filename fields in the outgoing packet. This
- /// is what this method does.
+ /// siaddr, sname or filename fields in the outgoing packet. Also, those
+ /// 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
/// response to be adjusted.
/// @param pkt packet to be classified
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.
/// Pointer to the allocation engine that we are currently using
/// It must be a pointer, because we will support changing engines
namespace {
-/// @brief Set of JSON configurations used throughout the DORA tests.
+/// @brief Set of JSON configurations used throughout the classify tests.
///
/// - Configuration 0:
/// - Used for testing direct traffic
/// - 1 subnet: 10.0.0.0/24
/// - 1 pool: 10.0.0.10-10.0.0.100
-/// - Router option present: 10.0.0.200 and 10.0.0.201
-/// - 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:
+/// - the following classes defined:
/// option[93].hex == 0x0009, next-server set to 1.2.3.4
/// option[93].hex == 0x0007, set server-hostname to deneb
/// option[93].hex == 0x0006, set boot-file-name to pxelinux.0
/// option[93].hex == 0x0001, set boot-file-name to ipxe.efi
const char* CONFIGS[] = {
-// 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
+ // Configuration 0
"{ \"interfaces-config\": {"
" \"interfaces\": [ \"*\" ]"
"},"
~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
testFixedFields(const char* config, uint8_t msgtype, const OptionPtr& extra_opt,
const std::string& exp_next_server, const std::string& exp_sname,
// This test checks that an incoming DISCOVER that does not match any classes
// will get the fixed fields empty.
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
// will get the fixed fields empty.
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
// will get the fixed fields empty.
TEST_F(ClassifyTest, fixedFieldsInformNoClasses) {
- testFixedFields(CONFIGS[1], DHCPINFORM, OptionPtr(), "0.0.0.0", "", "");
+ testFixedFields(CONFIGS[0], DHCPINFORM, OptionPtr(), "0.0.0.0", "", "");
}
TEST_F(ClassifyTest, fixedFieldsDiscoverNextServer) {
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
// next-server specified will result in a response that has the next-server set.
TEST_F(ClassifyTest, fixedFieldsRequestNextServer) {
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
// next-server specified will result in a response that has the next-server set.
TEST_F(ClassifyTest, fixedFieldsInformNextServer) {
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", "", "");
}
TEST_F(ClassifyTest, fixedFieldsDiscoverHostname) {
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
// server-hostname specified will result in a response that has the sname field set.
TEST_F(ClassifyTest, fixedFieldsRequestHostname) {
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
// server-hostname specified will result in a response that has the sname field set.
TEST_F(ClassifyTest, fixedFieldsInformHostname) {
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", "");
}
TEST_F(ClassifyTest, fixedFieldsDiscoverFile1) {
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
// boot-file-name specified will result in a response that has the filename field set.
TEST_F(ClassifyTest, fixedFieldsRequestFile1) {
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
// boot-file-name specified will result in a response that has the filename field set.
TEST_F(ClassifyTest, fixedFieldsInformFile1) {
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");
}
TEST_F(ClassifyTest, fixedFieldsDiscoverFile2) {
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
// boot-file-name specified will result in a response that has the filename field set.
TEST_F(ClassifyTest, fixedFieldsRequestFile2) {
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
// boot-file-name specified will result in a response that has the filename field set.
TEST_F(ClassifyTest, fixedFieldsInformFile2) {
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");
}
ASSERT_EQ(0, rcode_);
}
-// Checks if DOCSIS client packets are classified properly
-TEST_F(Dhcpv4SrvTest, docsisClientClassification) {
+/// Checks if DOCSIS client packets are classified properly
+///
+/// @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);
-// 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
// License, v. 2.0. If a copy of the MPL was not distributed with this
const ExpressionPtr& match_expr,
const CfgOptionPtr& 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
if (name_.empty()) {
ClientClassDef::ClientClassDef(const ClientClassDef& rhs)
: 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_) {
match_expr_.reset(new Expression());
const ExpressionPtr& match_expr,
const CfgOptionPtr& cfg_option,
asiolink::IOAddress next_server,
- const std::vector<uint8_t>& sname,
- const std::vector<uint8_t>& filename) {
+ const std::string& sname,
+ const std::string& filename) {
ClientClassDefPtr cclass(new ClientClassDef(name, match_expr, cfg_option));
cclass->setNextServer(next_server);
cclass->setSname(sname);
-// 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
// License, v. 2.0. If a copy of the MPL was not distributed with this
/// @brief returns next-server value
/// @return next-server value
- asiolink::IOAddress getNextServer() const {
+ const asiolink::IOAddress& getNextServer() const {
return (next_server_);
}
/// @brief sets the server-name value
///
/// @param sname the value to be set
- void setSname(const std::vector<uint8_t>& sname) {
+ void setSname(const std::string& sname) {
sname_ = sname;
}
/// @brief sets the boot-file-name value
///
/// @param filename the value to be set
- void setFilename(const std::vector<uint8_t>& filename) {
+ void setFilename(const std::string& filename) {
filename_ = filename;
}
/// @brief returns server-hostname value
/// @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_);
}
/// @brief returns boot-file-name value
/// @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_);
}
/// If set by the server-hostname parameter, this value will be
/// set in the sname field of the DHCPv4 packet.
/// This can be up to 64 octets long.
- std::vector<uint8_t> sname_;
+ std::string sname_;
/// @brief boot-file-name
/// If set by the boot-file-name parameter, this value will be
/// set in the file field of the DHCPv4 packet.
/// This can be up to 128 octets long.
- std::vector<uint8_t> filename_;
+ std::string filename_;
};
void addClass(const std::string& name, const ExpressionPtr& match_expr,
const CfgOptionPtr& options,
asiolink::IOAddress next_server = asiolink::IOAddress("0.0.0.0"),
- const std::vector<uint8_t>& sname = std::vector<uint8_t>(),
- const std::vector<uint8_t>& filename = std::vector<uint8_t>());
+ const std::string& sname = std::string(),
+ const std::string& filename = std::string());
/// @brief Adds a new class to the list
///
std::string name;
try {
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
- IOAddress next_server("0.0.0.0");
- string next_server_txt = string_values_->getOptionalParam("next-server", "0.0.0.0");
- try {
- next_server = IOAddress(next_server_txt);
- } catch (const IOError& ex) {
- isc_throw(DhcpConfigError, "Invalid next-server value specified: '"
- << 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 parse the next-server field
+ IOAddress next_server("0.0.0.0");
+ string next_server_txt = string_values_->getOptionalParam("next-server", "0.0.0.0");
+ try {
+ next_server = IOAddress(next_server_txt);
+ } catch (const IOError& ex) {
+ isc_throw(DhcpConfigError, "Invalid next-server value specified: '"
+ << next_server_txt);
+ }
- // Let's try to parse sname
- string sname_txt = string_values_->getOptionalParam("server-hostname", "");
- if (sname_txt.length() > Pkt4::MAX_SNAME_LEN) {
- 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());
+ if (next_server.getFamily() != AF_INET) {
+ isc_throw(DhcpConfigError, "Invalid next-server value: '"
+ << next_server_txt << "', must be IPv4 address");
+ }
- string file_txt = string_values_->getOptionalParam("boot-file-name", "");
- if (file_txt.length() > Pkt4::MAX_FILE_LEN) {
- isc_throw(DhcpConfigError, "boot-file-name must be at most "
- << Pkt4::MAX_FILE_LEN << " bytes long, it is "
- << file_txt.length());
- }
- std::vector<uint8_t> filename(file_txt.begin(), file_txt.end());
+ if (next_server.isV4Bcast()) {
+ isc_throw(DhcpConfigError, "Invalid next-server value: '"
+ << next_server_txt << "', must not be a broadcast");
+ }
+
+ // Let's try to parse sname
+ 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
class_dictionary_->addClass(name, match_expr_, options_, next_server,
sname, filename);
-// 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
// License, v. 2.0. If a copy of the MPL was not distributed with this
DhcpConfigError);
}
+// Test verifies that without any class specified, the fixed fields have their
+// default, empty value.
TEST_F(ClientClassDefParserTest, noFixedFields) {
std::string cfg_text =
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) {
std::string cfg_text =
EXPECT_EQ(0, cclass->getFilename().size());
}
+// Test verifies that the parser rejects bogus next-server value.
TEST_F(ClientClassDefParserTest, nextServerBogus) {
std::string bogus_v6 =
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) {
std::string cfg_text =
ASSERT_TRUE(cclass);
// And it should not have any fixed fields set
- std::string exp_txt("hal9000");
- std::vector<uint8_t> exp_sname(exp_txt.begin(), exp_txt.end());
+ std::string exp_sname("hal9000");
EXPECT_EQ(exp_sname, cclass->getSname());
}
+// Test verifies that the parser rejects bogus server-hostname value.
TEST_F(ClientClassDefParserTest, serverNameInvalid) {
std::string cfg_too_long =
}
+// 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) {
std::string cfg_text =
ASSERT_TRUE(cclass);
// And it should not have any fixed fields set
- std::string exp_txt("ipxe.efi");
- std::vector<uint8_t> exp_filename(exp_txt.begin(), exp_txt.end());
-
+ std::string exp_filename("ipxe.efi");
EXPECT_EQ(exp_filename, cclass->getFilename());
}
+// Test verifies that the parser rejects bogus boot-file-name value.
TEST_F(ClientClassDefParserTest, filenameBogus) {
// boot-file-name is allowed up to 128 bytes, this one is 129.
ASSERT_NO_THROW(cclass.reset(new ClientClassDef(name, expr)));
// 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());
EXPECT_EQ(empty, cclass->getSname());
EXPECT_EQ(empty, cclass->getFilename());
ASSERT_NO_THROW(cclass.reset(new ClientClassDef(name, expr)));
- string sname_txt = "This is a very long string that can be a server name";
- vector<uint8_t> sname(sname_txt.begin(), sname_txt.end());
-
- string filename_txt = "this-is-a-slightly-longish-name-of-a-file.txt";
- vector<uint8_t> filename(sname_txt.begin(), sname_txt.end());
+ string sname = "This is a very long string that can be a server name";
+ string filename = "this-is-a-slightly-longish-name-of-a-file.txt";
cclass->setNextServer(IOAddress("1.2.3.4"));
cclass->setSname(sname);