|
@@ -128,6 +128,20 @@ public:
|
|
|
static const uint16_t OVERRIDE_CLIENT_UPDATE = 4;
|
|
|
static const uint16_t REPLACE_CLIENT_NAME = 8;
|
|
|
|
|
|
+ // Enum used to specify whether a client (packet) should include
|
|
|
+ // the hostname option
|
|
|
+ enum ClientNameFlag {
|
|
|
+ CLIENT_NAME_PRESENT,
|
|
|
+ CLIENT_NAME_NOT_PRESENT
|
|
|
+ };
|
|
|
+
|
|
|
+ // Enum used to specify whether the server should replace/supply
|
|
|
+ // the hostname or not
|
|
|
+ enum ReplacementFlag {
|
|
|
+ NAME_REPLACED,
|
|
|
+ NAME_NOT_REPLACED
|
|
|
+ };
|
|
|
+
|
|
|
NameDhcpv4SrvTest()
|
|
|
: Dhcpv4SrvTest(),
|
|
|
d2_mgr_(CfgMgr::instance().getD2ClientMgr()),
|
|
@@ -173,7 +187,8 @@ public:
|
|
|
(mask & OVERRIDE_NO_UPDATE),
|
|
|
(mask & OVERRIDE_CLIENT_UPDATE),
|
|
|
((mask & REPLACE_CLIENT_NAME) ?
|
|
|
- D2ClientConfig::RCM_WHEN_PRESENT : D2ClientConfig::RCM_NEVER),
|
|
|
+ D2ClientConfig::RCM_WHEN_PRESENT
|
|
|
+ : D2ClientConfig::RCM_NEVER),
|
|
|
"myhost", "example.com")));
|
|
|
ASSERT_NO_THROW(CfgMgr::instance().setD2ClientConfig(cfg));
|
|
|
ASSERT_NO_THROW(srv_->startD2());
|
|
@@ -185,7 +200,8 @@ public:
|
|
|
const bool fqdn_fwd,
|
|
|
const bool fqdn_rev) {
|
|
|
const uint8_t hwaddr_data[] = { 0, 1, 2, 3, 4, 5, 6 };
|
|
|
- HWAddrPtr hwaddr(new HWAddr(hwaddr_data, sizeof(hwaddr_data), HTYPE_ETHER));
|
|
|
+ HWAddrPtr hwaddr(new HWAddr(hwaddr_data, sizeof(hwaddr_data),
|
|
|
+ HTYPE_ETHER));
|
|
|
Lease4Ptr lease(new Lease4(addr, hwaddr,
|
|
|
&generateClientId()->getData()[0],
|
|
|
generateClientId()->getData().size(),
|
|
@@ -310,6 +326,20 @@ public:
|
|
|
|
|
|
}
|
|
|
|
|
|
+ // Create a message holding of a given type
|
|
|
+ Pkt4Ptr generatePkt(const uint8_t msg_type) {
|
|
|
+ Pkt4Ptr pkt = Pkt4Ptr(new Pkt4(msg_type, 1234));
|
|
|
+ pkt->setRemoteAddr(IOAddress("192.0.2.3"));
|
|
|
+ // For DISCOVER we don't include server id, because client broadcasts
|
|
|
+ // the message to all servers.
|
|
|
+ if (msg_type != DHCPDISCOVER) {
|
|
|
+ pkt->addOption(srv_->getServerID());
|
|
|
+ }
|
|
|
+
|
|
|
+ pkt->addOption(generateClientId());
|
|
|
+ return (pkt);
|
|
|
+ }
|
|
|
+
|
|
|
// Test that server generates the appropriate FQDN option in response to
|
|
|
// client's FQDN option.
|
|
|
void testProcessFqdn(const Pkt4Ptr& query, const uint8_t exp_flags,
|
|
@@ -339,6 +369,75 @@ public:
|
|
|
|
|
|
}
|
|
|
|
|
|
+ // Test that the server's processes the hostname (or lack thereof)
|
|
|
+ // in a client request correctly, according to the replace-client-name
|
|
|
+ // mode configuration parameter.
|
|
|
+ //
|
|
|
+ // @param mode - value to use client-name-replacment parameter - for
|
|
|
+ // mode labels such as NEVER and ALWAYS must incluce enclosing quotes:
|
|
|
+ // "\"NEVER\"". This allows us to also pass in boolean literals which
|
|
|
+ // are unquoted.
|
|
|
+ // @param client_name_flag - specifies whether or not the client request
|
|
|
+ // should contain a hostname option
|
|
|
+ // @param exp_replacement_flag - specifies whether or not the server is
|
|
|
+ // expected to replace (or supply) the hostname in its response
|
|
|
+ void testReplaceClientNameMode(const char* mode,
|
|
|
+ enum ClientNameFlag client_name_flag,
|
|
|
+ enum ReplacementFlag exp_replacement_flag) {
|
|
|
+ // Configuration "template" with a replaceable mode parameter
|
|
|
+ const char* config_template =
|
|
|
+ "{ \"interfaces-config\": {"
|
|
|
+ " \"interfaces\": [ \"*\" ]"
|
|
|
+ "},"
|
|
|
+ "\"valid-lifetime\": 3000,"
|
|
|
+ "\"subnet4\": [ { "
|
|
|
+ " \"subnet\": \"10.0.0.0/24\", "
|
|
|
+ " \"id\": 1,"
|
|
|
+ " \"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.10\" } ]"
|
|
|
+ " }],"
|
|
|
+ "\"dhcp-ddns\": {"
|
|
|
+ "\"enable-updates\": true,"
|
|
|
+ "\"qualifying-suffix\": \"fake-suffix.isc.org.\","
|
|
|
+ "\"replace-client-name\": %s"
|
|
|
+ "}}";
|
|
|
+
|
|
|
+ // Create the configuration and configure the server
|
|
|
+ char config_buf[1024];
|
|
|
+ sprintf(config_buf, config_template, mode);
|
|
|
+ configure(config_buf, srv_);
|
|
|
+
|
|
|
+ // Build our client packet
|
|
|
+ Pkt4Ptr query;
|
|
|
+ if (client_name_flag == CLIENT_NAME_PRESENT) {
|
|
|
+ ASSERT_NO_THROW(query = generatePktWithHostname(DHCPREQUEST,
|
|
|
+ "my.example.com."));
|
|
|
+ } else {
|
|
|
+ ASSERT_NO_THROW(query = generatePkt(DHCPREQUEST));
|
|
|
+ }
|
|
|
+
|
|
|
+ // Run the packet through the server, extracting the hostname option
|
|
|
+ // from the response. If the option isn't present the returned pointer
|
|
|
+ // will be null.
|
|
|
+ OptionStringPtr hostname;
|
|
|
+ ASSERT_NO_THROW(
|
|
|
+ hostname = processHostname(query,
|
|
|
+ client_name_flag == CLIENT_NAME_PRESENT)
|
|
|
+ );
|
|
|
+
|
|
|
+ // Verify the contents (or lack thereof) of the hostname
|
|
|
+ if (exp_replacement_flag == NAME_REPLACED) {
|
|
|
+ ASSERT_TRUE(hostname);
|
|
|
+ EXPECT_EQ(".", hostname->getValue());
|
|
|
+ } else {
|
|
|
+ if (client_name_flag == CLIENT_NAME_PRESENT) {
|
|
|
+ ASSERT_TRUE(hostname);
|
|
|
+ EXPECT_EQ("my.example.com.", hostname->getValue());
|
|
|
+ } else {
|
|
|
+ ASSERT_FALSE(hostname);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
/// @brief Checks the packet's FQDN option flags against a given mask
|
|
|
///
|
|
|
/// @param pkt IPv4 packet whose FQDN flags should be checked.
|
|
@@ -363,8 +462,8 @@ public:
|
|
|
// the hostname option which would be sent to the client. It will
|
|
|
// throw NULL pointer if the hostname option is not to be included
|
|
|
// in the response.
|
|
|
- OptionStringPtr processHostname(const Pkt4Ptr& query) {
|
|
|
- if (!getHostnameOption(query)) {
|
|
|
+ OptionStringPtr processHostname(const Pkt4Ptr& query, bool must_have_host = true) {
|
|
|
+ if (!getHostnameOption(query) && must_have_host) {
|
|
|
ADD_FAILURE() << "Hostname option not carried in the query";
|
|
|
}
|
|
|
|
|
@@ -1282,4 +1381,43 @@ TEST_F(NameDhcpv4SrvTest, emptyFqdn) {
|
|
|
|
|
|
}
|
|
|
|
|
|
+// Verifies that the replace-client-name behavior is correct for each of
|
|
|
+// the supported modes.
|
|
|
+TEST_F(NameDhcpv4SrvTest, replaceClientNameModeTest) {
|
|
|
+
|
|
|
+ // We pass mode labels in with enclosing quotes so we can also test
|
|
|
+ // unquoted boolean literals true/false
|
|
|
+ testReplaceClientNameMode("\"NEVER\"",
|
|
|
+ CLIENT_NAME_NOT_PRESENT, NAME_NOT_REPLACED);
|
|
|
+ testReplaceClientNameMode("\"NEVER\"",
|
|
|
+ CLIENT_NAME_PRESENT, NAME_NOT_REPLACED);
|
|
|
+
|
|
|
+ testReplaceClientNameMode("\"ALWAYS\"",
|
|
|
+ CLIENT_NAME_NOT_PRESENT, NAME_REPLACED);
|
|
|
+ testReplaceClientNameMode("\"ALWAYS\"",
|
|
|
+ CLIENT_NAME_PRESENT, NAME_REPLACED);
|
|
|
+
|
|
|
+ testReplaceClientNameMode("\"WHEN_PRESENT\"",
|
|
|
+ CLIENT_NAME_NOT_PRESENT, NAME_NOT_REPLACED);
|
|
|
+ testReplaceClientNameMode("\"WHEN_PRESENT\"",
|
|
|
+ CLIENT_NAME_PRESENT, NAME_REPLACED);
|
|
|
+
|
|
|
+ testReplaceClientNameMode("\"WHEN_NOT_PRESENT\"",
|
|
|
+ CLIENT_NAME_NOT_PRESENT, NAME_REPLACED);
|
|
|
+ testReplaceClientNameMode("\"WHEN_NOT_PRESENT\"",
|
|
|
+ CLIENT_NAME_PRESENT, NAME_NOT_REPLACED);
|
|
|
+
|
|
|
+ // Verify that boolean false produces the same result as RCM_NEVER
|
|
|
+ testReplaceClientNameMode("false",
|
|
|
+ CLIENT_NAME_NOT_PRESENT, NAME_NOT_REPLACED);
|
|
|
+ testReplaceClientNameMode("false",
|
|
|
+ CLIENT_NAME_PRESENT, NAME_NOT_REPLACED);
|
|
|
+
|
|
|
+ // Verify that boolean true produces the same result as RCM_WHEN_PRESENT
|
|
|
+ testReplaceClientNameMode("true",
|
|
|
+ CLIENT_NAME_NOT_PRESENT, NAME_NOT_REPLACED);
|
|
|
+ testReplaceClientNameMode("true",
|
|
|
+ CLIENT_NAME_PRESENT, NAME_REPLACED);
|
|
|
+}
|
|
|
+
|
|
|
} // end of anonymous namespace
|