Browse Source

[5277] Extended server-hostname and boot-file-name (native for subnet4, else inherited)

Francis Dupont 7 years ago
parent
commit
f8aaf42fc5

+ 4 - 0
doc/examples/kea4/shared-network.json

@@ -72,6 +72,8 @@
                     "id": 1,
                     "match-client-id": true,
                     "next-server": "0.0.0.0",
+                    "server-hostname": "",
+                    "boot-file-name": "",
                     "option-data": [ ],
                     "pools": [ ],
                     "rebind-timer": 20,
@@ -91,6 +93,8 @@
                     "id": 2,
                     "match-client-id": true,
                     "next-server": "0.0.0.0",
+                    "server-hostname": "",
+                    "boot-file-name": "",
                     "option-data": [ ],
                     "pools": [ ],
                     "rebind-timer": 20,

+ 12 - 2
doc/guide/dhcp4-srv.xml

@@ -2591,14 +2591,24 @@ It is merely echoed by the server
       not be sent. It may also be set to an empty string, which means the same as if
       it was not defined at all, i.e. use the global value.
       </para>
+      <para>
+      The <command>server-hostname</command> (that conveys a server hostname,
+      can be up to 64 bytes long and will be sent in the sname field) and
+      <command>boot-file-name</command> (that conveys the configuration file,
+      can be up to 128 bytes long and will be sent using file field)
+      directives are handled the same way than <command>next-server</command>.
+      </para>
 
 <screen>
 "Dhcp4": {
-    <userinput>"next-server": "192.0.2.123"</userinput>,
+    <userinput>"next-server": "192.0.2.123",
+    "boot-file-name": "/dev/null"</userinput>,
     ...,
     "subnet4": [
         {
-            <userinput>"next-server": "192.0.2.234"</userinput>,
+            <userinput>"next-server": "192.0.2.234",
+            "server-hostname": "some-name.example.org",
+            "boot-file-name": "bootfile.efi"</userinput>,
             ...
         }
     ]

+ 22 - 0
src/bin/dhcp4/dhcp4_srv.cc

@@ -2165,6 +2165,28 @@ Dhcpv4Srv::setFixedFields(Dhcpv4Exchange& ex) {
         if (!subnet_next_server.isV4Zero()) {
             response->setSiaddr(subnet_next_server);
         }
+
+        const string& sname = subnet->getSname();
+        if (!sname.empty()) {
+            // Converting string to (const uint8_t*, size_t len) format is
+            // tricky. reinterpret_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 life
+            // will be easier.
+            response->setSname(reinterpret_cast<const uint8_t*>(sname.c_str()),
+                               sname.size());
+        }
+
+        const string& filename = subnet->getFilename();
+        if (!filename.empty()) {
+            // Converting string to (const uint8_t*, size_t len) format is
+            // tricky. reinterpret_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 life
+            // will be easier.
+            response->setFile(reinterpret_cast<const uint8_t*>(filename.c_str()),
+                              filename.size());
+        }
     }
 
     // Step 2: Try to set the values based on classes.

+ 3 - 1
src/bin/dhcp4/json_config_parser.cc

@@ -455,7 +455,9 @@ configureDhcp4Server(Dhcpv4Srv&, isc::data::ConstElementPtr config_set,
                  (config_pair.first == "dhcp4o6-port") ||
                  (config_pair.first == "echo-client-id") ||
                  (config_pair.first == "match-client-id") ||
-                 (config_pair.first == "next-server")) {
+                 (config_pair.first == "next-server") ||
+                 (config_pair.first == "server-hostname") ||
+                 (config_pair.first == "boot-file-name")) {
                 continue;
             }
 

+ 71 - 4
src/bin/dhcp4/tests/config_parser_unittest.cc

@@ -1106,14 +1106,16 @@ TEST_F(Dhcp4ParserTest, reconfigureRemoveSubnet) {
 
 /// @todo: implement subnet removal test as part of #3281.
 
-// Checks if the next-server defined as global parameter is taken into
-// consideration.
+// Checks if the next-server and other fixed BOOTP fields defined as
+// global parameter are taken into consideration.
 TEST_F(Dhcp4ParserTest, nextServerGlobal) {
 
     string config = "{ " + genIfaceConfig() + "," +
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"next-server\": \"1.2.3.4\", "
+        "\"server-hostname\": \"foo\", "
+        "\"boot-file-name\": \"bar\", "
         "\"subnet4\": [ { "
         "    \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
         "    \"subnet\": \"192.0.2.0/24\" } ],"
@@ -1135,10 +1137,12 @@ TEST_F(Dhcp4ParserTest, nextServerGlobal) {
         getCfgSubnets4()->selectSubnet(IOAddress("192.0.2.200"));
     ASSERT_TRUE(subnet);
     EXPECT_EQ("1.2.3.4", subnet->getSiaddr().toText());
+    EXPECT_EQ("foo", subnet->getSname());
+    EXPECT_EQ("bar", subnet->getFilename());
 }
 
-// Checks if the next-server defined as subnet parameter is taken into
-// consideration.
+// Checks if the next-server and other fixed BOOTP fields defined as
+// subnet parameter are taken into consideration.
 TEST_F(Dhcp4ParserTest, nextServerSubnet) {
 
     string config = "{ " + genIfaceConfig() + "," +
@@ -1147,6 +1151,8 @@ TEST_F(Dhcp4ParserTest, nextServerSubnet) {
         "\"subnet4\": [ { "
         "    \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
         "    \"next-server\": \"1.2.3.4\", "
+        "    \"server-hostname\": \"foo\", "
+        "    \"boot-file-name\": \"bar\", "
         "    \"subnet\": \"192.0.2.0/24\" } ],"
         "\"valid-lifetime\": 4000 }";
 
@@ -1166,6 +1172,8 @@ TEST_F(Dhcp4ParserTest, nextServerSubnet) {
         getCfgSubnets4()->selectSubnet(IOAddress("192.0.2.200"));
     ASSERT_TRUE(subnet);
     EXPECT_EQ("1.2.3.4", subnet->getSiaddr().toText());
+    EXPECT_EQ("foo", subnet->getSname());
+    EXPECT_EQ("bar", subnet->getFilename());
 }
 
 // Test checks several negative scenarios for next-server configuration: bogus
@@ -1209,12 +1217,43 @@ TEST_F(Dhcp4ParserTest, nextServerNegative) {
         "    \"subnet\": \"192.0.2.0/24\" } ],"
         "\"valid-lifetime\": 4000 }";
 
+    // Config with too large server-hostname
+    string bigsname(Pkt4::MAX_SNAME_LEN + 1, ' ');
+    string config_bogus4 = "{ " + genIfaceConfig() + "," +
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet4\": [ { "
+        "    \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
+        "    \"rebind-timer\": 2000, "
+        "    \"renew-timer\": 1000, "
+        "    \"server-hostname\": \"" + bigsname + "\", " +
+        "    \"subnet\": \"192.0.2.0/24\" } ],"
+        "\"valid-lifetime\": 4000 }";
+
+    // Config with too large boot-file-hostname
+    string bigfilename(Pkt4::MAX_FILE_LEN + 1, ' ');
+    string config_bogus5 = "{ " + genIfaceConfig() + "," +
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet4\": [ { "
+        "    \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
+        "    \"rebind-timer\": 2000, "
+        "    \"renew-timer\": 1000, "
+        "    \"boot-file-name\": \"" + bigfilename + "\", " +
+        "    \"subnet\": \"192.0.2.0/24\" } ],"
+        "\"valid-lifetime\": 4000 }";
+
+
     ConstElementPtr json1;
     ASSERT_NO_THROW(json1 = parseDHCP4(config_bogus1));
     ConstElementPtr json2;
     ASSERT_NO_THROW(json2 = parseDHCP4(config_bogus2));
     ConstElementPtr json3;
     ASSERT_NO_THROW(json3 = parseDHCP4(config_bogus3));
+    ConstElementPtr json4;
+    ASSERT_NO_THROW(json4 = parseDHCP4(config_bogus4));
+    ConstElementPtr json5;
+    ASSERT_NO_THROW(json5 = parseDHCP4(config_bogus5));
 
     // check if returned status is always a failure
     ConstElementPtr status;
@@ -1233,6 +1272,18 @@ TEST_F(Dhcp4ParserTest, nextServerNegative) {
     EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json3));
     checkResult(status, 0);
     EXPECT_FALSE(errorContainsPosition(status, "<string>"));
+
+    CfgMgr::instance().clear();
+
+    EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json4));
+    checkResult(status, 1);
+    EXPECT_TRUE(errorContainsPosition(status, "<string>"));
+
+    CfgMgr::instance().clear();
+
+    EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json5));
+    checkResult(status, 1);
+    EXPECT_TRUE(errorContainsPosition(status, "<string>"));
 }
 
 // Checks if the next-server defined as global value is overridden by subnet
