From: Marcin Siodelski Date: Tue, 17 Oct 2017 09:23:37 +0000 (+0200) Subject: [5376] Accept explicitly configured server identifiers in inbound msgs. X-Git-Tag: trac5266_base~3^2~3^2~2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=62f0e9a53a0f099f1ac9d3b718a51b0b7c4fe3db;p=thirdparty%2Fkea.git [5376] Accept explicitly configured server identifiers in inbound msgs. --- diff --git a/src/bin/dhcp4/dhcp4_srv.cc b/src/bin/dhcp4/dhcp4_srv.cc index 6e05c28bc0..45972d6570 100644 --- a/src/bin/dhcp4/dhcp4_srv.cc +++ b/src/bin/dhcp4/dhcp4_srv.cc @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -27,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -62,6 +64,7 @@ #include #include #include +#include #include #include @@ -2824,7 +2827,53 @@ Dhcpv4Srv::acceptServerId(const Pkt4Ptr& query) const { // performance hit should be acceptable. If it turns out to // be significant, we will have to cache server identifiers // when sockets are opened. - return (IfaceMgr::instance().hasOpenSocket(server_id)); + if (IfaceMgr::instance().hasOpenSocket(server_id)) { + return (true); + } + + // There are some cases when an administrator explicitly sets server + // identifier (option 54) that should be used for a given, subnet, + // network etc. It doesn't have to be an address assigned to any of + // the server interfaces. Thus, we have to check if the server + // identifier received is the one that we explicitly set in the + // server configuration. At this point, we don't know which subnet + // the client belongs to so we can't match the server id with any + // subnet. We simply check if this server identifier is configured + // anywhere. This should be good enough to eliminate exchanges + // with other servers in the same network. + + /// @todo Currently we only check subnet identifiers configured on the + /// subnet level, shared network level and global level. This should + /// be sufficient for most of cases. At this point, trying to support + /// server identifiers on the class level seems to be an overkill and + /// is probably not needed. Same with host reservations. In fact, + /// at this point we don't know the reservations for the client + /// communicating with the server. We may revise some of these choices + /// in the future. + + SrvConfigPtr cfg = CfgMgr::instance().getCurrentCfg(); + + // Check if there is at least one subnet configured with this server + // identifier. + ConstCfgSubnets4Ptr cfg_subnets = cfg->getCfgSubnets4(); + if (cfg_subnets->hasSubnetWithServerId(server_id)) { + return (true); + } + + // This server identifier is not configured for any of the subnets, so + // check on the shared network level. + CfgSharedNetworks4Ptr cfg_networks = cfg->getCfgSharedNetworks4(); + if (cfg_networks->hasSubnetWithServerId(server_id)) { + return (true); + } + + // Finally, it is possible that the server identifier is specified + // on the global level. + ConstCfgOptionPtr cfg_global_options = cfg->getCfgOption(); + OptionCustomPtr opt_server_id = boost::dynamic_pointer_cast + (cfg_global_options->get(DHCP4_OPTION_SPACE, DHO_DHCP_SERVER_IDENTIFIER).option_); + + return (opt_server_id && (opt_server_id->readAddress() == server_id)); } void diff --git a/src/bin/dhcp4/tests/dora_unittest.cc b/src/bin/dhcp4/tests/dora_unittest.cc index 691e6e3eb7..14f0edf440 100644 --- a/src/bin/dhcp4/tests/dora_unittest.cc +++ b/src/bin/dhcp4/tests/dora_unittest.cc @@ -88,8 +88,9 @@ namespace { /// /// - Configuration 7: /// - Used for testing custom value of dhcp-server-identifier option. -/// - 1 subnets: 10.0.0.0/24 and 192.0.2.0/24 -/// - Custom server identifier specified for each subnet. +/// - 3 subnets: 10.0.0.0/24, 192.0.2.0/26 and 192.0.2.64/26 +/// - Custom server identifier specified for 2 subnets subnet. +/// - Custom server identifier specified at global level. /// /// - Configuration 8: /// - Simple configuration with a single subnet and single pool @@ -291,6 +292,12 @@ const char* DORA_CONFIGS[] = { " \"interfaces\": [ \"*\" ]" "}," "\"valid-lifetime\": 600," + "\"option-data\": [" + " {" + " \"name\": \"dhcp-server-identifier\"," + " \"data\": \"3.4.5.6\"" + " }" + "]," "\"subnet4\": [" " {" " \"subnet\": \"10.0.0.0/24\", " @@ -304,8 +311,8 @@ const char* DORA_CONFIGS[] = { " ]" " }," " {" - " \"subnet\": \"192.0.2.0/24\", " - " \"pools\": [ { \"pool\": \"192.0.2.10-192.0.2.100\" } ]," + " \"subnet\": \"192.0.2.0/26\", " + " \"pools\": [ { \"pool\": \"192.0.2.10-192.0.2.63\" } ]," " \"interface\": \"eth1\"," " \"option-data\": [" " {" @@ -313,7 +320,13 @@ const char* DORA_CONFIGS[] = { " \"data\": \"2.3.4.5\"" " }" " ]" - + " }," + " {" + " \"subnet\": \"192.0.2.64/26\", " + " \"pools\": [ { \"pool\": \"192.0.2.65-192.0.2.100\" } ]," + " \"relay\": {" + " \"ip-address\": \"10.2.3.4\"" + " }" " }" "]" "}", @@ -1625,11 +1638,24 @@ TEST_F(DORATest, customServerIdentifier) { // Repeat the test for different subnet. Dhcp4Client client2(client1.getServer(), Dhcp4Client::SELECTING); client2.setIfaceName("eth1"); + ASSERT_NO_THROW(client2.doDORA()); ASSERT_TRUE(client2.getContext().response_); resp = client2.getContext().response_; ASSERT_EQ(DHCPACK, static_cast(resp->getType())); EXPECT_EQ("2.3.4.5", client2.config_.serverid_.toText()); + + // Create relayed client which will be assigned a lease from the third + // subnet. This subnet inherits server identifier value from the global + // scope. + Dhcp4Client client3(client1.getServer(), Dhcp4Client::SELECTING); + client3.useRelay(true, IOAddress("10.2.3.4")); + + ASSERT_NO_THROW(client3.doDORA()); + ASSERT_TRUE(client3.getContext().response_); + resp = client3.getContext().response_; + ASSERT_EQ(DHCPACK, static_cast(resp->getType())); + EXPECT_EQ("3.4.5.6", client3.config_.serverid_.toText()); } // Starting tests which require MySQL backend availability. Those tests diff --git a/src/bin/dhcp4/tests/shared_network_unittest.cc b/src/bin/dhcp4/tests/shared_network_unittest.cc index 39901d86cd..fde8151d26 100644 --- a/src/bin/dhcp4/tests/shared_network_unittest.cc +++ b/src/bin/dhcp4/tests/shared_network_unittest.cc @@ -808,6 +808,59 @@ const char* NETWORKS_CONFIG[] = { " ]" "}", +// Configuration #15 +// - two shared networks, each comes with its own server identifier. + "{" + " \"interfaces-config\": {" + " \"interfaces\": [ \"*\" ]" + " }," + " \"valid-lifetime\": 600," + " \"shared-networks\": [" + " {" + " \"name\": \"frog\"," + " \"interface\": \"eth1\"," + " \"option-data\": [" + " {" + " \"name\": \"dhcp-server-identifier\"," + " \"data\": \"1.2.3.4\"" + " }" + " ]," + " \"subnet4\": [" + " {" + " \"subnet\": \"192.0.2.0/26\"," + " \"id\": 10," + " \"pools\": [" + " {" + " \"pool\": \"192.0.2.1 - 192.0.2.63\"" + " }" + " ]" + " }" + " ]" + " }," + " {" + " \"name\": \"dog\"," + " \"interface\": \"eth0\"," + " \"option-data\": [" + " {" + " \"name\": \"dhcp-server-identifier\"," + " \"data\": \"2.3.4.5\"" + " }" + " ]," + " \"subnet4\": [" + " {" + " \"subnet\": \"10.0.0.0/26\"," + " \"id\": 1000," + " \"pools\": [" + " {" + " \"pool\": \"10.0.0.1 - 10.0.0.63\"" + " }" + " ]" + " }" + " ]" + " }" + " ]" + "}" + }; /// @Brief Test fixture class for DHCPv4 server using shared networks. @@ -1661,4 +1714,44 @@ TEST_F(Dhcpv4SharedNetworkTest, sharedNetworkSelectedByClass) { }); } +// This test verifies that custom server identifier can be specified for a +// shared network. +TEST_F(Dhcpv4SharedNetworkTest, customServerIdentifier) { + Dhcp4Client client1(Dhcp4Client::SELECTING); + client1.setIfaceName("eth1"); + + // Configure DHCP server. + ASSERT_NO_THROW(configure(NETWORKS_CONFIG[15], *client1.getServer())); + + testAssigned([this, &client1] { + ASSERT_NO_THROW(client1.doDORA()); + }); + + // Make sure that the server responded. + ASSERT_TRUE(client1.getContext().response_); + Pkt4Ptr resp = client1.getContext().response_; + // Make sure that the server has responded with DHCPACK. + ASSERT_EQ(DHCPACK, static_cast(resp->getType())); + // The explicitly configured server identifier should take precedence + // over generated server identifier. + EXPECT_EQ("1.2.3.4", client1.config_.serverid_.toText()); + + // Create another client using different interface. + Dhcp4Client client2(client1.getServer(), Dhcp4Client::SELECTING); + client2.setIfaceName("eth0"); + + testAssigned([this, &client2] { + ASSERT_NO_THROW(client2.doDORA()); + }); + + // Make sure that the server responded. + ASSERT_TRUE(client2.getContext().response_); + resp = client2.getContext().response_; + // Make sure that the server has responded with DHCPACK. + ASSERT_EQ(DHCPACK, static_cast(resp->getType())); + // The explicitly configured server identifier should take precedence + // over generated server identifier. + EXPECT_EQ("2.3.4.5", client2.config_.serverid_.toText()); +} + } // end of anonymous namespace diff --git a/src/lib/dhcpsrv/Makefile.am b/src/lib/dhcpsrv/Makefile.am index 57a0d8e23b..0ab949294b 100644 --- a/src/lib/dhcpsrv/Makefile.am +++ b/src/lib/dhcpsrv/Makefile.am @@ -101,7 +101,7 @@ libkea_dhcpsrv_la_SOURCES += cfg_host_operations.cc cfg_host_operations.h libkea_dhcpsrv_la_SOURCES += cfg_option.cc cfg_option.h libkea_dhcpsrv_la_SOURCES += cfg_option_def.cc cfg_option_def.h libkea_dhcpsrv_la_SOURCES += cfg_rsoo.cc cfg_rsoo.h -libkea_dhcpsrv_la_SOURCES += cfg_shared_networks.h +libkea_dhcpsrv_la_SOURCES += cfg_shared_networks.cc cfg_shared_networks.h libkea_dhcpsrv_la_SOURCES += cfg_subnets4.cc cfg_subnets4.h libkea_dhcpsrv_la_SOURCES += cfg_subnets6.cc cfg_subnets6.h libkea_dhcpsrv_la_SOURCES += cfg_mac_source.cc cfg_mac_source.h diff --git a/src/lib/dhcpsrv/cfg_shared_networks.cc b/src/lib/dhcpsrv/cfg_shared_networks.cc new file mode 100644 index 0000000000..5b7971df8a --- /dev/null +++ b/src/lib/dhcpsrv/cfg_shared_networks.cc @@ -0,0 +1,24 @@ +// Copyright (C) 2017 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 +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include +#include + +using namespace isc::asiolink; + +namespace isc { +namespace dhcp { + +bool +CfgSharedNetworks4::hasSubnetWithServerId(const IOAddress& server_id) const { + const auto& index = networks_.get(); + auto network_it = index.find(server_id); + return (network_it != index.cend()); +} + + +} // end of namespace isc::dhcp +} // end of namespace isc diff --git a/src/lib/dhcpsrv/cfg_shared_networks.h b/src/lib/dhcpsrv/cfg_shared_networks.h index 5d954850cd..5daaa389ea 100644 --- a/src/lib/dhcpsrv/cfg_shared_networks.h +++ b/src/lib/dhcpsrv/cfg_shared_networks.h @@ -7,6 +7,7 @@ #ifndef CFG_SHARED_NETWORKS_H #define CFG_SHARED_NETWORKS_H +#include #include #include #include @@ -112,6 +113,15 @@ public: return (&networks_); } + /// @brief Checks if specified server identifier has been specified for + /// any network. + /// + /// @param server_id Server identifier. + /// + /// @return true if there is a network with a specified server identifier. + bool hasSubnetWithServerId(const asiolink::IOAddress& server_id) const; + + }; /// @brief Pointer to the configuration of IPv4 shared networks. diff --git a/src/lib/dhcpsrv/cfg_subnets4.cc b/src/lib/dhcpsrv/cfg_subnets4.cc index e4aa7bf578..0e47c6d0fc 100644 --- a/src/lib/dhcpsrv/cfg_subnets4.cc +++ b/src/lib/dhcpsrv/cfg_subnets4.cc @@ -68,6 +68,13 @@ CfgSubnets4::getByPrefix(const std::string& subnet_text) const { return ((subnet_it != index.cend()) ? (*subnet_it) : ConstSubnet4Ptr()); } +bool +CfgSubnets4::hasSubnetWithServerId(const asiolink::IOAddress& server_id) const { + const auto& index = subnets_.get(); + auto subnet_it = index.find(server_id); + return (subnet_it != index.cend()); +} + Subnet4Ptr CfgSubnets4::selectSubnet4o6(const SubnetSelector& selector) const { diff --git a/src/lib/dhcpsrv/cfg_subnets4.h b/src/lib/dhcpsrv/cfg_subnets4.h index 16ecff5379..86758172a5 100644 --- a/src/lib/dhcpsrv/cfg_subnets4.h +++ b/src/lib/dhcpsrv/cfg_subnets4.h @@ -91,6 +91,14 @@ public: /// subnet doesn't exist. ConstSubnet4Ptr getByPrefix(const std::string& subnet_prefix) const; + /// @brief Checks if specified server identifier has been specified for + /// any subnet. + /// + /// @param server_id Server identifier. + /// + /// @return true if there is a subnet with a specified server identifier. + bool hasSubnetWithServerId(const asiolink::IOAddress& server_id) const; + /// @brief Returns a pointer to the selected subnet. /// /// This method tries to retrieve the subnet for the client using various diff --git a/src/lib/dhcpsrv/network.cc b/src/lib/dhcpsrv/network.cc index ce0c9bdc23..46b7aabb41 100644 --- a/src/lib/dhcpsrv/network.cc +++ b/src/lib/dhcpsrv/network.cc @@ -4,8 +4,13 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include +#include +#include #include +#include +using namespace isc::asiolink; using namespace isc::data; namespace isc { @@ -112,6 +117,21 @@ Network4::toElement() const { return (map); } +IOAddress +Network4::getServerId() const { + try { + OptionCustomPtr opt_server_id = boost::dynamic_pointer_cast + (cfg_option_->get(DHCP4_OPTION_SPACE, DHO_DHCP_SERVER_IDENTIFIER).option_); + if (opt_server_id) { + return (opt_server_id->readAddress()); + } + } catch (const std::exception&) { + // Ignore any exceptions and simply return empty buffer. + } + + return (IOAddress::IPV4_ZERO_ADDRESS()); +} + ElementPtr Network6::toElement() const { ElementPtr map = Network::toElement(); diff --git a/src/lib/dhcpsrv/network.h b/src/lib/dhcpsrv/network.h index f34693eceb..3909441a64 100644 --- a/src/lib/dhcpsrv/network.h +++ b/src/lib/dhcpsrv/network.h @@ -337,6 +337,12 @@ public: /// @return A pointer to unparsed network configuration. virtual data::ElementPtr toElement() const; + /// @brief Returns binary representation of the dhcp-server-identifier option (54). + /// + /// @return Server identifier option as IPv4 address. Zero IPv4 address + /// indicates that server identifier hasn't been specified. + virtual asiolink::IOAddress getServerId() const; + private: /// @brief Should server use client identifiers for client lease diff --git a/src/lib/dhcpsrv/shared_network.h b/src/lib/dhcpsrv/shared_network.h index 83c65a9ee6..838fca73f5 100644 --- a/src/lib/dhcpsrv/shared_network.h +++ b/src/lib/dhcpsrv/shared_network.h @@ -7,6 +7,7 @@ #ifndef SHARED_NETWORK_H #define SHARED_NETWORK_H +#include #include #include #include @@ -30,6 +31,9 @@ struct SharedNetworkRandomAccessIndexTag { }; /// @brief A tag for accessing index by shared network name. struct SharedNetworkNameIndexTag { }; +/// @brief A tag for accessing index by server identifier. +struct SharedNetworkServerIdIndexTag { }; + /// @brief Shared network holding IPv4 subnets. /// /// Specialization of the @ref Network4 class for IPv4 shared networks. @@ -149,7 +153,15 @@ typedef boost::multi_index_container< boost::multi_index::tag, boost::multi_index::const_mem_fun + >, + // Third index allows for access by server identifier specified for the + // network. + boost::multi_index::ordered_non_unique< + boost::multi_index::tag, + boost::multi_index::const_mem_fun > + > > SharedNetwork4Collection; diff --git a/src/lib/dhcpsrv/subnet.h b/src/lib/dhcpsrv/subnet.h index 818f83cbc7..2bb84a4579 100644 --- a/src/lib/dhcpsrv/subnet.h +++ b/src/lib/dhcpsrv/subnet.h @@ -613,13 +613,20 @@ struct SubnetSubnetIdIndexTag { }; /// @brief Tag for the index for searching by subnet prefix. struct SubnetPrefixIndexTag { }; -/// @brief Multi index container holding subnets. +/// @brief Tag for the index for searching by server identifier. +struct SubnetServerIdIndexTag { }; + +/// @brief A collection of @c Subnet4 objects +/// +/// This container provides a set of indexes which can be used to retrieve +/// subnets by various properties. /// -/// This multi index container can hold pointers to @ref Subnet4 or -/// @ref Subnet6 objects representing subnets. It provides indexes for -/// subnet lookups using subnet properties such as: subnet identifier -/// or subnet prefix. It also provides a random access index which -/// allows for using the container like a vector. +/// This multi index container can hold pointers to @ref Subnet4 +/// objects representing subnets. It provides indexes for subnet lookups +/// using subnet properties such as: subnet identifier, +/// subnet prefix or server identifier specified for a subnet. It also +/// provides a random access index which allows for using the container +/// like a vector. /// /// The random access index is used by the DHCP servers which perform /// a full scan on subnets to find the one that matches some specific @@ -632,12 +639,9 @@ struct SubnetPrefixIndexTag { }; /// @todo We should consider optimizing subnet selection by leveraging /// the indexing capabilities of this container, e.g. searching for /// a subnet by interface name, relay address etc. -/// -/// @tparam SubnetType Type of the subnet: @ref Subnet4 or @ref Subnet6. -template -using SubnetCollection = boost::multi_index_container< +typedef boost::multi_index_container< // Multi index container holds pointers to the subnets. - boost::shared_ptr, + Subnet4Ptr, // The following holds all indexes. boost::multi_index::indexed_by< // First is the random access index allowing for accessing @@ -654,21 +658,61 @@ using SubnetCollection = boost::multi_index_container< boost::multi_index::ordered_unique< boost::multi_index::tag, boost::multi_index::const_mem_fun + >, + + // Fourth index allows for searching using an output from getServerId + boost::multi_index::ordered_non_unique< + boost::multi_index::tag, + boost::multi_index::const_mem_fun > > ->; - -/// @brief A collection of @c Subnet4 objects -/// -/// This container provides a set of indexes which can be used to retrieve -/// subnets by various properties. -typedef SubnetCollection Subnet4Collection; +> Subnet4Collection; /// @brief A collection of @c Subnet6 objects /// /// This container provides a set of indexes which can be used to retrieve /// subnets by various properties. -typedef SubnetCollection Subnet6Collection; +/// +/// This multi index container can hold pointers to @ref Subnet6 objects +/// representing subnets. It provides indexes for subnet lookups using +/// subnet properties such as: subnet identifier or subnet prefix. It +/// also provides a random access index which allows for using the +/// container like a vector. +/// +/// The random access index is used by the DHCP servers which perform +/// a full scan on subnets to find the one that matches some specific +/// criteria for subnet selection. +/// +/// The remaining indexes are used for searching for a specific subnet +/// as a result of receiving a command over the control API, e.g. +/// when 'subnet-get' command is received. +/// +/// @todo We should consider optimizing subnet selection by leveraging +/// the indexing capabilities of this container, e.g. searching for +/// a subnet by interface name, relay address etc. +typedef boost::multi_index_container< + // Multi index container holds pointers to the subnets. + Subnet6Ptr, + // The following holds all indexes. + boost::multi_index::indexed_by< + // First is the random access index allowing for accessing + // objects just like we'd do with a vector. + boost::multi_index::random_access< + boost::multi_index::tag + >, + // Second index allows for searching using subnet identifier. + boost::multi_index::ordered_unique< + boost::multi_index::tag, + boost::multi_index::const_mem_fun + >, + // Third index allows for searching using an output from toText function. + boost::multi_index::ordered_unique< + boost::multi_index::tag, + boost::multi_index::const_mem_fun + > + > +> Subnet6Collection; //@} diff --git a/src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc b/src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc index a0664d24f4..2cf518d776 100644 --- a/src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc +++ b/src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc @@ -6,6 +6,10 @@ #include #include +#include +#include +#include +#include #include #include #include @@ -856,4 +860,23 @@ TEST(CfgSubnets4Test, getSubnet) { EXPECT_EQ(Subnet4Ptr(), cfg.getSubnet(400)); // no such subnet } +// This test verifies that hasSubnetWithServerId returns correct value. +TEST(CfgSubnets4Test, hasSubnetWithServerId) { + CfgSubnets4 cfg; + + // Initially, there is no server identifier option present. + EXPECT_FALSE(cfg.hasSubnetWithServerId(IOAddress("1.2.3.4"))); + + OptionDefinitionPtr def = LibDHCP::getOptionDef(DHCP4_OPTION_SPACE, + DHO_DHCP_SERVER_IDENTIFIER); + OptionCustomPtr opt_server_id(new OptionCustom(*def, Option::V4)); + opt_server_id->writeAddress(IOAddress("1.2.3.4")); + Subnet4Ptr subnet(new Subnet4(IOAddress("192.0.2.0"), 26, 1, 2, 3, 100)); + subnet->getCfgOption()->add(opt_server_id, false, DHCP4_OPTION_SPACE); + cfg.add(subnet); + + EXPECT_TRUE(cfg.hasSubnetWithServerId(IOAddress("1.2.3.4"))); + EXPECT_FALSE(cfg.hasSubnetWithServerId(IOAddress("2.3.4.5"))); +} + } // end of anonymous namespace diff --git a/src/lib/dhcpsrv/tests/subnet_unittest.cc b/src/lib/dhcpsrv/tests/subnet_unittest.cc index 8081d11c9a..4a2d28dc08 100644 --- a/src/lib/dhcpsrv/tests/subnet_unittest.cc +++ b/src/lib/dhcpsrv/tests/subnet_unittest.cc @@ -7,13 +7,19 @@ #include #include +#include +#include +#include #include +#include +#include #include #include #include #include #include +#include #include #include #include @@ -497,6 +503,28 @@ TEST(Subnet4Test, PoolType) { EXPECT_THROW(subnet->addPool(pool5), BadValue); } +// Tests if correct value of server identifier is returned when getServerId is +// called. +TEST(Subnet4Test, getServerId) { + // Initially, the subnet has no server identifier. + Subnet4 subnet(IOAddress("192.2.0.0"), 16, 1, 2, 3); + EXPECT_TRUE(subnet.getServerId().isV4Zero()); + + // Add server identifier. + OptionDefinitionPtr option_def = LibDHCP::getOptionDef(DHCP4_OPTION_SPACE, + DHO_DHCP_SERVER_IDENTIFIER); + OptionCustomPtr option_server_id(new OptionCustom(*option_def, Option::V4)); + option_server_id->writeAddress(IOAddress("1.2.3.4")); + + CfgOptionPtr cfg_option = subnet.getCfgOption(); + cfg_option->add(option_server_id, false, DHCP4_OPTION_SPACE); + + // Verify that the server identifier returned by the Subnet4 object is + // correct. + OptionBuffer server_id_buf = { 1, 2, 3, 4 }; + EXPECT_EQ("1.2.3.4", subnet.getServerId().toText()); +} + // Tests for Subnet6 TEST(Subnet6Test, constructor) {