From: Marcin Siodelski Date: Mon, 22 Aug 2016 08:38:28 +0000 (+0200) Subject: [4552] Updated Host object with DHCPv4 specific message fields. X-Git-Tag: trac4631_base~4^2~9 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=111e0e73ed6e5316df7f28782a39f02baf414e1b;p=thirdparty%2Fkea.git [4552] Updated Host object with DHCPv4 specific message fields. --- diff --git a/src/lib/dhcpsrv/host.cc b/src/lib/dhcpsrv/host.cc index 3c54e188b2..70211e903b 100644 --- a/src/lib/dhcpsrv/host.cc +++ b/src/lib/dhcpsrv/host.cc @@ -74,14 +74,20 @@ Host::Host(const uint8_t* identifier, const size_t identifier_len, const asiolink::IOAddress& ipv4_reservation, const std::string& hostname, const std::string& dhcp4_client_classes, - const std::string& dhcp6_client_classes) + const std::string& dhcp6_client_classes, + const asiolink::IOAddress& next_server, + const std::string& server_host_name, + const std::string& boot_file_name) + : identifier_type_(identifier_type), identifier_value_(), ipv4_subnet_id_(ipv4_subnet_id), ipv6_subnet_id_(ipv6_subnet_id), ipv4_reservation_(asiolink::IOAddress::IPV4_ZERO_ADDRESS()), hostname_(hostname), dhcp4_client_classes_(dhcp4_client_classes), - dhcp6_client_classes_(dhcp6_client_classes), host_id_(0), - cfg_option4_(new CfgOption()), cfg_option6_(new CfgOption()) { + dhcp6_client_classes_(dhcp6_client_classes), + next_server_(asiolink::IOAddress::IPV4_ZERO_ADDRESS()), + server_host_name_(server_host_name), boot_file_name_(boot_file_name), + host_id_(0), cfg_option4_(new CfgOption()), cfg_option6_(new CfgOption()) { // Initialize host identifier. setIdentifier(identifier, identifier_len, identifier_type); @@ -90,6 +96,11 @@ Host::Host(const uint8_t* identifier, const size_t identifier_len, // Validate and set IPv4 address reservation. setIPv4Reservation(ipv4_reservation); } + + if (!next_server.isV4Zero()) { + // Validate and set next server address. + setNextServer(next_server); + } } Host::Host(const std::string& identifier, const std::string& identifier_name, @@ -97,14 +108,19 @@ Host::Host(const std::string& identifier, const std::string& identifier_name, const asiolink::IOAddress& ipv4_reservation, const std::string& hostname, const std::string& dhcp4_client_classes, - const std::string& dhcp6_client_classes) + const std::string& dhcp6_client_classes, + const asiolink::IOAddress& next_server, + const std::string& server_host_name, + const std::string& boot_file_name) : identifier_type_(IDENT_HWADDR), identifier_value_(), ipv4_subnet_id_(ipv4_subnet_id), ipv6_subnet_id_(ipv6_subnet_id), ipv4_reservation_(asiolink::IOAddress::IPV4_ZERO_ADDRESS()), hostname_(hostname), dhcp4_client_classes_(dhcp4_client_classes), - dhcp6_client_classes_(dhcp6_client_classes), host_id_(0), - cfg_option4_(new CfgOption()), cfg_option6_(new CfgOption()) { + dhcp6_client_classes_(dhcp6_client_classes), + next_server_(asiolink::IOAddress::IPV4_ZERO_ADDRESS()), + server_host_name_(server_host_name), boot_file_name_(boot_file_name), + host_id_(0), cfg_option4_(new CfgOption()), cfg_option6_(new CfgOption()) { // Initialize host identifier. setIdentifier(identifier, identifier_name); @@ -113,6 +129,11 @@ Host::Host(const std::string& identifier, const std::string& identifier_name, // Validate and set IPv4 address reservation. setIPv4Reservation(ipv4_reservation); } + + if (!next_server.isV4Zero()) { + // Validate and set next server address. + setNextServer(next_server); + } } const std::vector& @@ -339,6 +360,19 @@ Host::addClientClassInternal(ClientClasses& classes, } } +void +Host::setNextServer(const asiolink::IOAddress& next_server) { + if (!next_server.isV4()) { + isc_throw(isc::BadValue, "next server address '" << next_server + << "' is not a valid IPv4 address"); + } else if (next_server.isV4Zero() || next_server.isV4Bcast()) { + isc_throw(isc::BadValue, "invalid next server address '" + << next_server << "'"); + } + + next_server_ = next_server; +} + std::string Host::toText() const { std::ostringstream s; @@ -363,6 +397,16 @@ Host::toText() const { s << " ipv4_reservation=" << (ipv4_reservation_.isV4Zero() ? "(no)" : ipv4_reservation_.toText()); + // Add next server. + s << " siaddr=" << (next_server_.isV4Zero() ? "(no)" : + next_server_.toText()); + + // Add server host name. + s << " sname=" << (server_host_name_.empty() ? "(empty)" : server_host_name_); + + // Add boot file name. + s << " file=" << (boot_file_name_.empty() ? "(empty)" : boot_file_name_); + if (ipv6_reservations_.empty()) { s << " ipv6_reservations=(none)"; diff --git a/src/lib/dhcpsrv/host.h b/src/lib/dhcpsrv/host.h index 8b38185a5e..d09c181ed8 100644 --- a/src/lib/dhcpsrv/host.h +++ b/src/lib/dhcpsrv/host.h @@ -214,6 +214,9 @@ public: /// separated by commas. The names get trimmed by this constructor. /// @param dhcp6_client_classes A string holding DHCPv6 client class names /// separated by commas. The names get trimmed by this constructor. + /// @param next_server IPv4 address of next server (siaddr). + /// @param server_host_name Server host name (a.k.a. sname). + /// @param boot_file_name Boot file name (a.k.a. file). /// /// @throw BadValue if the provided values are invalid. In particular, /// if the identifier is invalid. @@ -223,7 +226,10 @@ public: const asiolink::IOAddress& ipv4_reservation, const std::string& hostname = "", const std::string& dhcp4_client_classes = "", - const std::string& dhcp6_client_classes = ""); + const std::string& dhcp6_client_classes = "", + const asiolink::IOAddress& next_server = asiolink::IOAddress::IPV4_ZERO_ADDRESS(), + const std::string& server_host_name = "", + const std::string& boot_file_name = ""); /// @brief Constructor. /// @@ -258,6 +264,9 @@ public: /// separated by commas. The names get trimmed by this constructor. /// @param dhcp6_client_classes A string holding DHCPv6 client class names /// separated by commas. The names get trimmed by this constructor. + /// @param next_server IPv4 address of next server (siaddr). + /// @param server_host_name Server host name (a.k.a. sname). + /// @param boot_file_name Boot file name (a.k.a. file). /// /// @throw BadValue if the provided values are invalid. In particular, /// if the identifier is invalid. @@ -266,7 +275,10 @@ public: const asiolink::IOAddress& ipv4_reservation, const std::string& hostname = "", const std::string& dhcp4_client_classes = "", - const std::string& dhcp6_client_classes = ""); + const std::string& dhcp6_client_classes = "", + const asiolink::IOAddress& next_server = asiolink::IOAddress::IPV4_ZERO_ADDRESS(), + const std::string& server_host_name = "", + const std::string& boot_file_name = ""); /// @brief Replaces currently used identifier with a new identifier. /// @@ -449,6 +461,43 @@ public: return (dhcp6_client_classes_); } + /// @brief Sets new value for next server field (siaddr). + /// + /// @param next_server New address of a next server. + /// + /// @throw isc::BadValue if the provided address is not an IPv4 address, + /// is a 0 address or broadcast address. + void setNextServer(const asiolink::IOAddress& next_server); + + /// @brief Returns value of next server field (siaddr). + const asiolink::IOAddress& getNextServer() const { + return (next_server_); + } + + /// @brief Sets new value for server hostname (sname). + /// + /// @param server_host_name New value for server hostname. + void setServerHostname(const std::string& server_host_name) { + server_host_name_ = server_host_name; + } + + /// @brief Returns value of server hostname (sname). + std::string getServerHostname() const { + return (server_host_name_); + } + + /// @brief Sets new value for boot file name (file). + /// + /// @param boot_file_name New value of boot file name. + void setBootFileName(const std::string& boot_file_name) { + boot_file_name_ = boot_file_name; + } + + /// @brief Returns value of boot file name (file). + std::string getBootFileName() const { + return (boot_file_name_); + } + /// @brief Returns pointer to the DHCPv4 option data configuration for /// this host. /// @@ -527,6 +576,12 @@ private: ClientClasses dhcp4_client_classes_; /// @brief Collection of classes associated with a DHCPv6 client. ClientClasses dhcp6_client_classes_; + /// @brief Next server (a.k.a. siaddr, carried in DHCPv4 message). + asiolink::IOAddress next_server_; + /// @brief Server host name (a.k.a. sname, carried in DHCPv4 message). + std::string server_host_name_; + /// @brief Boot file name (a.k.a. file, carried in DHCPv4 message) + std::string boot_file_name_; /// @brief HostID (a unique identifier assigned when the host is stored in /// MySQL or Pgsql) diff --git a/src/lib/dhcpsrv/tests/host_unittest.cc b/src/lib/dhcpsrv/tests/host_unittest.cc index 535d861ef4..195b7766cd 100644 --- a/src/lib/dhcpsrv/tests/host_unittest.cc +++ b/src/lib/dhcpsrv/tests/host_unittest.cc @@ -192,7 +192,11 @@ TEST_F(HostTest, createFromHWAddrString) { ASSERT_NO_THROW(host.reset(new Host("01:02:03:04:05:06", "hw-address", SubnetID(1), SubnetID(2), IOAddress("192.0.2.3"), - "somehost.example.org"))); + "somehost.example.org", + std::string(), std::string(), + IOAddress("192.0.0.2"), + "server-hostname.example.org", + "bootfile.efi"))); // The HW address should be set to non-null. HWAddrPtr hwaddr = host->getHWAddress(); ASSERT_TRUE(hwaddr); @@ -205,6 +209,9 @@ TEST_F(HostTest, createFromHWAddrString) { EXPECT_EQ(2, host->getIPv6SubnetID()); EXPECT_EQ("192.0.2.3", host->getIPv4Reservation().toText()); EXPECT_EQ("somehost.example.org", host->getHostname()); + EXPECT_EQ("192.0.0.2", host->getNextServer().toText()); + EXPECT_EQ("server-hostname.example.org", host->getServerHostname()); + EXPECT_EQ("bootfile.efi", host->getBootFileName()); // Use invalid identifier name EXPECT_THROW(Host("01:02:03:04:05:06", "bogus", SubnetID(1), SubnetID(2), @@ -264,7 +271,12 @@ TEST_F(HostTest, createFromHWAddrBinary) { Host::IDENT_HWADDR, SubnetID(1), SubnetID(2), IOAddress("192.0.2.3"), - "somehost.example.org"))); + "somehost.example.org", + std::string(), std::string(), + IOAddress("192.0.0.2"), + "server-hostname.example.org", + "bootfile.efi"))); + // Hardware address should be non-null. HWAddrPtr hwaddr = host->getHWAddress(); ASSERT_TRUE(hwaddr); @@ -277,6 +289,9 @@ TEST_F(HostTest, createFromHWAddrBinary) { EXPECT_EQ(2, host->getIPv6SubnetID()); EXPECT_EQ("192.0.2.3", host->getIPv4Reservation().toText()); EXPECT_EQ("somehost.example.org", host->getHostname()); + EXPECT_EQ("192.0.0.2", host->getNextServer().toText()); + EXPECT_EQ("server-hostname.example.org", host->getServerHostname()); + EXPECT_EQ("bootfile.efi", host->getBootFileName()); } // This test verifies that it is possible to create a Host object using @@ -644,11 +659,17 @@ TEST_F(HostTest, setValues) { host->setIPv6SubnetID(SubnetID(234)); host->setIPv4Reservation(IOAddress("10.0.0.1")); host->setHostname("other-host.example.org"); + host->setNextServer(IOAddress("192.0.2.2")); + host->setServerHostname("server-hostname.example.org"); + host->setBootFileName("bootfile.efi"); EXPECT_EQ(123, host->getIPv4SubnetID()); EXPECT_EQ(234, host->getIPv6SubnetID()); EXPECT_EQ("10.0.0.1", host->getIPv4Reservation().toText()); EXPECT_EQ("other-host.example.org", host->getHostname()); + EXPECT_EQ("192.0.2.2", host->getNextServer().toText()); + EXPECT_EQ("server-hostname.example.org", host->getServerHostname()); + EXPECT_EQ("bootfile.efi", host->getBootFileName()); // Remove IPv4 reservation. host->removeIPv4Reservation(); @@ -664,6 +685,12 @@ TEST_F(HostTest, setValues) { // Broadcast address can't be set. EXPECT_THROW(host->setIPv4Reservation(IOAddress::IPV4_BCAST_ADDRESS()), isc::BadValue); + + // Zero or broadcast are invalid addresses for next server. + EXPECT_THROW(host->setNextServer(asiolink::IOAddress::IPV4_ZERO_ADDRESS()), + isc::BadValue); + EXPECT_THROW(host->setNextServer(asiolink::IOAddress::IPV4_BCAST_ADDRESS()), + isc::BadValue); } // Test that Host constructors initialize client classes from string. @@ -918,6 +945,9 @@ TEST_F(HostTest, toText) { EXPECT_EQ("hwaddr=010203040506 ipv4_subnet_id=1 ipv6_subnet_id=2" " hostname=myhost.example.com" " ipv4_reservation=192.0.2.3" + " siaddr=(no)" + " sname=(empty)" + " file=(empty)" " ipv6_reservation0=2001:db8:1::cafe" " ipv6_reservation1=2001:db8:1::1" " ipv6_reservation2=2001:db8:1:1::/64" @@ -931,6 +961,9 @@ TEST_F(HostTest, toText) { EXPECT_EQ("hwaddr=010203040506 ipv6_subnet_id=2" " hostname=(empty) ipv4_reservation=(no)" + " siaddr=(no)" + " sname=(empty)" + " file=(empty)" " ipv6_reservation0=2001:db8:1::cafe" " ipv6_reservation1=2001:db8:1::1" " ipv6_reservation2=2001:db8:1:1::/64" @@ -945,6 +978,9 @@ TEST_F(HostTest, toText) { "myhost"))); EXPECT_EQ("duid=1112131415 hostname=myhost ipv4_reservation=(no)" + " siaddr=(no)" + " sname=(empty)" + " file=(empty)" " ipv6_reservations=(none)", host->toText()); // Add some classes. @@ -952,6 +988,9 @@ TEST_F(HostTest, toText) { host->addClientClass4("router"); EXPECT_EQ("duid=1112131415 hostname=myhost ipv4_reservation=(no)" + " siaddr=(no)" + " sname=(empty)" + " file=(empty)" " ipv6_reservations=(none)" " dhcp4_class0=modem dhcp4_class1=router", host->toText()); @@ -960,6 +999,9 @@ TEST_F(HostTest, toText) { host->addClientClass6("device"); EXPECT_EQ("duid=1112131415 hostname=myhost ipv4_reservation=(no)" + " siaddr=(no)" + " sname=(empty)" + " file=(empty)" " ipv6_reservations=(none)" " dhcp4_class0=modem dhcp4_class1=router" " dhcp6_class0=device dhcp6_class1=hub",