@@ -1243,9 +1294,13 @@ TEST_F(Dhcp4ParserTest, nextServerOverride) {
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"next-server\": \"192.0.0.1\", "
+        "\"server-hostname\": \"nohost\","
+        "\"boot-file-name\": \"nofile\","        
         "\"subnet4\": [ { "
         "    \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
         "    \"next-server\": \"1.2.3.4\", "
+        "    \"server-hostname\": \"some-name.example.org\","
+        "    \"boot-file-name\": \"bootfile.efi\","
         "    \"subnet\": \"192.0.2.0/24\" } ],"
         "\"valid-lifetime\": 4000 }";
 
@@ -1265,6 +1320,8 @@ TEST_F(Dhcp4ParserTest, nextServerOverride) {
         getCfgSubnets4()->selectSubnet(IOAddress("192.0.2.200"));
     ASSERT_TRUE(subnet);
     EXPECT_EQ("1.2.3.4", subnet->getSiaddr().toText());
+    EXPECT_EQ("some-name.example.org", subnet->getSname());
+    EXPECT_EQ("bootfile.efi", subnet->getFilename());
 }
 
 // Check whether it is possible to configure echo-client-id
@@ -5229,6 +5286,8 @@ TEST_F(Dhcp4ParserTest, sharedNetworksDerive) {
         "    \"interface\": \"eth0\",\n"
         "    \"match-client-id\": false,\n"
         "    \"next-server\": \"1.2.3.4\",\n"
+        "    \"server-hostname\": \"foo\",\n"
+        "    \"boot-file-name\": \"bar\",\n"
         "    \"relay\": {\n"
         "        \"ip-address\": \"5.6.7.8\"\n"
         "    },\n"
@@ -5249,6 +5308,8 @@ TEST_F(Dhcp4ParserTest, sharedNetworksDerive) {
         "        \"valid-lifetime\": 400,\n"
         "        \"match-client-id\": true,\n"
         "        \"next-server\": \"11.22.33.44\",\n"
+        "        \"server-hostname\": \"some-name.example.org\",\n"
+        "        \"boot-file-name\": \"bootfile.efi\",\n"
         "        \"relay\": {\n"
         "            \"ip-address\": \"55.66.77.88\"\n"
         "        },\n"
@@ -5299,6 +5360,8 @@ TEST_F(Dhcp4ParserTest, sharedNetworksDerive) {
     EXPECT_EQ("eth0", s->getIface());
     EXPECT_EQ(false, s->getMatchClientId());
     EXPECT_EQ(IOAddress("1.2.3.4"), s->getSiaddr());
+    EXPECT_EQ("foo", s->getSname());
+    EXPECT_EQ("bar", s->getFilename());
     EXPECT_EQ(IOAddress("5.6.7.8"), s->getRelayInfo().addr_);
     EXPECT_EQ(Network::HR_OUT_OF_POOL, s->getHostReservationMode());
 
@@ -5312,6 +5375,8 @@ TEST_F(Dhcp4ParserTest, sharedNetworksDerive) {
     EXPECT_EQ("eth0", s->getIface());
     EXPECT_EQ(true, s->getMatchClientId());
     EXPECT_EQ(IOAddress("11.22.33.44"), s->getSiaddr());
+    EXPECT_EQ("some-name.example.org", s->getSname());
+    EXPECT_EQ("bootfile.efi", s->getFilename());
     EXPECT_EQ(IOAddress("55.66.77.88"), s->getRelayInfo().addr_);
     EXPECT_EQ(Network::HR_DISABLED, s->getHostReservationMode());
 
@@ -5329,6 +5394,8 @@ TEST_F(Dhcp4ParserTest, sharedNetworksDerive) {
     EXPECT_EQ("", s->getIface());
     EXPECT_EQ(true, s->getMatchClientId());
     EXPECT_EQ(IOAddress("0.0.0.0"), s->getSiaddr());
+    EXPECT_TRUE(s->getSname().empty());
+    EXPECT_TRUE(s->getFilename().empty());
     EXPECT_EQ(IOAddress("0.0.0.0"), s->getRelayInfo().addr_);
     EXPECT_EQ(Network::HR_ALL, s->getHostReservationMode());
 }

+ 27 - 1
src/bin/dhcp4/tests/dhcp4_srv_unittest.cc

@@ -1255,7 +1255,7 @@ TEST_F(Dhcpv4SrvTest, siaddr) {
 // Checks if the next-server defined as global value is overridden by subnet
 // specific value and returned in server messages. There's also similar test for
 // checking parser only configuration, see Dhcp4ParserTest.nextServerOverride in
-// config_parser_unittest.cc.
+// config_parser_unittest.cc. This test was extended to other BOOTP fixed fields.
 TEST_F(Dhcpv4SrvTest, nextServerOverride) {
     IfaceMgrTestConfig test_config(true);
     IfaceMgr::instance().openSockets4();
@@ -1270,9 +1270,13 @@ TEST_F(Dhcpv4SrvTest, nextServerOverride) {
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"next-server\": \"192.0.0.1\", "
+        "\"server-hostname\": \"nohost\", "
+        "\"boot-file-name\": \"nofile\", "
         "\"subnet4\": [ { "
         "    \"pools\": [ { \"pool\":  \"192.0.2.1 - 192.0.2.100\" } ],"
         "    \"next-server\": \"1.2.3.4\", "
+        "    \"server-hostname\": \"some-name.example.org\", "
+        "    \"boot-file-name\": \"bootfile.efi\", "
         "    \"subnet\": \"192.0.2.0/24\" } ],"
         "\"valid-lifetime\": 4000 }";
 
@@ -1300,6 +1304,16 @@ TEST_F(Dhcpv4SrvTest, nextServerOverride) {
     EXPECT_EQ(DHCPOFFER, offer->getType());
 
     EXPECT_EQ("1.2.3.4", offer->getSiaddr().toText());
+    std::string sname("some-name.example.org");
+    uint8_t sname_buf[Pkt4::MAX_SNAME_LEN];
+    std::memset(sname_buf, 0, Pkt4::MAX_SNAME_LEN);
+    std::memcpy(sname_buf, sname.c_str(), sname.size());
+    EXPECT_EQ(0, std::memcmp(sname_buf, &offer->getSname()[0], Pkt4::MAX_SNAME_LEN));
+    std::string filename("bootfile.efi");
+    uint8_t filename_buf[Pkt4::MAX_FILE_LEN];
+    std::memset(filename_buf, 0, Pkt4::MAX_FILE_LEN);
+    std::memcpy(filename_buf, filename.c_str(), filename.size());
+    EXPECT_EQ(0, std::memcmp(filename_buf, &offer->getFile()[0], Pkt4::MAX_FILE_LEN));
 }
 
 // Checks if the next-server defined as global value is used in responses
@@ -1320,6 +1334,8 @@ TEST_F(Dhcpv4SrvTest, nextServerGlobal) {
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"next-server\": \"192.0.0.1\", "
+        "\"server-hostname\": \"some-name.example.org\", "
+        "\"boot-file-name\": \"bootfile.efi\", "
         "\"subnet4\": [ { "
         "    \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
         "    \"subnet\": \"192.0.2.0/24\" } ],"
@@ -1349,6 +1365,16 @@ TEST_F(Dhcpv4SrvTest, nextServerGlobal) {
     EXPECT_EQ(DHCPOFFER, offer->getType());
 
     EXPECT_EQ("192.0.0.1", offer->getSiaddr().toText());
+    std::string sname("some-name.example.org");
+    uint8_t sname_buf[Pkt4::MAX_SNAME_LEN];
+    std::memset(sname_buf, 0, Pkt4::MAX_SNAME_LEN);
+    std::memcpy(sname_buf, sname.c_str(), sname.size());
+    EXPECT_EQ(0, std::memcmp(sname_buf, &offer->getSname()[0], Pkt4::MAX_SNAME_LEN));
+    std::string filename("bootfile.efi");
+    uint8_t filename_buf[Pkt4::MAX_FILE_LEN];
+    std::memset(filename_buf, 0, Pkt4::MAX_FILE_LEN);
+    std::memcpy(filename_buf, filename.c_str(), filename.size());
+    EXPECT_EQ(0, std::memcmp(filename_buf, &offer->getFile()[0], Pkt4::MAX_FILE_LEN));
 }
 
 // Checks if server is able to handle a relayed traffic from DOCSIS3.0 modems

+ 2 - 0
src/bin/dhcp4/tests/dora_unittest.cc

@@ -267,6 +267,8 @@ const char* DORA_CONFIGS[] = {
         "},"
         "\"valid-lifetime\": 600,"
         "\"next-server\": \"10.0.0.1\","
+        "\"server-hostname\": \"nohost\","
+        "\"boot-file-name\": \"nofile\","
         "\"subnet4\": [ { "
         "    \"subnet\": \"10.0.0.0/24\", "
         "    \"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ],"

+ 124 - 2
src/bin/dhcp4/tests/get_config_unittest.cc

@@ -223,12 +223,14 @@ const char* EXTRACTED_CONFIGS[] = {
 "    }\n",
     // CONFIGURATION 6
 "{\n"
+"        \"boot-file-name\": \"bar\",\n"
 "        \"interfaces-config\": {\n"
 "            \"interfaces\": [ \"*\" ]\n"
 "        },\n"
 "        \"next-server\": \"1.2.3.4\",\n"
 "        \"rebind-timer\": 2000,\n"
 "        \"renew-timer\": 1000,\n"
+"        \"server-hostname\": \"foo\",\n"
 "        \"subnet4\": [\n"
 "            {\n"
 "                \"pools\": [\n"
@@ -250,12 +252,14 @@ const char* EXTRACTED_CONFIGS[] = {
 "        \"renew-timer\": 1000,\n"
 "        \"subnet4\": [\n"
 "            {\n"
+"                \"boot-file-name\": \"bar\",\n"
 "                \"next-server\": \"1.2.3.4\",\n"
 "                \"pools\": [\n"
 "                    {\n"
 "                        \"pool\": \"192.0.2.1 - 192.0.2.100\"\n"
 "                    }\n"
 "                ],\n"
+"                \"server-hostname\": \"foo\",\n"
 "                \"subnet\": \"192.0.2.0/24\"\n"
 "            }\n"
 "        ],\n"
@@ -263,20 +267,24 @@ const char* EXTRACTED_CONFIGS[] = {
 "    }\n",
     // CONFIGURATION 8
 "{\n"
+"        \"boot-file-name\": \"nofile\",\n"
 "        \"interfaces-config\": {\n"
 "            \"interfaces\": [ \"*\" ]\n"
 "        },\n"
 "        \"next-server\": \"192.0.0.1\",\n"
 "        \"rebind-timer\": 2000,\n"
 "        \"renew-timer\": 1000,\n"
+"        \"server-hostname\": \"nohost\",\n"
 "        \"subnet4\": [\n"
 "            {\n"
+"                \"boot-file-name\": \"bootfile.efi\",\n"
 "                \"next-server\": \"1.2.3.4\",\n"
 "                \"pools\": [\n"
 "                    {\n"
 "                        \"pool\": \"192.0.2.1 - 192.0.2.100\"\n"
 "                    }\n"
 "                ],\n"
+"                \"server-hostname\": \"some-name.example.org\",\n"
 "                \"subnet\": \"192.0.2.0/24\"\n"
 "            }\n"
 "        ],\n"
@@ -1547,7 +1555,8 @@ const char* EXTRACTED_CONFIGS[] = {
 "            }\n"
 "        ],\n"
 "        \"valid-lifetime\": 4000\n"
-"    }\n"};
+"    }\n"
+};
 
 /// @brief unparsed configurations
 const char* UNPARSED_CONFIGS[] = {
@@ -1640,6 +1649,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"4o6-interface\": \"\",\n"
 "                \"4o6-interface-id\": \"\",\n"
 "                \"4o6-subnet\": \"\",\n"
+"                \"boot-file-name\": \"\",\n"
 "                \"id\": 1,\n"
 "                \"match-client-id\": true,\n"
 "                \"next-server\": \"0.0.0.0\",\n"
@@ -1657,6 +1667,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"renew-timer\": 900,\n"
 "                \"reservation-mode\": \"all\",\n"
 "                \"reservations\": [ ],\n"
+"                \"server-hostname\": \"\",\n"
 "                \"subnet\": \"192.0.2.0/24\",\n"
 "                \"valid-lifetime\": 4000\n"
 "            }\n"
@@ -1708,6 +1719,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"4o6-interface\": \"\",\n"
 "                \"4o6-interface-id\": \"\",\n"
 "                \"4o6-subnet\": \"\",\n"
+"                \"boot-file-name\": \"\",\n"
 "                \"id\": 1,\n"
 "                \"match-client-id\": true,\n"
 "                \"next-server\": \"0.0.0.0\",\n"
@@ -1725,6 +1737,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"renew-timer\": 1000,\n"
 "                \"reservation-mode\": \"all\",\n"
 "                \"reservations\": [ ],\n"
+"                \"server-hostname\": \"\",\n"
 "                \"subnet\": \"192.0.2.0/24\",\n"
 "                \"valid-lifetime\": 4000\n"
 "            }\n"
@@ -1776,6 +1789,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"4o6-interface\": \"\",\n"
 "                \"4o6-interface-id\": \"\",\n"
 "                \"4o6-subnet\": \"\",\n"
+"                \"boot-file-name\": \"\",\n"
 "                \"id\": 1,\n"
 "                \"match-client-id\": true,\n"
 "                \"next-server\": \"0.0.0.0\",\n"
@@ -1793,6 +1807,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"renew-timer\": 1000,\n"
 "                \"reservation-mode\": \"all\",\n"
 "                \"reservations\": [ ],\n"
+"                \"server-hostname\": \"\",\n"
 "                \"subnet\": \"192.0.2.0/24\",\n"
 "                \"valid-lifetime\": 4000\n"
 "            }\n"
@@ -1844,6 +1859,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"4o6-interface\": \"\",\n"
 "                \"4o6-interface-id\": \"\",\n"
 "                \"4o6-subnet\": \"\",\n"
+"                \"boot-file-name\": \"\",\n"
 "                \"id\": 1,\n"
 "                \"match-client-id\": true,\n"
 "                \"next-server\": \"0.0.0.0\",\n"
@@ -1861,6 +1877,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"renew-timer\": 1000,\n"
 "                \"reservation-mode\": \"all\",\n"
 "                \"reservations\": [ ],\n"
+"                \"server-hostname\": \"\",\n"
 "                \"subnet\": \"192.0.2.0/24\",\n"
 "                \"valid-lifetime\": 4000\n"
 "            },\n"
@@ -1868,6 +1885,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"4o6-interface\": \"\",\n"
 "                \"4o6-interface-id\": \"\",\n"
 "                \"4o6-subnet\": \"\",\n"
+"                \"boot-file-name\": \"\",\n"
 "                \"id\": 2,\n"
 "                \"match-client-id\": true,\n"
 "                \"next-server\": \"0.0.0.0\",\n"
@@ -1885,6 +1903,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"renew-timer\": 1000,\n"
 "                \"reservation-mode\": \"all\",\n"
 "                \"reservations\": [ ],\n"
+"                \"server-hostname\": \"\",\n"
 "                \"subnet\": \"192.0.3.0/24\",\n"
 "                \"valid-lifetime\": 4000\n"
 "            },\n"
@@ -1892,6 +1911,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"4o6-interface\": \"\",\n"
 "                \"4o6-interface-id\": \"\",\n"
 "                \"4o6-subnet\": \"\",\n"
+"                \"boot-file-name\": \"\",\n"
 "                \"id\": 3,\n"
 "                \"match-client-id\": true,\n"
 "                \"next-server\": \"0.0.0.0\",\n"
@@ -1909,6 +1929,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"renew-timer\": 1000,\n"
 "                \"reservation-mode\": \"all\",\n"
 "                \"reservations\": [ ],\n"
+"                \"server-hostname\": \"\",\n"
 "                \"subnet\": \"192.0.4.0/24\",\n"
 "                \"valid-lifetime\": 4000\n"
 "            },\n"
@@ -1916,6 +1937,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"4o6-interface\": \"\",\n"
 "                \"4o6-interface-id\": \"\",\n"
 "                \"4o6-subnet\": \"\",\n"
+"                \"boot-file-name\": \"\",\n"
 "                \"id\": 4,\n"
 "                \"match-client-id\": true,\n"
 "                \"next-server\": \"0.0.0.0\",\n"
@@ -1933,6 +1955,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"renew-timer\": 1000,\n"
 "                \"reservation-mode\": \"all\",\n"
 "                \"reservations\": [ ],\n"
+"                \"server-hostname\": \"\",\n"
 "                \"subnet\": \"192.0.5.0/24\",\n"
 "                \"valid-lifetime\": 4000\n"
 "            }\n"
@@ -1984,6 +2007,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"4o6-interface\": \"\",\n"
 "                \"4o6-interface-id\": \"\",\n"
 "                \"4o6-subnet\": \"\",\n"
+"                \"boot-file-name\": \"\",\n"
 "                \"id\": 1024,\n"
 "                \"match-client-id\": true,\n"
 "                \"next-server\": \"0.0.0.0\",\n"
@@ -2001,6 +2025,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"renew-timer\": 1000,\n"
 "                \"reservation-mode\": \"all\",\n"
 "                \"reservations\": [ ],\n"
+"                \"server-hostname\": \"\",\n"
 "                \"subnet\": \"192.0.2.0/24\",\n"
 "                \"valid-lifetime\": 4000\n"
 "            },\n"
@@ -2008,6 +2033,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"4o6-interface\": \"\",\n"
 "                \"4o6-interface-id\": \"\",\n"
 "                \"4o6-subnet\": \"\",\n"
+"                \"boot-file-name\": \"\",\n"
 "                \"id\": 100,\n"
 "                \"match-client-id\": true,\n"
 "                \"next-server\": \"0.0.0.0\",\n"
@@ -2025,6 +2051,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"renew-timer\": 1000,\n"
 "                \"reservation-mode\": \"all\",\n"
 "                \"reservations\": [ ],\n"
+"                \"server-hostname\": \"\",\n"
 "                \"subnet\": \"192.0.3.0/24\",\n"
 "                \"valid-lifetime\": 4000\n"
 "            },\n"
@@ -2032,6 +2059,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"4o6-interface\": \"\",\n"
 "                \"4o6-interface-id\": \"\",\n"
 "                \"4o6-subnet\": \"\",\n"
+"                \"boot-file-name\": \"\",\n"
 "                \"id\": 1,\n"
 "                \"match-client-id\": true,\n"
 "                \"next-server\": \"0.0.0.0\",\n"
@@ -2049,6 +2077,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"renew-timer\": 1000,\n"
 "                \"reservation-mode\": \"all\",\n"
 "                \"reservations\": [ ],\n"
+"                \"server-hostname\": \"\",\n"
 "                \"subnet\": \"192.0.4.0/24\",\n"
 "                \"valid-lifetime\": 4000\n"
 "            },\n"
@@ -2056,6 +2085,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"4o6-interface\": \"\",\n"
 "                \"4o6-interface-id\": \"\",\n"
 "                \"4o6-subnet\": \"\",\n"
+"                \"boot-file-name\": \"\",\n"
 "                \"id\": 34,\n"
 "                \"match-client-id\": true,\n"
 "                \"next-server\": \"0.0.0.0\",\n"
@@ -2073,6 +2103,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"renew-timer\": 1000,\n"
 "                \"reservation-mode\": \"all\",\n"
 "                \"reservations\": [ ],\n"
+"                \"server-hostname\": \"\",\n"
 "                \"subnet\": \"192.0.5.0/24\",\n"
 "                \"valid-lifetime\": 4000\n"
 "            }\n"
@@ -2124,6 +2155,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"4o6-interface\": \"\",\n"
 "                \"4o6-interface-id\": \"\",\n"
 "                \"4o6-subnet\": \"\",\n"
+"                \"boot-file-name\": \"bar\",\n"
 "                \"id\": 1,\n"
 "                \"match-client-id\": true,\n"
 "                \"next-server\": \"1.2.3.4\",\n"
@@ -2141,6 +2173,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"renew-timer\": 1000,\n"
 "                \"reservation-mode\": \"all\",\n"
 "                \"reservations\": [ ],\n"
+"                \"server-hostname\": \"foo\",\n"
 "                \"subnet\": \"192.0.2.0/24\",\n"
 "                \"valid-lifetime\": 4000\n"
 "            }\n"
@@ -2192,6 +2225,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"4o6-interface\": \"\",\n"
 "                \"4o6-interface-id\": \"\",\n"
 "                \"4o6-subnet\": \"\",\n"
+"                \"boot-file-name\": \"bar\",\n"
 "                \"id\": 1,\n"
 "                \"match-client-id\": true,\n"
 "                \"next-server\": \"1.2.3.4\",\n"
@@ -2209,6 +2243,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"renew-timer\": 1000,\n"
 "                \"reservation-mode\": \"all\",\n"
 "                \"reservations\": [ ],\n"
+"                \"server-hostname\": \"foo\",\n"
 "                \"subnet\": \"192.0.2.0/24\",\n"
 "                \"valid-lifetime\": 4000\n"
 "            }\n"
@@ -2260,6 +2295,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"4o6-interface\": \"\",\n"
 "                \"4o6-interface-id\": \"\",\n"
 "                \"4o6-subnet\": \"\",\n"
+"                \"boot-file-name\": \"bootfile.efi\",\n"
 "                \"id\": 1,\n"
 "                \"match-client-id\": true,\n"
 "                \"next-server\": \"1.2.3.4\",\n"
@@ -2277,6 +2313,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"renew-timer\": 1000,\n"
 "                \"reservation-mode\": \"all\",\n"
 "                \"reservations\": [ ],\n"
+"                \"server-hostname\": \"some-name.example.org\",\n"
 "                \"subnet\": \"192.0.2.0/24\",\n"
 "                \"valid-lifetime\": 4000\n"
 "            }\n"
@@ -2328,6 +2365,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"4o6-interface\": \"\",\n"
 "                \"4o6-interface-id\": \"\",\n"
 "                \"4o6-subnet\": \"\",\n"
+"                \"boot-file-name\": \"\",\n"
 "                \"id\": 1,\n"
 "                \"match-client-id\": true,\n"
 "                \"next-server\": \"0.0.0.0\",\n"
@@ -2345,6 +2383,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"renew-timer\": 1000,\n"
 "                \"reservation-mode\": \"all\",\n"
 "                \"reservations\": [ ],\n"
+"                \"server-hostname\": \"\",\n"
 "                \"subnet\": \"192.0.2.0/24\",\n"
 "                \"valid-lifetime\": 4000\n"
 "            }\n"
@@ -2396,6 +2435,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"4o6-interface\": \"\",\n"
 "                \"4o6-interface-id\": \"\",\n"
 "                \"4o6-subnet\": \"\",\n"
+"                \"boot-file-name\": \"\",\n"
 "                \"id\": 1,\n"
 "                \"match-client-id\": true,\n"
 "                \"next-server\": \"0.0.0.0\",\n"
@@ -2413,6 +2453,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"renew-timer\": 1000,\n"
 "                \"reservation-mode\": \"all\",\n"
 "                \"reservations\": [ ],\n"
+"                \"server-hostname\": \"\",\n"
 "                \"subnet\": \"192.0.2.0/24\",\n"
 "                \"valid-lifetime\": 4000\n"
 "            }\n"
@@ -2464,6 +2505,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"4o6-interface\": \"\",\n"
 "                \"4o6-interface-id\": \"\",\n"
 "                \"4o6-subnet\": \"\",\n"
+"                \"boot-file-name\": \"\",\n"
 "                \"id\": 1,\n"
 "                \"match-client-id\": true,\n"
 "                \"next-server\": \"0.0.0.0\",\n"
@@ -2481,6 +2523,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"renew-timer\": 1000,\n"
 "                \"reservation-mode\": \"all\",\n"
 "                \"reservations\": [ ],\n"
+"                \"server-hostname\": \"\",\n"
 "                \"subnet\": \"192.0.2.0/24\",\n"
 "                \"valid-lifetime\": 4000\n"
 "            },\n"
@@ -2488,6 +2531,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"4o6-interface\": \"\",\n"
 "                \"4o6-interface-id\": \"\",\n"
 "                \"4o6-subnet\": \"\",\n"
+"                \"boot-file-name\": \"\",\n"
 "                \"id\": 2,\n"
 "                \"match-client-id\": false,\n"
 "                \"next-server\": \"0.0.0.0\",\n"
@@ -2505,6 +2549,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"renew-timer\": 1000,\n"
 "                \"reservation-mode\": \"all\",\n"
 "                \"reservations\": [ ],\n"
+"                \"server-hostname\": \"\",\n"
 "                \"subnet\": \"192.0.3.0/24\",\n"
 "                \"valid-lifetime\": 4000\n"
 "            }\n"
@@ -2556,6 +2601,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"4o6-interface\": \"\",\n"
 "                \"4o6-interface-id\": \"\",\n"
 "                \"4o6-subnet\": \"\",\n"
+"                \"boot-file-name\": \"\",\n"
 "                \"id\": 1,\n"
 "                \"match-client-id\": false,\n"
 "                \"next-server\": \"0.0.0.0\",\n"
@@ -2573,6 +2619,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"renew-timer\": 1000,\n"
 "                \"reservation-mode\": \"all\",\n"
 "                \"reservations\": [ ],\n"
+"                \"server-hostname\": \"\",\n"
 "                \"subnet\": \"192.0.2.0/24\",\n"
 "                \"valid-lifetime\": 4000\n"
 "            },\n"
@@ -2580,6 +2627,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"4o6-interface\": \"\",\n"
 "                \"4o6-interface-id\": \"\",\n"
 "                \"4o6-subnet\": \"\",\n"
+"                \"boot-file-name\": \"\",\n"
 "                \"id\": 2,\n"
 "                \"match-client-id\": true,\n"
 "                \"next-server\": \"0.0.0.0\",\n"
@@ -2597,6 +2645,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"renew-timer\": 1000,\n"
 "                \"reservation-mode\": \"all\",\n"
 "                \"reservations\": [ ],\n"
+"                \"server-hostname\": \"\",\n"
 "                \"subnet\": \"192.0.3.0/24\",\n"
 "                \"valid-lifetime\": 4000\n"
 "            }\n"
@@ -2648,6 +2697,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"4o6-interface\": \"\",\n"
 "                \"4o6-interface-id\": \"\",\n"
 "                \"4o6-subnet\": \"\",\n"
+"                \"boot-file-name\": \"\",\n"
 "                \"id\": 1,\n"
 "                \"match-client-id\": true,\n"
 "                \"next-server\": \"0.0.0.0\",\n"
@@ -2665,6 +2715,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"renew-timer\": 1,\n"
 "                \"reservation-mode\": \"all\",\n"
 "                \"reservations\": [ ],\n"
+"                \"server-hostname\": \"\",\n"
 "                \"subnet\": \"192.0.2.0/24\",\n"
 "                \"valid-lifetime\": 4\n"
 "            }\n"
@@ -2716,6 +2767,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"4o6-interface\": \"\",\n"
 "                \"4o6-interface-id\": \"\",\n"
 "                \"4o6-subnet\": \"\",\n"
+"                \"boot-file-name\": \"\",\n"
 "                \"id\": 1,\n"
 "                \"match-client-id\": true,\n"
 "                \"next-server\": \"0.0.0.0\",\n"
@@ -2737,6 +2789,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"renew-timer\": 1000,\n"
 "                \"reservation-mode\": \"all\",\n"
 "                \"reservations\": [ ],\n"
+"                \"server-hostname\": \"\",\n"
 "                \"subnet\": \"192.0.2.0/24\",\n"
 "                \"valid-lifetime\": 4000\n"
 "            },\n"
@@ -2744,6 +2797,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"4o6-interface\": \"\",\n"
 "                \"4o6-interface-id\": \"\",\n"
 "                \"4o6-subnet\": \"\",\n"
+"                \"boot-file-name\": \"\",\n"
 "                \"id\": 2,\n"
 "                \"match-client-id\": true,\n"
 "                \"next-server\": \"0.0.0.0\",\n"
@@ -2765,6 +2819,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"renew-timer\": 1000,\n"
 "                \"reservation-mode\": \"all\",\n"
 "                \"reservations\": [ ],\n"
+"                \"server-hostname\": \"\",\n"
 "                \"subnet\": \"192.0.3.0/24\",\n"
 "                \"valid-lifetime\": 4000\n"
 "            }\n"
@@ -2816,6 +2871,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"4o6-interface\": \"\",\n"
 "                \"4o6-interface-id\": \"\",\n"
 "                \"4o6-subnet\": \"\",\n"
+"                \"boot-file-name\": \"\",\n"
 "                \"id\": 1,\n"
 "                \"match-client-id\": true,\n"
 "                \"next-server\": \"0.0.0.0\",\n"
@@ -2833,6 +2889,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"renew-timer\": 1000,\n"
 "                \"reservation-mode\": \"all\",\n"
 "                \"reservations\": [ ],\n"
+"                \"server-hostname\": \"\",\n"
 "                \"subnet\": \"192.0.2.0/24\",\n"
 "                \"valid-lifetime\": 4000\n"
 "            }\n"
@@ -3281,6 +3338,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"4o6-interface\": \"\",\n"
 "                \"4o6-interface-id\": \"\",\n"
 "                \"4o6-subnet\": \"\",\n"
+"                \"boot-file-name\": \"\",\n"
 "                \"id\": 1,\n"
 "                \"match-client-id\": true,\n"
 "                \"next-server\": \"0.0.0.0\",\n"
@@ -3298,6 +3356,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"renew-timer\": 1000,\n"
 "                \"reservation-mode\": \"all\",\n"
 "                \"reservations\": [ ],\n"
+"                \"server-hostname\": \"\",\n"
 "                \"subnet\": \"192.0.2.0/24\",\n"
 "                \"valid-lifetime\": 4000\n"
 "            }\n"
@@ -3349,6 +3408,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"4o6-interface\": \"\",\n"
 "                \"4o6-interface-id\": \"\",\n"
 "                \"4o6-subnet\": \"\",\n"
+"                \"boot-file-name\": \"\",\n"
 "                \"id\": 1,\n"
 "                \"match-client-id\": true,\n"
 "                \"next-server\": \"0.0.0.0\",\n"
@@ -3383,6 +3443,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"renew-timer\": 1000,\n"
 "                \"reservation-mode\": \"all\",\n"
 "                \"reservations\": [ ],\n"
+"                \"server-hostname\": \"\",\n"
 "                \"subnet\": \"192.0.2.0/24\",\n"
 "                \"valid-lifetime\": 4000\n"
 "            }\n"
@@ -3461,6 +3522,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"4o6-interface\": \"\",\n"
 "                \"4o6-interface-id\": \"\",\n"
 "                \"4o6-subnet\": \"\",\n"
+"                \"boot-file-name\": \"\",\n"
 "                \"id\": 1,\n"
 "                \"match-client-id\": true,\n"
 "                \"next-server\": \"0.0.0.0\",\n"
@@ -3478,6 +3540,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"renew-timer\": 1000,\n"
 "                \"reservation-mode\": \"all\",\n"
 "                \"reservations\": [ ],\n"
+"                \"server-hostname\": \"\",\n"
 "                \"subnet\": \"192.0.2.0/24\",\n"
 "                \"valid-lifetime\": 4000\n"
 "            }\n"
@@ -3661,6 +3724,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"4o6-interface\": \"\",\n"
 "                \"4o6-interface-id\": \"\",\n"
 "                \"4o6-subnet\": \"\",\n"
+"                \"boot-file-name\": \"\",\n"
 "                \"id\": 1,\n"
 "                \"match-client-id\": true,\n"
 "                \"next-server\": \"0.0.0.0\",\n"
@@ -3678,6 +3742,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"renew-timer\": 1000,\n"
 "                \"reservation-mode\": \"all\",\n"
 "                \"reservations\": [ ],\n"
+"                \"server-hostname\": \"\",\n"
 "                \"subnet\": \"192.0.2.0/24\",\n"
 "                \"valid-lifetime\": 3000\n"
 "            }\n"
@@ -3738,6 +3803,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"4o6-interface\": \"\",\n"
 "                \"4o6-interface-id\": \"\",\n"
 "                \"4o6-subnet\": \"\",\n"
+"                \"boot-file-name\": \"\",\n"
 "                \"id\": 1,\n"
 "                \"match-client-id\": true,\n"
 "                \"next-server\": \"0.0.0.0\",\n"
@@ -3772,6 +3838,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"renew-timer\": 1000,\n"
 "                \"reservation-mode\": \"all\",\n"
 "                \"reservations\": [ ],\n"
+"                \"server-hostname\": \"\",\n"
 "                \"subnet\": \"192.0.2.0/24\",\n"
 "                \"valid-lifetime\": 4000\n"
 "            }\n"
@@ -3823,6 +3890,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"4o6-interface\": \"\",\n"
 "                \"4o6-interface-id\": \"\",\n"
 "                \"4o6-subnet\": \"\",\n"
+"                \"boot-file-name\": \"\",\n"
 "                \"id\": 1,\n"
 "                \"match-client-id\": true,\n"
 "                \"next-server\": \"0.0.0.0\",\n"
@@ -3849,6 +3917,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"renew-timer\": 1000,\n"
 "                \"reservation-mode\": \"all\",\n"
 "                \"reservations\": [ ],\n"
+"                \"server-hostname\": \"\",\n"
 "                \"subnet\": \"192.0.2.0/24\",\n"
 "                \"valid-lifetime\": 4000\n"
 "            },\n"
@@ -3856,6 +3925,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"4o6-interface\": \"\",\n"
 "                \"4o6-interface-id\": \"\",\n"
 "                \"4o6-subnet\": \"\",\n"
+"                \"boot-file-name\": \"\",\n"
 "                \"id\": 2,\n"
 "                \"match-client-id\": true,\n"
 "                \"next-server\": \"0.0.0.0\",\n"
@@ -3882,6 +3952,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"renew-timer\": 1000,\n"
 "                \"reservation-mode\": \"all\",\n"
 "                \"reservations\": [ ],\n"
+"                \"server-hostname\": \"\",\n"
 "                \"subnet\": \"192.0.3.0/24\",\n"
 "                \"valid-lifetime\": 4000\n"
 "            }\n"
@@ -3933,6 +4004,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"4o6-interface\": \"\",\n"
 "                \"4o6-interface-id\": \"\",\n"
 "                \"4o6-subnet\": \"\",\n"
+"                \"boot-file-name\": \"\",\n"
 "                \"id\": 1,\n"
 "                \"match-client-id\": true,\n"
 "                \"next-server\": \"0.0.0.0\",\n"
@@ -3967,6 +4039,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"renew-timer\": 1000,\n"
 "                \"reservation-mode\": \"all\",\n"
 "                \"reservations\": [ ],\n"
+"                \"server-hostname\": \"\",\n"
 "                \"subnet\": \"192.0.2.0/24\",\n"
 "                \"valid-lifetime\": 4000\n"
 "            }\n"
@@ -4018,6 +4091,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"4o6-interface\": \"\",\n"
 "                \"4o6-interface-id\": \"\",\n"
 "                \"4o6-subnet\": \"\",\n"
+"                \"boot-file-name\": \"\",\n"
 "                \"id\": 1,\n"
 "                \"match-client-id\": true,\n"
 "                \"next-server\": \"0.0.0.0\",\n"
@@ -4057,6 +4131,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"renew-timer\": 1000,\n"
 "                \"reservation-mode\": \"all\",\n"
 "                \"reservations\": [ ],\n"
+"                \"server-hostname\": \"\",\n"
 "                \"subnet\": \"192.0.2.0/24\",\n"
 "                \"valid-lifetime\": 4000\n"
 "            }\n"
@@ -4231,6 +4306,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"4o6-interface\": \"\",\n"
 "                \"4o6-interface-id\": \"\",\n"
 "                \"4o6-subnet\": \"\",\n"
+"                \"boot-file-name\": \"\",\n"
 "                \"id\": 1,\n"
 "                \"match-client-id\": true,\n"
 "                \"next-server\": \"0.0.0.0\",\n"
@@ -4248,6 +4324,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"renew-timer\": 1000,\n"
 "                \"reservation-mode\": \"all\",\n"
 "                \"reservations\": [ ],\n"
+"                \"server-hostname\": \"\",\n"
 "                \"subnet\": \"192.0.2.0/24\",\n"
 "                \"valid-lifetime\": 3000\n"
 "            }\n"
@@ -4314,6 +4391,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"4o6-interface\": \"\",\n"
 "                \"4o6-interface-id\": \"\",\n"
 "                \"4o6-subnet\": \"\",\n"
+"                \"boot-file-name\": \"\",\n"
 "                \"id\": 1,\n"
 "                \"match-client-id\": true,\n"
 "                \"next-server\": \"0.0.0.0\",\n"
@@ -4331,6 +4409,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"renew-timer\": 1000,\n"
 "                \"reservation-mode\": \"all\",\n"
 "                \"reservations\": [ ],\n"
+"                \"server-hostname\": \"\",\n"
 "                \"subnet\": \"192.0.2.0/24\",\n"
 "                \"valid-lifetime\": 4000\n"
 "            }\n"
@@ -4401,6 +4480,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"4o6-interface\": \"\",\n"
 "                \"4o6-interface-id\": \"\",\n"
 "                \"4o6-subnet\": \"\",\n"
+"                \"boot-file-name\": \"\",\n"
 "                \"id\": 1,\n"
 "                \"match-client-id\": true,\n"
 "                \"next-server\": \"0.0.0.0\",\n"
@@ -4418,6 +4498,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"renew-timer\": 1000,\n"
 "                \"reservation-mode\": \"all\",\n"
 "                \"reservations\": [ ],\n"
+"                \"server-hostname\": \"\",\n"
 "                \"subnet\": \"192.0.2.0/24\",\n"
 "                \"valid-lifetime\": 4000\n"
 "            }\n"
@@ -4555,6 +4636,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"4o6-interface\": \"\",\n"
 "                \"4o6-interface-id\": \"\",\n"
 "                \"4o6-subnet\": \"\",\n"
+"                \"boot-file-name\": \"\",\n"
 "                \"id\": 1,\n"
 "                \"match-client-id\": true,\n"
 "                \"next-server\": \"0.0.0.0\",\n"
@@ -4572,6 +4654,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"renew-timer\": 1000,\n"
 "                \"reservation-mode\": \"all\",\n"
 "                \"reservations\": [ ],\n"
+"                \"server-hostname\": \"\",\n"
 "                \"subnet\": \"192.0.2.0/24\",\n"
 "                \"valid-lifetime\": 4000\n"
 "            }\n"
@@ -4623,6 +4706,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"4o6-interface\": \"\",\n"
 "                \"4o6-interface-id\": \"\",\n"
 "                \"4o6-subnet\": \"\",\n"
+"                \"boot-file-name\": \"\",\n"
 "                \"id\": 1,\n"
 "                \"match-client-id\": true,\n"
 "                \"next-server\": \"0.0.0.0\",\n"
@@ -4640,6 +4724,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"renew-timer\": 1,\n"
 "                \"reservation-mode\": \"all\",\n"
 "                \"reservations\": [ ],\n"
+"                \"server-hostname\": \"\",\n"
 "                \"subnet\": \"192.0.2.0/24\",\n"
 "                \"valid-lifetime\": 4\n"
 "            }\n"
@@ -4691,6 +4776,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"4o6-interface\": \"\",\n"
 "                \"4o6-interface-id\": \"\",\n"
 "                \"4o6-subnet\": \"\",\n"
+"                \"boot-file-name\": \"\",\n"
 "                \"id\": 123,\n"
 "                \"match-client-id\": true,\n"
 "                \"next-server\": \"0.0.0.0\",\n"
@@ -4708,6 +4794,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"renew-timer\": 1000,\n"
 "                \"reservation-mode\": \"all\",\n"
 "                \"reservations\": [ ],\n"
+"                \"server-hostname\": \"\",\n"
 "                \"subnet\": \"192.0.2.0/24\",\n"
 "                \"valid-lifetime\": 4000\n"
 "            },\n"
@@ -4715,6 +4802,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"4o6-interface\": \"\",\n"
 "                \"4o6-interface-id\": \"\",\n"
 "                \"4o6-subnet\": \"\",\n"
+"                \"boot-file-name\": \"\",\n"
 "                \"id\": 234,\n"
 "                \"match-client-id\": true,\n"
 "                \"next-server\": \"0.0.0.0\",\n"
@@ -4787,6 +4875,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                        \"server-hostname\": \"\"\n"
 "                    }\n"
 "                ],\n"
+"                \"server-hostname\": \"\",\n"
 "                \"subnet\": \"192.0.3.0/24\",\n"
 "                \"valid-lifetime\": 4000\n"
 "            },\n"
@@ -4794,6 +4883,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"4o6-interface\": \"\",\n"
 "                \"4o6-interface-id\": \"\",\n"
 "                \"4o6-subnet\": \"\",\n"
+"                \"boot-file-name\": \"\",\n"
 "                \"id\": 542,\n"
 "                \"match-client-id\": true,\n"
 "                \"next-server\": \"0.0.0.0\",\n"
@@ -4859,6 +4949,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                        \"server-hostname\": \"\"\n"
 "                    }\n"
 "                ],\n"
+"                \"server-hostname\": \"\",\n"
 "                \"subnet\": \"192.0.4.0/24\",\n"
 "                \"valid-lifetime\": 4000\n"
 "            }\n"
@@ -4920,6 +5011,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"4o6-interface\": \"\",\n"
 "                \"4o6-interface-id\": \"\",\n"
 "                \"4o6-subnet\": \"\",\n"
+"                \"boot-file-name\": \"\",\n"
 "                \"id\": 234,\n"
 "                \"match-client-id\": true,\n"
 "                \"next-server\": \"0.0.0.0\",\n"
@@ -4957,6 +5049,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                        \"server-hostname\": \"\"\n"
 "                    }\n"
 "                ],\n"
+"                \"server-hostname\": \"\",\n"
 "                \"subnet\": \"192.0.3.0/24\",\n"
 "                \"valid-lifetime\": 4000\n"
 "            }\n"
@@ -5008,6 +5101,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"4o6-interface\": \"\",\n"
 "                \"4o6-interface-id\": \"\",\n"
 "                \"4o6-subnet\": \"\",\n"
+"                \"boot-file-name\": \"\",\n"
 "                \"id\": 1,\n"
 "                \"match-client-id\": true,\n"
 "                \"next-server\": \"0.0.0.0\",\n"
@@ -5025,6 +5119,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"renew-timer\": 1000,\n"
 "                \"reservation-mode\": \"all\",\n"
 "                \"reservations\": [ ],\n"
+"                \"server-hostname\": \"\",\n"
 "                \"subnet\": \"192.0.2.0/24\",\n"
 "                \"valid-lifetime\": 4000\n"
 "            },\n"
@@ -5032,6 +5127,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"4o6-interface\": \"\",\n"
 "                \"4o6-interface-id\": \"\",\n"
 "                \"4o6-subnet\": \"\",\n"
+"                \"boot-file-name\": \"\",\n"
 "                \"id\": 2,\n"
 "                \"match-client-id\": true,\n"
 "                \"next-server\": \"0.0.0.0\",\n"
@@ -5049,6 +5145,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"renew-timer\": 1000,\n"
 "                \"reservation-mode\": \"out-of-pool\",\n"
 "                \"reservations\": [ ],\n"
+"                \"server-hostname\": \"\",\n"
 "                \"subnet\": \"192.0.3.0/24\",\n"
 "                \"valid-lifetime\": 4000\n"
 "            },\n"
@@ -5056,6 +5153,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"4o6-interface\": \"\",\n"
 "                \"4o6-interface-id\": \"\",\n"
 "                \"4o6-subnet\": \"\",\n"
+"                \"boot-file-name\": \"\",\n"
 "                \"id\": 3,\n"
 "                \"match-client-id\": true,\n"
 "                \"next-server\": \"0.0.0.0\",\n"
@@ -5073,6 +5171,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"renew-timer\": 1000,\n"
 "                \"reservation-mode\": \"disabled\",\n"
 "                \"reservations\": [ ],\n"
+"                \"server-hostname\": \"\",\n"
 "                \"subnet\": \"192.0.4.0/24\",\n"
 "                \"valid-lifetime\": 4000\n"
 "            },\n"
@@ -5080,6 +5179,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"4o6-interface\": \"\",\n"
 "                \"4o6-interface-id\": \"\",\n"
 "                \"4o6-subnet\": \"\",\n"
+"                \"boot-file-name\": \"\",\n"
 "                \"id\": 4,\n"
 "                \"match-client-id\": true,\n"
 "                \"next-server\": \"0.0.0.0\",\n"
@@ -5097,6 +5197,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"renew-timer\": 1000,\n"
 "                \"reservation-mode\": \"all\",\n"
 "                \"reservations\": [ ],\n"
+"                \"server-hostname\": \"\",\n"
 "                \"subnet\": \"192.0.5.0/24\",\n"
 "                \"valid-lifetime\": 4000\n"
 "            }\n"
@@ -5320,6 +5421,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"4o6-interface\": \"\",\n"
 "                \"4o6-interface-id\": \"\",\n"
 "                \"4o6-subnet\": \"\",\n"
+"                \"boot-file-name\": \"\",\n"
 "                \"id\": 1,\n"
 "                \"match-client-id\": true,\n"
 "                \"next-server\": \"0.0.0.0\",\n"
@@ -5337,6 +5439,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"renew-timer\": 1000,\n"
 "                \"reservation-mode\": \"all\",\n"
 "                \"reservations\": [ ],\n"
+"                \"server-hostname\": \"\",\n"
 "                \"subnet\": \"192.0.2.0/24\",\n"
 "                \"valid-lifetime\": 4000\n"
 "            }\n"
@@ -5388,6 +5491,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"4o6-interface\": \"\",\n"
 "                \"4o6-interface-id\": \"\",\n"
 "                \"4o6-subnet\": \"2001:db8::123/45\",\n"
+"                \"boot-file-name\": \"\",\n"
 "                \"id\": 1,\n"
 "                \"match-client-id\": true,\n"
 "                \"next-server\": \"0.0.0.0\",\n"
@@ -5405,6 +5509,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"renew-timer\": 1000,\n"
 "                \"reservation-mode\": \"all\",\n"
 "                \"reservations\": [ ],\n"
+"                \"server-hostname\": \"\",\n"
 "                \"subnet\": \"192.0.2.0/24\",\n"
 "                \"valid-lifetime\": 4000\n"
 "            }\n"
@@ -5456,6 +5561,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"4o6-interface\": \"ethX\",\n"
 "                \"4o6-interface-id\": \"\",\n"
 "                \"4o6-subnet\": \"\",\n"
+"                \"boot-file-name\": \"\",\n"
 "                \"id\": 1,\n"
 "                \"match-client-id\": true,\n"
 "                \"next-server\": \"0.0.0.0\",\n"
@@ -5473,6 +5579,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"renew-timer\": 1000,\n"
 "                \"reservation-mode\": \"all\",\n"
 "                \"reservations\": [ ],\n"
+"                \"server-hostname\": \"\",\n"
 "                \"subnet\": \"192.0.2.0/24\",\n"
 "                \"valid-lifetime\": 4000\n"
 "            }\n"
@@ -5524,6 +5631,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"4o6-interface\": \"ethX\",\n"
 "                \"4o6-interface-id\": \"\",\n"
 "                \"4o6-subnet\": \"2001:db8::543/21\",\n"
+"                \"boot-file-name\": \"\",\n"
 "                \"id\": 1,\n"
 "                \"match-client-id\": true,\n"
 "                \"next-server\": \"0.0.0.0\",\n"
@@ -5541,6 +5649,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"renew-timer\": 1000,\n"
 "                \"reservation-mode\": \"all\",\n"
 "                \"reservations\": [ ],\n"
+"                \"server-hostname\": \"\",\n"
 "                \"subnet\": \"192.0.2.0/24\",\n"
 "                \"valid-lifetime\": 4000\n"
 "            }\n"
@@ -5592,6 +5701,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"4o6-interface\": \"\",\n"
 "                \"4o6-interface-id\": \"vlan123\",\n"
 "                \"4o6-subnet\": \"\",\n"
+"                \"boot-file-name\": \"\",\n"
 "                \"id\": 1,\n"
 "                \"match-client-id\": true,\n"
 "                \"next-server\": \"0.0.0.0\",\n"
@@ -5609,6 +5719,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"renew-timer\": 1000,\n"
 "                \"reservation-mode\": \"all\",\n"
 "                \"reservations\": [ ],\n"
+"                \"server-hostname\": \"\",\n"
 "                \"subnet\": \"192.0.2.0/24\",\n"
 "                \"valid-lifetime\": 4000\n"
 "            }\n"
@@ -5686,6 +5797,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"4o6-interface\": \"\",\n"
 "                \"4o6-interface-id\": \"\",\n"
 "                \"4o6-subnet\": \"\",\n"
+"                \"boot-file-name\": \"\",\n"
 "                \"id\": 1,\n"
 "                \"match-client-id\": true,\n"
 "                \"next-server\": \"0.0.0.0\",\n"
@@ -5703,6 +5815,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"renew-timer\": 1000,\n"
 "                \"reservation-mode\": \"all\",\n"
 "                \"reservations\": [ ],\n"
+"                \"server-hostname\": \"\",\n"
 "                \"subnet\": \"192.0.2.0/24\",\n"
 "                \"valid-lifetime\": 4000\n"
 "            }\n"
@@ -5754,6 +5867,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"4o6-interface\": \"\",\n"
 "                \"4o6-interface-id\": \"\",\n"
 "                \"4o6-subnet\": \"\",\n"
+"                \"boot-file-name\": \"\",\n"
 "                \"id\": 1,\n"
 "                \"match-client-id\": true,\n"
 "                \"next-server\": \"0.0.0.0\",\n"
@@ -5771,6 +5885,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"renew-timer\": 1000,\n"
 "                \"reservation-mode\": \"all\",\n"
 "                \"reservations\": [ ],\n"
+"                \"server-hostname\": \"\",\n"
 "                \"subnet\": \"192.0.2.0/24\",\n"
 "                \"valid-lifetime\": 4000\n"
 "            }\n"
@@ -5822,6 +5937,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"4o6-interface\": \"\",\n"
 "                \"4o6-interface-id\": \"\",\n"
 "                \"4o6-subnet\": \"\",\n"
+"                \"boot-file-name\": \"\",\n"
 "                \"id\": 1,\n"
 "                \"match-client-id\": true,\n"
 "                \"next-server\": \"0.0.0.0\",\n"
@@ -5840,6 +5956,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"renew-timer\": 1000,\n"
 "                \"reservation-mode\": \"all\",\n"
 "                \"reservations\": [ ],\n"
+"                \"server-hostname\": \"\",\n"
 "                \"subnet\": \"192.0.2.0/24\",\n"
 "                \"valid-lifetime\": 4000\n"
 "            }\n"
@@ -5891,6 +6008,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"4o6-interface\": \"\",\n"
 "                \"4o6-interface-id\": \"\",\n"
 "                \"4o6-subnet\": \"\",\n"
+"                \"boot-file-name\": \"\",\n"
 "                \"id\": 1,\n"
 "                \"match-client-id\": true,\n"
 "                \"next-server\": \"0.0.0.0\",\n"
@@ -5913,6 +6031,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"renew-timer\": 1000,\n"
 "                \"reservation-mode\": \"all\",\n"
 "                \"reservations\": [ ],\n"
+"                \"server-hostname\": \"\",\n"
 "                \"subnet\": \"192.0.2.0/24\",\n"
 "                \"valid-lifetime\": 4000\n"
 "            }\n"
@@ -5964,6 +6083,7 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"4o6-interface\": \"\",\n"
 "                \"4o6-interface-id\": \"\",\n"
 "                \"4o6-subnet\": \"\",\n"
+"                \"boot-file-name\": \"\",\n"
 "                \"id\": 1,\n"
 "                \"match-client-id\": true,\n"
 "                \"next-server\": \"0.0.0.0\",\n"
@@ -5986,11 +6106,13 @@ const char* UNPARSED_CONFIGS[] = {
 "                \"renew-timer\": 1000,\n"
 "                \"reservation-mode\": \"all\",\n"
 "                \"reservations\": [ ],\n"
+"                \"server-hostname\": \"\",\n"
 "                \"subnet\": \"192.0.2.0/24\",\n"
 "                \"valid-lifetime\": 4000\n"
 "            }\n"
 "        ]\n"
-"    }\n"};
+"    }\n"
+};
 
 /// @brief the number of configurations
 const size_t max_config_counter = sizeof(EXTRACTED_CONFIGS) / sizeof(char*);

+ 2 - 0
src/bin/dhcp4/tests/inform_unittest.cc

@@ -109,6 +109,8 @@ const char* INFORM_CONFIGS[] = {
         "},"
         "\"valid-lifetime\": 600,"
         "\"next-server\": \"10.0.0.1\","
+        "\"server-hostname\": \"nohost\","
+        "\"server-hostname\": \"nofile\","
         "\"subnet4\": [ { "
         "    \"subnet\": \"192.0.2.0/24\", "
         "    \"reservations\": [ "

+ 26 - 0
src/lib/dhcpsrv/parsers/dhcp_parsers.cc

@@ -708,6 +708,32 @@ Subnet4ConfigParser::initSubnet(data::ConstElementPtr params,
                   << next_server << "(" << pos << ")");
     }
 
+    // Set server-hostname.
+    std::string sname = getString(params, "server-hostname");
+    if (!sname.empty()) {
+        if (sname.length() >= Pkt4::MAX_SNAME_LEN) {
+            ConstElementPtr error = params->get("server-hostname");
+            isc_throw(DhcpConfigError, "server-hostname must be at most "
+                      << Pkt4::MAX_SNAME_LEN - 1 << " bytes long, it is "
+                      << sname.length() << " ("
+                      << error->getPosition() << ")");
+        }
+        subnet4->setSname(sname);
+    }
+
+    // Set boot-file-name.
+    std::string filename =getString(params, "boot-file-name");
+    if (!filename.empty()) {
+        if (filename.length() > Pkt4::MAX_FILE_LEN) {
+            ConstElementPtr error = params->get("boot-file-name");
+            isc_throw(DhcpConfigError, "boot-file-name must be at most "
+                      << Pkt4::MAX_FILE_LEN - 1 << " bytes long, it is "
+                      << filename.length() << " ("
+                      << error->getPosition() << ")");
+        }
+        subnet4->setFilename(filename);
+    }
+
     // Get interface name. If it is defined, then the subnet is available
     // directly over specified network interface.
     std::string iface = getString(params, "interface");

+ 5 - 1
src/lib/dhcpsrv/parsers/simple_parser4.cc

@@ -63,7 +63,9 @@ const SimpleDefaults SimpleParser4::GLOBAL4_DEFAULTS = {
     { "dhcp4o6-port",             Element::integer, "0" },
     { "echo-client-id",           Element::boolean, "true" },
     { "match-client-id",          Element::boolean, "true" },
-    { "next-server",              Element::string,  "0.0.0.0" }
+    { "next-server",              Element::string,  "0.0.0.0" },
+    { "server-hostname",          Element::string,  "" },
+    { "boot-file-name",           Element::string,  "" }
 };
 
 /// @brief This table defines default values for each IPv4 subnet.
@@ -118,6 +120,7 @@ const SimpleDefaults SimpleParser4::IFACE4_DEFAULTS = {
 /// This list is also used for inheriting from global to shared networks
 /// and from shared networks to subnets within it.
 const ParamsList SimpleParser4::INHERIT_TO_SUBNET4 = {
+    "boot-file-name",
     "client-class",
     "interface",
     "match-client-id",
@@ -126,6 +129,7 @@ const ParamsList SimpleParser4::INHERIT_TO_SUBNET4 = {
     "relay",
     "renew-timer",
     "reservation-mode",
+    "server-hostname",
     "valid-lifetime"
 };
 

+ 21 - 0
src/lib/dhcpsrv/subnet.cc

@@ -239,6 +239,21 @@ isc::asiolink::IOAddress Subnet4::getSiaddr() const {
     return (siaddr_);
 }
 
+void Subnet4::setSname(const std::string& sname) {
+    sname_ = sname;
+}
+
+const std::string& Subnet4::getSname() const {
+    return (sname_);
+}
+void Subnet4::setFilename(const std::string& filename) {
+    filename_ = filename;
+}
+
+const std::string& Subnet4::getFilename() const {
+    return (filename_);
+}
+
 const PoolCollection& Subnet::getPools(Lease::Type type) const {
     // check if the type is valid (and throw if it isn't)
     checkType(type);
@@ -574,6 +589,12 @@ Subnet4::toElement() const {
     // Set next-server
     map->set("next-server", Element::create(getSiaddr().toText()));
 
+    // Set server-hostname
+    map->set("server-hostname", Element::create(getSname()));
+
+    // Set boot-file-name
+    map->set("boot-file-name",Element::create(getFilename()));
+
     // Set pools
     const PoolCollection& pools = getPools(Lease::TYPE_V4);
     ElementPtr pool_list = Element::createList();

+ 26 - 0
src/lib/dhcpsrv/subnet.h

@@ -457,6 +457,26 @@ public:
     /// @return siaddr value
     isc::asiolink::IOAddress getSiaddr() const;
 
+    /// @brief Sets server hostname for the Subnet4 
+    ///
+    /// Will be used for server hostname field (may be empty if not defined)
+    void setSname(const std::string& sname);
+
+    /// @brief Returns server hostname for this subnet
+    ///
+    /// @return server hostname value
+    const std::string& getSname() const;
+
+    /// @brief Sets boot file name for the Subnet4 
+    ///
+    /// Will be used for boot file name (may be empty if not defined)
+    void setFilename(const std::string& filename);
+
+    /// @brief Returns boot file name for this subnet
+    ///
+    /// @return boot file name value
+    const std::string& getFilename() const;
+
     /// @brief Returns DHCP4o6 configuration parameters.
     ///
     /// This structure is always available. If the 4o6 is not enabled, its
@@ -497,6 +517,12 @@ private:
     /// @brief siaddr value for this subnet
     isc::asiolink::IOAddress siaddr_;
 
+    /// @brief server hostname for this subnet
+    std::string sname_;
+
+    /// @brief boot file name for this subnet
+    std::string filename_;
+
     /// @brief All the information related to DHCP4o6
     Cfg4o6 dhcp4o6_;
 };

+ 8 - 0
src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc

@@ -748,6 +748,8 @@ TEST(CfgSubnets4Test, unparseSubnet) {
         "    \"relay\": { \"ip-address\": \"0.0.0.0\" },\n"
         "    \"match-client-id\": true,\n"
         "    \"next-server\": \"0.0.0.0\",\n"
+        "    \"server-hostname\": \"\",\n"
+        "    \"boot-file-name\": \"\",\n"
         "    \"renew-timer\": 1,\n"
         "    \"rebind-timer\": 2,\n"
         "    \"valid-lifetime\": 3,\n"
@@ -765,6 +767,8 @@ TEST(CfgSubnets4Test, unparseSubnet) {
         "    \"interface\": \"lo\",\n"
         "    \"match-client-id\": true,\n"
         "    \"next-server\": \"0.0.0.0\",\n"
+        "    \"server-hostname\": \"\",\n"
+        "    \"boot-file-name\": \"\",\n"
         "    \"renew-timer\": 1,\n"
         "    \"rebind-timer\": 2,\n"
         "    \"valid-lifetime\": 3,\n"
@@ -781,6 +785,8 @@ TEST(CfgSubnets4Test, unparseSubnet) {
         "    \"interface\": \"eth1\",\n"
         "    \"match-client-id\": true,\n"
         "    \"next-server\": \"0.0.0.0\",\n"
+        "    \"server-hostname\": \"\",\n"
+        "    \"boot-file-name\": \"\",\n"
         "    \"renew-timer\": 1,\n"
         "    \"rebind-timer\": 2,\n"
         "    \"valid-lifetime\": 3,\n"
@@ -815,6 +821,8 @@ TEST(CfgSubnets4Test, unparsePool) {
         "    \"relay\": { \"ip-address\": \"0.0.0.0\" },\n"
         "    \"match-client-id\": true,\n"
         "    \"next-server\": \"0.0.0.0\",\n"
+        "    \"server-hostname\": \"\",\n"
+        "    \"boot-file-name\": \"\",\n"
         "    \"renew-timer\": 1,\n"
         "    \"rebind-timer\": 2,\n"
         "    \"valid-lifetime\": 3,\n"

+ 4 - 0
src/lib/dhcpsrv/tests/shared_network_parser_unittest.cc

@@ -49,6 +49,8 @@ public:
                 "            \"valid-lifetime\": 300,"
                 "            \"match-client-id\": false,"
                 "            \"next-server\": \"\","
+                "            \"server-hostname\": \"\","
+                "            \"boot-file-name\": \"\","
                 "            \"client-class\": \"\","
                 "            \"reservation-mode\": \"all\","
                 "            \"4o6-interface\": \"\","
@@ -67,6 +69,8 @@ public:
                 "            \"valid-lifetime\": 30,"
                 "            \"match-client-id\": false,"
                 "            \"next-server\": \"\","
+                "            \"server-hostname\": \"\","
+                "            \"boot-file-name\": \"\","
                 "            \"client-class\": \"\","
                 "            \"reservation-mode\": \"all\","
                 "            \"4o6-interface\": \"\","

+ 4 - 0
src/lib/dhcpsrv/tests/shared_network_unittest.cc

@@ -222,6 +222,8 @@ TEST(SharedNetwork4Test, unparse) {
         "        \"id\": 1,\n"
         "        \"match-client-id\": true,\n"
         "        \"next-server\": \"0.0.0.0\",\n"
+        "        \"server-hostname\": \"\",\n"
+        "        \"boot-file-name\": \"\",\n"
         "        \"option-data\": [ ],\n"
         "        \"pools\": [ ],\n"
         "        \"rebind-timer\": 20,\n"
@@ -240,6 +242,8 @@ TEST(SharedNetwork4Test, unparse) {
         "        \"id\": 2,\n"
         "        \"match-client-id\": true,\n"
         "        \"next-server\": \"0.0.0.0\",\n"
+        "        \"server-hostname\": \"\",\n"
+        "        \"boot-file-name\": \"\",\n"
         "        \"option-data\": [ ],\n"
         "        \"pools\": [ ],\n"
         "        \"rebind-timer\": 20,\n"

+ 28 - 0
src/lib/dhcpsrv/tests/subnet_unittest.cc

@@ -110,6 +110,34 @@ TEST(Subnet4Test, siaddr) {
         BadValue);
 }
 
+// Checks whether server-hostname field can be set and retrieved correctly.
+TEST(Subnet4Test, serverHostname) {
+    Subnet4 subnet(IOAddress("192.0.2.1"), 24, 1000, 2000, 3000);
+
+    // Check if the default is empty
+    EXPECT_TRUE(subnet.getSname().empty());
+
+    // Check that we can set it up
+    EXPECT_NO_THROW(subnet.setSname("foobar"));
+
+    // Check that we can get it back
+    EXPECT_EQ("foobar", subnet.getSname());
+}
+
+// Checks whether boot-file-name field can be set and retrieved correctly.
+TEST(Subnet4Test, bootFileName) {
+    Subnet4 subnet(IOAddress("192.0.2.1"), 24, 1000, 2000, 3000);
+
+    // Check if the default is empty
+    EXPECT_TRUE(subnet.getFilename().empty());
+
+    // Check that we can set it up
+    EXPECT_NO_THROW(subnet.setFilename("foobar"));
+
+    // Check that we can get it back
+    EXPECT_EQ("foobar", subnet.getFilename());
+}
+
 // Checks if the match-client-id flag can be set and retrieved.
 TEST(Subnet4Test, matchClientId) {
     Subnet4 subnet(IOAddress("192.0.2.1"), 24, 1000, 2000, 3000);