Browse Source

[5288] Added option-data in DHCPv4 pools

Francis Dupont 7 years ago
parent
commit
556f6591be

+ 14 - 4
doc/examples/kea4/multiple-options.json

@@ -32,9 +32,11 @@
 // clients connected to this subnet. The first two options are
 // identified by the name. The third option is identified by the
 // option code.
+// There is an address pool defined within this subnet. Pool
+// specific value for option domain-name-servers is defined
+// for the pool.
   "subnet4": [
     {
-       "pools": [ { "pool":  "192.0.2.10 - 192.0.2.200" } ],
        "subnet": "192.0.2.0/24",
        "interface": "ethX",
        "option-data": [
@@ -124,9 +126,17 @@
             "name": "default-ip-ttl",
             "data": "0xf0"
          }
-       ]
-    } 
-  ]
+       ],
+       "pools": [ {
+         "pool":  "192.0.2.10 - 192.0.2.200",
+         "option-data": [
+            {
+                "name": "domain-name-servers",
+                "data": "192.0.2.3, 192.0.2.4"
+            }
+         ]
+       } ]
+    } ]
 },
 
 // The following configures logging. It assumes that messages with at

+ 51 - 0
doc/guide/dhcp4-srv.xml

@@ -1058,6 +1058,57 @@ temporarily override a list of interface names and listen on all interfaces.
       </para>
 
       <para>
+        In some cases it is useful to associate some options with an
+        address pool from which a client is assigned a lease. Pool
+        specific option values override subnet specific and global
+        option values. If the client is assigned multiple leases from
+        different pools, the server will assign options from all pools
+        from which the leases have been obtained. However, if the
+        particular option is specified in multiple pools from which
+        the client obtains the leases, only one instance of this
+        option will be handed out to the client. The server's
+        administrator must not try to prioritize assignment of pool
+        specific options by trying to order pools declarations in the
+        server configuration. Future Kea releases may change the order
+        in which options are assigned from the pools without any
+        notice.
+      </para>
+
+     <para>
+       The following configuration snippet demonstrates how to specify the
+       DNS servers option, which will be assigned to a client only if the
+       client obtains an address from the given pool:
+<screen>
+"Dhcp4": {
+    "subnet4": [
+        {
+            "pools": [
+                {
+                    "pool": "192.0.2.1 - 192.0.2.200",
+                    <userinput>"option-data": [
+                        {
+                            "name": "domain-name-servers",
+                            "code": 6,
+                            "space": "dhcp4",
+                            "csv-format": true,
+                            "data": "192.0.2.3"
+                         },
+                         ...
+                    ]</userinput>,
+                    ...
+                },
+                ...
+            ],
+            ...
+        },
+        ...
+    ],
+    ...
+}
+</screen>
+      </para>
+
+      <para>
         The currently supported standard DHCPv4 options are
         listed in <xref linkend="dhcp4-std-options-list"/>.
         The "Name" and "Code"

+ 151 - 0
src/bin/dhcp4/tests/config_parser_unittest.cc

@@ -2739,6 +2739,157 @@ TEST_F(Dhcp4ParserTest, optionDataInMultipleSubnets) {
     testOption(*range2.first, 23, foo2_expected, sizeof(foo2_expected));
 }
 
+// This test verifies that it is possible to specify options on
+// pool levels.
+TEST_F(Dhcp4ParserTest, optionDataSinglePool) {
+    ConstElementPtr x;
+    string config = "{ " + genIfaceConfig() + ","
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet4\": [ { "
+        "    \"pools\": [ { "
+        "        \"pool\": \"192.0.2.1 - 192.0.2.100\","
+        "        \"option-data\": [ {"
+        "            \"name\": \"dhcp-message\","
+        "            \"data\": \"ABCDEF0105\","
+        "            \"csv-format\": false"
+        "        },"
+        "        {"
+        "          \"name\": \"default-ip-ttl\","
+        "          \"data\": \"01\","
+        "          \"csv-format\": false"
+        "        } ]"
+        "    } ],"
+        "    \"subnet\": \"192.0.2.0/24\""
+        " } ],"
+        "\"valid-lifetime\": 4000 }";
+
+    ConstElementPtr json;
+    ASSERT_NO_THROW(json = parseDHCP4(config));
+    extractConfig(config);
+
+    EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json));
+    checkResult(x, 0);
+
+    Subnet4Ptr subnet = CfgMgr::instance().getStagingCfg()->getCfgSubnets4()->
+        selectSubnet(IOAddress("192.0.2.24"), classify_);
+    ASSERT_TRUE(subnet);
+
+    PoolPtr pool = subnet->getPool(Lease::TYPE_V4, IOAddress("192.0.2.24"), false);
+    ASSERT_TRUE(pool);
+    Pool4Ptr pool4 = boost::dynamic_pointer_cast<Pool4>(pool);
+    ASSERT_TRUE(pool4);
+
+    OptionContainerPtr options = pool4->getCfgOption()->getAll("dhcp4");
+    ASSERT_EQ(2, options->size());
+
+    // Get the search index. Index #1 is to search using option code.
+    const OptionContainerTypeIndex& idx = options->get<1>();
+
+    // Get the options for specified index. Expecting one option to be
+    // returned but in theory we may have multiple options with the same
+    // code so we get the range.
+    std::pair<OptionContainerTypeIndex::const_iterator,
+              OptionContainerTypeIndex::const_iterator> range =
+        idx.equal_range(56);
+    // Expect a single option with the code equal to 100.
+    ASSERT_EQ(1, std::distance(range.first, range.second));
+    const uint8_t foo_expected[] = {
+	0xAB, 0xCD, 0xEF, 0x01, 0x05
+    };
+    // Check if option is valid in terms of code and carried data.
+    testOption(*range.first, 56, foo_expected, sizeof(foo_expected));
+
+    range = idx.equal_range(23);
+    ASSERT_EQ(1, std::distance(range.first, range.second));
+    // Do another round of testing with second option.
+    const uint8_t foo2_expected[] = {
+        0x01
+    };
+    testOption(*range.first, 23, foo2_expected, sizeof(foo2_expected));
+}
+
+TEST_F(Dhcp4ParserTest, optionDataMultiplePools) {
+    ConstElementPtr x;
+    string config = "{ " + genIfaceConfig() + ","
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet4\": [ { "
+        "    \"pools\": [ { "
+        "        \"pool\": \"192.0.2.1 - 192.0.2.100\","
+        "        \"option-data\": [ {"
+        "            \"name\": \"dhcp-message\","
+        "            \"data\": \"ABCDEF0105\","
+        "            \"csv-format\": false"
+        "        } ]"
+        "    },"
+        "    {"
+        "        \"pool\": \"192.0.2.200 - 192.0.2.250\","
+        "        \"option-data\": [ {"
+        "          \"name\": \"default-ip-ttl\","
+        "          \"data\": \"01\","
+        "          \"csv-format\": false"
+        "        } ]"
+        "    } ],"
+        "    \"subnet\": \"192.0.2.0/24\""
+        " } ],"
+        "\"valid-lifetime\": 4000 }";
+
+    ConstElementPtr json;
+    ASSERT_NO_THROW(json = parseDHCP4(config));
+    extractConfig(config);
+
+    EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json));
+    checkResult(x, 0);
+
+    Subnet4Ptr subnet = CfgMgr::instance().getStagingCfg()->getCfgSubnets4()->
+        selectSubnet(IOAddress("192.0.2.24"), classify_);
+    ASSERT_TRUE(subnet);
+
+    PoolPtr pool1 = subnet->getPool(Lease::TYPE_V4, IOAddress("192.0.2.24"), false);
+    ASSERT_TRUE(pool1);
+    Pool4Ptr pool41 = boost::dynamic_pointer_cast<Pool4>(pool1);
+    ASSERT_TRUE(pool41);
+
+    OptionContainerPtr options1 = pool41->getCfgOption()->getAll("dhcp4");
+    ASSERT_EQ(1, options1->size());
+
+    // Get the search index. Index #1 is to search using option code.
+    const OptionContainerTypeIndex& idx1 = options1->get<1>();
+
+    // Get the options for specified index. Expecting one option to be
+    // returned but in theory we may have multiple options with the same
+    // code so we get the range.
+    std::pair<OptionContainerTypeIndex::const_iterator,
+              OptionContainerTypeIndex::const_iterator> range1 =
+        idx1.equal_range(56);
+    // Expect a single option with the code equal to 100.
+    ASSERT_EQ(1, std::distance(range1.first, range1.second));
+    const uint8_t foo_expected[] = {
+	0xAB, 0xCD, 0xEF, 0x01, 0x05
+    };
+    // Check if option is valid in terms of code and carried data.
+    testOption(*range1.first, 56, foo_expected, sizeof(foo_expected));
+
+    // Test another pool in the same way.
+    PoolPtr pool2 = subnet->getPool(Lease::TYPE_V4, IOAddress("192.0.2.240"), false);
+    ASSERT_TRUE(pool2);
+    Pool4Ptr pool42 = boost::dynamic_pointer_cast<Pool4>(pool2);
+    ASSERT_TRUE(pool42);
+
+    OptionContainerPtr options2 = pool42->getCfgOption()->getAll("dhcp4");
+    ASSERT_EQ(1, options2->size());
+
+    const OptionContainerTypeIndex& idx2 = options2->get<1>();
+    std::pair<OptionContainerTypeIndex::const_iterator,
+	      OptionContainerTypeIndex::const_iterator> range2 =
+	idx2.equal_range(23);
+    ASSERT_EQ(1, std::distance(range2.first, range2.second));
+    const uint8_t foo2_expected[] = {
+        0x01
+    };
+    testOption(*range2.first, 23, foo2_expected, sizeof(foo2_expected));
+}
 
 
 // Verify that empty option name is rejected in the configuration.

+ 250 - 48
src/bin/dhcp4/tests/get_config_unittest.cc

@@ -816,6 +816,73 @@ const char* EXTRACTED_CONFIGS[] = {
 "        \"interfaces-config\": {\n"
 "            \"interfaces\": [ \"*\" ]\n"
 "        },\n"
+"        \"rebind-timer\": 2000,\n"
+"        \"renew-timer\": 1000,\n"
+"        \"subnet4\": [\n"
+"            {\n"
+"                \"pools\": [\n"
+"                    {\n"
+"                        \"option-data\": [\n"
+"                            {\n"
+"                                \"csv-format\": false,\n"
+"                                \"data\": \"ABCDEF0105\",\n"
+"                                \"name\": \"dhcp-message\"\n"
+"                            },\n"
+"                            {\n"
+"                                \"csv-format\": false,\n"
+"                                \"data\": \"01\",\n"
+"                                \"name\": \"default-ip-ttl\"\n"
+"                            }\n"
+"                        ],\n"
+"                        \"pool\": \"192.0.2.1 - 192.0.2.100\"\n"
+"                    }\n"
+"                ],\n"
+"                \"subnet\": \"192.0.2.0/24\"\n"
+"            }\n"
+"        ],\n"
+"        \"valid-lifetime\": 4000\n"
+"    }\n",
+    // CONFIGURATION 31
+"{\n"
+"        \"interfaces-config\": {\n"
+"            \"interfaces\": [ \"*\" ]\n"
+"        },\n"
+"        \"rebind-timer\": 2000,\n"
+"        \"renew-timer\": 1000,\n"
+"        \"subnet4\": [\n"
+"            {\n"
+"                \"pools\": [\n"
+"                    {\n"
+"                        \"option-data\": [\n"
+"                            {\n"
+"                                \"csv-format\": false,\n"
+"                                \"data\": \"ABCDEF0105\",\n"
+"                                \"name\": \"dhcp-message\"\n"
+"                            }\n"
+"                        ],\n"
+"                        \"pool\": \"192.0.2.1 - 192.0.2.100\"\n"
+"                    },\n"
+"                    {\n"
+"                        \"option-data\": [\n"
+"                            {\n"
+"                                \"csv-format\": false,\n"
+"                                \"data\": \"01\",\n"
+"                                \"name\": \"default-ip-ttl\"\n"
+"                            }\n"
+"                        ],\n"
+"                        \"pool\": \"192.0.2.200 - 192.0.2.250\"\n"
+"                    }\n"
+"                ],\n"
+"                \"subnet\": \"192.0.2.0/24\"\n"
+"            }\n"
+"        ],\n"
+"        \"valid-lifetime\": 4000\n"
+"    }\n",
+    // CONFIGURATION 32
+"{\n"
+"        \"interfaces-config\": {\n"
+"            \"interfaces\": [ \"*\" ]\n"
+"        },\n"
 "        \"option-data\": [\n"
 "            {\n"
 "                \"data\": \"1234\",\n"
@@ -846,7 +913,7 @@ const char* EXTRACTED_CONFIGS[] = {
 "        \"renew-timer\": 1000,\n"
 "        \"valid-lifetime\": 4000\n"
 "    }\n",
-    // CONFIGURATION 31
+    // CONFIGURATION 33
 "{\n"
 "        \"interfaces-config\": {\n"
 "            \"interfaces\": [ \"*\" ]\n"
@@ -897,7 +964,7 @@ const char* EXTRACTED_CONFIGS[] = {
 "        ],\n"
 "        \"valid-lifetime\": 3000\n"
 "    }\n",
-    // CONFIGURATION 32
+    // CONFIGURATION 34
 "{\n"
 "        \"interfaces-config\": {\n"
 "            \"interfaces\": [ \"*\" ]\n"
@@ -932,7 +999,7 @@ const char* EXTRACTED_CONFIGS[] = {
 "        ],\n"
 "        \"valid-lifetime\": 4000\n"
 "    }\n",
-    // CONFIGURATION 33
+    // CONFIGURATION 35
 "{\n"
 "        \"interfaces-config\": {\n"
 "            \"interfaces\": [ \"*\" ]\n"
@@ -967,7 +1034,7 @@ const char* EXTRACTED_CONFIGS[] = {
 "        ],\n"
 "        \"valid-lifetime\": 4000\n"
 "    }\n",
-    // CONFIGURATION 34
+    // CONFIGURATION 36
 "{\n"
 "        \"interfaces-config\": {\n"
 "            \"interfaces\": [ \"eth0\", \"eth1\" ]\n"
@@ -976,7 +1043,7 @@ const char* EXTRACTED_CONFIGS[] = {
 "        \"renew-timer\": 1000,\n"
 "        \"valid-lifetime\": 4000\n"
 "    }\n",
-    // CONFIGURATION 35
+    // CONFIGURATION 37
 "{\n"
 "        \"interfaces-config\": {\n"
 "            \"interfaces\": [ \"eth0\", \"*\", \"eth1\" ]\n"
@@ -985,7 +1052,7 @@ const char* EXTRACTED_CONFIGS[] = {
 "        \"renew-timer\": 1000,\n"
 "        \"valid-lifetime\": 4000\n"
 "    }\n",
-    // CONFIGURATION 36
+    // CONFIGURATION 38
 "{\n"
 "        \"dhcp-ddns\": {\n"
 "            \"always-include-fqdn\": true,\n"
@@ -1020,7 +1087,7 @@ const char* EXTRACTED_CONFIGS[] = {
 "        ],\n"
 "        \"valid-lifetime\": 4000\n"
 "    }\n",
-    // CONFIGURATION 37
+    // CONFIGURATION 39
 "{\n"
 "        \"interfaces-config\": {\n"
 "            \"interfaces\": [ \"*\" ]\n"
@@ -1045,7 +1112,7 @@ const char* EXTRACTED_CONFIGS[] = {
 "        ],\n"
 "        \"valid-lifetime\": 4000\n"
 "    }\n",
-    // CONFIGURATION 38
+    // CONFIGURATION 40
 "{\n"
 "        \"interfaces-config\": {\n"
 "            \"interfaces\": [ \"*\" ]\n"
@@ -1143,7 +1210,7 @@ const char* EXTRACTED_CONFIGS[] = {
 "        ],\n"
 "        \"valid-lifetime\": 4000\n"
 "    }\n",
-    // CONFIGURATION 39
+    // CONFIGURATION 41
 "{\n"
 "        \"interfaces-config\": {\n"
 "            \"interfaces\": [ \"*\" ]\n"
@@ -1184,7 +1251,7 @@ const char* EXTRACTED_CONFIGS[] = {
 "        ],\n"
 "        \"valid-lifetime\": 4000\n"
 "    }\n",
-    // CONFIGURATION 40
+    // CONFIGURATION 42
 "{\n"
 "        \"rebind-timer\": 2000,\n"
 "        \"renew-timer\": 1000,\n"
@@ -1227,21 +1294,21 @@ const char* EXTRACTED_CONFIGS[] = {
 "        ],\n"
 "        \"valid-lifetime\": 4000\n"
 "    }\n",
-    // CONFIGURATION 41
+    // CONFIGURATION 43
 "{\n"
 "        \"interfaces-config\": {\n"
 "            \"interfaces\": [ \"*\" ]\n"
 "        },\n"
 "        \"subnet4\": [ ]\n"
 "    }\n",
-    // CONFIGURATION 42
+    // CONFIGURATION 44
 "{\n"
 "        \"interfaces-config\": {\n"
 "            \"interfaces\": [ \"*\" ]\n"
 "        },\n"
 "        \"subnet4\": [ ]\n"
 "    }\n",
-    // CONFIGURATION 43
+    // CONFIGURATION 45
 "{\n"
 "        \"decline-probation-period\": 12345,\n"
 "        \"interfaces-config\": {\n"
@@ -1249,7 +1316,7 @@ const char* EXTRACTED_CONFIGS[] = {
 "        },\n"
 "        \"subnet4\": [ ]\n"
 "    }\n",
-    // CONFIGURATION 44
+    // CONFIGURATION 46
 "{\n"
 "        \"expired-leases-processing\": {\n"
 "            \"flush-reclaimed-timer-wait-time\": 35,\n"
@@ -1264,7 +1331,7 @@ const char* EXTRACTED_CONFIGS[] = {
 "        },\n"
 "        \"subnet4\": [ ]\n"
 "    }\n",
-    // CONFIGURATION 45
+    // CONFIGURATION 47
 "{\n"
 "        \"interfaces-config\": {\n"
 "            \"interfaces\": [ \"*\" ]\n"
@@ -1283,7 +1350,7 @@ const char* EXTRACTED_CONFIGS[] = {
 "        ],\n"
 "        \"valid-lifetime\": 4000\n"
 "    }\n",
-    // CONFIGURATION 46
+    // CONFIGURATION 48
 "{\n"
 "        \"interfaces-config\": {\n"
 "            \"interfaces\": [ \"*\" ]\n"
@@ -1303,7 +1370,7 @@ const char* EXTRACTED_CONFIGS[] = {
 "        ],\n"
 "        \"valid-lifetime\": 4000\n"
 "    }\n",
-    // CONFIGURATION 47
+    // CONFIGURATION 49
 "{\n"
 "        \"interfaces-config\": {\n"
 "            \"interfaces\": [ \"*\" ]\n"
@@ -1323,7 +1390,7 @@ const char* EXTRACTED_CONFIGS[] = {
 "        ],\n"
 "        \"valid-lifetime\": 4000\n"
 "    }\n",
-    // CONFIGURATION 48
+    // CONFIGURATION 50
 "{\n"
 "        \"interfaces-config\": {\n"
 "            \"interfaces\": [ \"*\" ]\n"
@@ -1344,7 +1411,7 @@ const char* EXTRACTED_CONFIGS[] = {
 "        ],\n"
 "        \"valid-lifetime\": 4000\n"
 "    }\n",
-    // CONFIGURATION 49
+    // CONFIGURATION 51
 "{\n"
 "        \"interfaces-config\": {\n"
 "            \"interfaces\": [ \"*\" ]\n"
@@ -1364,7 +1431,7 @@ const char* EXTRACTED_CONFIGS[] = {
 "        ],\n"
 "        \"valid-lifetime\": 4000\n"
 "    }\n",
-    // CONFIGURATION 50
+    // CONFIGURATION 52
 "{\n"
 "        \"client-classes\": [\n"
 "            {\n"
@@ -1394,7 +1461,7 @@ const char* EXTRACTED_CONFIGS[] = {
 "        ],\n"
 "        \"valid-lifetime\": 4000\n"
 "    }\n",
-    // CONFIGURATION 51
+    // CONFIGURATION 53
 "{\n"
 "        \"interfaces-config\": {\n"
 "            \"interfaces\": [ \"*\" ]\n"
@@ -1413,7 +1480,7 @@ const char* EXTRACTED_CONFIGS[] = {
 "        ],\n"
 "        \"valid-lifetime\": 4000\n"
 "    }\n",
-    // CONFIGURATION 52
+    // CONFIGURATION 54
 "{\n"
 "        \"interfaces-config\": {\n"
 "            \"interfaces\": [ \"*\" ]\n"
@@ -1433,7 +1500,7 @@ const char* EXTRACTED_CONFIGS[] = {
 "        ],\n"
 "        \"valid-lifetime\": 4000\n"
 "    }\n",
-    // CONFIGURATION 53
+    // CONFIGURATION 55
 "{\n"
 "        \"interfaces-config\": {\n"
 "            \"interfaces\": [ \"*\" ]\n"
@@ -1457,7 +1524,7 @@ const char* EXTRACTED_CONFIGS[] = {
 "        ],\n"
 "        \"valid-lifetime\": 4000\n"
 "    }\n",
-    // CONFIGURATION 54
+    // CONFIGURATION 56
 "{\n"
 "        \"interfaces-config\": {\n"
 "            \"interfaces\": [ \"*\" ]\n"
@@ -3780,6 +3847,141 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"lease-database\": {\n"
 "            \"type\": \"memfile\"\n"
 "        },\n"
+"        \"option-data\": [ ],\n"
+"        \"option-def\": [ ],\n"
+"        \"subnet4\": [\n"
+"            {\n"
+"                \"4o6-interface\": \"\",\n"
+"                \"4o6-interface-id\": \"\",\n"
+"                \"4o6-subnet\": \"\",\n"
+"                \"id\": 1,\n"
+"                \"interface\": \"\",\n"
+"                \"match-client-id\": true,\n"
+"                \"next-server\": \"0.0.0.0\",\n"
+"                \"option-data\": [ ],\n"
+"                \"pools\": [\n"
+"                    {\n"
+"                        \"pool\": \"192.0.2.1-192.0.2.100\"\n"
+"                    }\n"
+"                ],\n"
+"                \"rebind-timer\": 2000,\n"
+"                \"relay\": {\n"
+"                    \"ip-address\": \"0.0.0.0\"\n"
+"                },\n"
+"                \"renew-timer\": 1000,\n"
+"                \"reservation-mode\": \"all\",\n"
+"                \"reservations\": [ ],\n"
+"                \"subnet\": \"192.0.2.0/24\",\n"
+"                \"valid-lifetime\": 4000\n"
+"            }\n"
+"        ]\n"
+"    }\n",
+    // CONFIGURATION 31
+"{\n"
+"        \"decline-probation-period\": 86400,\n"
+"        \"dhcp-ddns\": {\n"
+"            \"always-include-fqdn\": false,\n"
+"            \"enable-updates\": false,\n"
+"            \"generated-prefix\": \"myhost\",\n"
+"            \"max-queue-size\": 1024,\n"
+"            \"ncr-format\": \"JSON\",\n"
+"            \"ncr-protocol\": \"UDP\",\n"
+"            \"override-client-update\": false,\n"
+"            \"override-no-update\": false,\n"
+"            \"qualifying-suffix\": \"\",\n"
+"            \"replace-client-name\": \"never\",\n"
+"            \"sender-ip\": \"0.0.0.0\",\n"
+"            \"sender-port\": 0,\n"
+"            \"server-ip\": \"127.0.0.1\",\n"
+"            \"server-port\": 53001\n"
+"        },\n"
+"        \"dhcp4o6-port\": 0,\n"
+"        \"echo-client-id\": true,\n"
+"        \"expired-leases-processing\": {\n"
+"            \"flush-reclaimed-timer-wait-time\": 25,\n"
+"            \"hold-reclaimed-time\": 3600,\n"
+"            \"max-reclaim-leases\": 100,\n"
+"            \"max-reclaim-time\": 250,\n"
+"            \"reclaim-timer-wait-time\": 10,\n"
+"            \"unwarned-reclaim-cycles\": 5\n"
+"        },\n"
+"        \"hooks-libraries\": [ ],\n"
+"        \"host-reservation-identifiers\": [ \"hw-address\", \"duid\", \"circuit-id\", \"client-id\" ],\n"
+"        \"interfaces-config\": {\n"
+"            \"interfaces\": [ \"*\" ]\n"
+"        },\n"
+"        \"lease-database\": {\n"
+"            \"type\": \"memfile\"\n"
+"        },\n"
+"        \"option-data\": [ ],\n"
+"        \"option-def\": [ ],\n"
+"        \"subnet4\": [\n"
+"            {\n"
+"                \"4o6-interface\": \"\",\n"
+"                \"4o6-interface-id\": \"\",\n"
+"                \"4o6-subnet\": \"\",\n"
+"                \"id\": 1,\n"
+"                \"interface\": \"\",\n"
+"                \"match-client-id\": true,\n"
+"                \"next-server\": \"0.0.0.0\",\n"
+"                \"option-data\": [ ],\n"
+"                \"pools\": [\n"
+"                    {\n"
+"                        \"pool\": \"192.0.2.1-192.0.2.100\"\n"
+"                    },\n"
+"                    {\n"
+"                        \"pool\": \"192.0.2.200-192.0.2.250\"\n"
+"                    }\n"
+"                ],\n"
+"                \"rebind-timer\": 2000,\n"
+"                \"relay\": {\n"
+"                    \"ip-address\": \"0.0.0.0\"\n"
+"                },\n"
+"                \"renew-timer\": 1000,\n"
+"                \"reservation-mode\": \"all\",\n"
+"                \"reservations\": [ ],\n"
+"                \"subnet\": \"192.0.2.0/24\",\n"
+"                \"valid-lifetime\": 4000\n"
+"            }\n"
+"        ]\n"
+"    }\n",
+    // CONFIGURATION 32
+"{\n"
+"        \"decline-probation-period\": 86400,\n"
+"        \"dhcp-ddns\": {\n"
+"            \"always-include-fqdn\": false,\n"
+"            \"enable-updates\": false,\n"
+"            \"generated-prefix\": \"myhost\",\n"
+"            \"max-queue-size\": 1024,\n"
+"            \"ncr-format\": \"JSON\",\n"
+"            \"ncr-protocol\": \"UDP\",\n"
+"            \"override-client-update\": false,\n"
+"            \"override-no-update\": false,\n"
+"            \"qualifying-suffix\": \"\",\n"
+"            \"replace-client-name\": \"never\",\n"
+"            \"sender-ip\": \"0.0.0.0\",\n"
+"            \"sender-port\": 0,\n"
+"            \"server-ip\": \"127.0.0.1\",\n"
+"            \"server-port\": 53001\n"
+"        },\n"
+"        \"dhcp4o6-port\": 0,\n"
+"        \"echo-client-id\": true,\n"
+"        \"expired-leases-processing\": {\n"
+"            \"flush-reclaimed-timer-wait-time\": 25,\n"
+"            \"hold-reclaimed-time\": 3600,\n"
+"            \"max-reclaim-leases\": 100,\n"
+"            \"max-reclaim-time\": 250,\n"
+"            \"reclaim-timer-wait-time\": 10,\n"
+"            \"unwarned-reclaim-cycles\": 5\n"
+"        },\n"
+"        \"hooks-libraries\": [ ],\n"
+"        \"host-reservation-identifiers\": [ \"hw-address\", \"duid\", \"circuit-id\", \"client-id\" ],\n"
+"        \"interfaces-config\": {\n"
+"            \"interfaces\": [ \"*\" ]\n"
+"        },\n"
+"        \"lease-database\": {\n"
+"            \"type\": \"memfile\"\n"
+"        },\n"
 "        \"option-data\": [\n"
 "            {\n"
 "                \"code\": 1,\n"
@@ -3818,7 +4020,7 @@ const char* UNPARSED_CONFIGS[] = {
 "        ],\n"
 "        \"subnet4\": [ ]\n"
 "    }\n",
-    // CONFIGURATION 31
+    // CONFIGURATION 33
 "{\n"
 "        \"decline-probation-period\": 86400,\n"
 "        \"dhcp-ddns\": {\n"
@@ -3925,7 +4127,7 @@ const char* UNPARSED_CONFIGS[] = {
 "            }\n"
 "        ]\n"
 "    }\n",
-    // CONFIGURATION 32
+    // CONFIGURATION 34
 "{\n"
 "        \"decline-probation-period\": 86400,\n"
 "        \"dhcp-ddns\": {\n"
@@ -4004,7 +4206,7 @@ const char* UNPARSED_CONFIGS[] = {
 "            }\n"
 "        ]\n"
 "    }\n",
-    // CONFIGURATION 33
+    // CONFIGURATION 35
 "{\n"
 "        \"decline-probation-period\": 86400,\n"
 "        \"dhcp-ddns\": {\n"
@@ -4088,7 +4290,7 @@ const char* UNPARSED_CONFIGS[] = {
 "            }\n"
 "        ]\n"
 "    }\n",
-    // CONFIGURATION 34
+    // CONFIGURATION 36
 "{\n"
 "        \"decline-probation-period\": 86400,\n"
 "        \"dhcp-ddns\": {\n"
@@ -4129,7 +4331,7 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"option-def\": [ ],\n"
 "        \"subnet4\": [ ]\n"
 "    }\n",
-    // CONFIGURATION 35
+    // CONFIGURATION 37
 "{\n"
 "        \"decline-probation-period\": 86400,\n"
 "        \"dhcp-ddns\": {\n"
@@ -4170,7 +4372,7 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"option-def\": [ ],\n"
 "        \"subnet4\": [ ]\n"
 "    }\n",
-    // CONFIGURATION 36
+    // CONFIGURATION 38
 "{\n"
 "        \"decline-probation-period\": 86400,\n"
 "        \"dhcp-ddns\": {\n"
@@ -4236,7 +4438,7 @@ const char* UNPARSED_CONFIGS[] = {
 "            }\n"
 "        ]\n"
 "    }\n",
-    // CONFIGURATION 37
+    // CONFIGURATION 39
 "{\n"
 "        \"decline-probation-period\": 86400,\n"
 "        \"dhcp-ddns\": {\n"
@@ -4302,7 +4504,7 @@ const char* UNPARSED_CONFIGS[] = {
 "            }\n"
 "        ]\n"
 "    }\n",
-    // CONFIGURATION 38
+    // CONFIGURATION 40
 "{\n"
 "        \"decline-probation-period\": 86400,\n"
 "        \"dhcp-ddns\": {\n"
@@ -4513,7 +4715,7 @@ const char* UNPARSED_CONFIGS[] = {
 "            }\n"
 "        ]\n"
 "    }\n",
-    // CONFIGURATION 39
+    // CONFIGURATION 41
 "{\n"
 "        \"decline-probation-period\": 86400,\n"
 "        \"dhcp-ddns\": {\n"
@@ -4608,7 +4810,7 @@ const char* UNPARSED_CONFIGS[] = {
 "            }\n"
 "        ]\n"
 "    }\n",
-    // CONFIGURATION 40
+    // CONFIGURATION 42
 "{\n"
 "        \"decline-probation-period\": 86400,\n"
 "        \"dhcp-ddns\": {\n"
@@ -4746,7 +4948,7 @@ const char* UNPARSED_CONFIGS[] = {
 "            }\n"
 "        ]\n"
 "    }\n",
-    // CONFIGURATION 41
+    // CONFIGURATION 43
 "{\n"
 "        \"decline-probation-period\": 86400,\n"
 "        \"dhcp-ddns\": {\n"
@@ -4787,7 +4989,7 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"option-def\": [ ],\n"
 "        \"subnet4\": [ ]\n"
 "    }\n",
-    // CONFIGURATION 42
+    // CONFIGURATION 44
 "{\n"
 "        \"decline-probation-period\": 86400,\n"
 "        \"dhcp-ddns\": {\n"
@@ -4828,7 +5030,7 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"option-def\": [ ],\n"
 "        \"subnet4\": [ ]\n"
 "    }\n",
-    // CONFIGURATION 43
+    // CONFIGURATION 45
 "{\n"
 "        \"decline-probation-period\": 12345,\n"
 "        \"dhcp-ddns\": {\n"
@@ -4869,7 +5071,7 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"option-def\": [ ],\n"
 "        \"subnet4\": [ ]\n"
 "    }\n",
-    // CONFIGURATION 44
+    // CONFIGURATION 46
 "{\n"
 "        \"decline-probation-period\": 86400,\n"
 "        \"dhcp-ddns\": {\n"
@@ -4910,7 +5112,7 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"option-def\": [ ],\n"
 "        \"subnet4\": [ ]\n"
 "    }\n",
-    // CONFIGURATION 45
+    // CONFIGURATION 47
 "{\n"
 "        \"decline-probation-period\": 86400,\n"
 "        \"dhcp-ddns\": {\n"
@@ -4976,7 +5178,7 @@ const char* UNPARSED_CONFIGS[] = {
 "            }\n"
 "        ]\n"
 "    }\n",
-    // CONFIGURATION 46
+    // CONFIGURATION 48
 "{\n"
 "        \"decline-probation-period\": 86400,\n"
 "        \"dhcp-ddns\": {\n"
@@ -5042,7 +5244,7 @@ const char* UNPARSED_CONFIGS[] = {
 "            }\n"
 "        ]\n"
 "    }\n",
-    // CONFIGURATION 47
+    // CONFIGURATION 49
 "{\n"
 "        \"decline-probation-period\": 86400,\n"
 "        \"dhcp-ddns\": {\n"
@@ -5108,7 +5310,7 @@ const char* UNPARSED_CONFIGS[] = {
 "            }\n"
 "        ]\n"
 "    }\n",
-    // CONFIGURATION 48
+    // CONFIGURATION 50
 "{\n"
 "        \"decline-probation-period\": 86400,\n"
 "        \"dhcp-ddns\": {\n"
@@ -5174,7 +5376,7 @@ const char* UNPARSED_CONFIGS[] = {
 "            }\n"
 "        ]\n"
 "    }\n",
-    // CONFIGURATION 49
+    // CONFIGURATION 51
 "{\n"
 "        \"decline-probation-period\": 86400,\n"
 "        \"dhcp-ddns\": {\n"
@@ -5240,7 +5442,7 @@ const char* UNPARSED_CONFIGS[] = {
 "            }\n"
 "        ]\n"
 "    }\n",
-    // CONFIGURATION 50
+    // CONFIGURATION 52
 "{\n"
 "        \"client-classes\": [\n"
 "            {\n"
@@ -5329,7 +5531,7 @@ const char* UNPARSED_CONFIGS[] = {
 "            }\n"
 "        ]\n"
 "    }\n",
-    // CONFIGURATION 51
+    // CONFIGURATION 53
 "{\n"
 "        \"decline-probation-period\": 86400,\n"
 "        \"dhcp-ddns\": {\n"
@@ -5395,7 +5597,7 @@ const char* UNPARSED_CONFIGS[] = {
 "            }\n"
 "        ]\n"
 "    }\n",
-    // CONFIGURATION 52
+    // CONFIGURATION 54
 "{\n"
 "        \"decline-probation-period\": 86400,\n"
 "        \"dhcp-ddns\": {\n"
@@ -5462,7 +5664,7 @@ const char* UNPARSED_CONFIGS[] = {
 "            }\n"
 "        ]\n"
 "    }\n",
-    // CONFIGURATION 53
+    // CONFIGURATION 55
 "{\n"
 "        \"decline-probation-period\": 86400,\n"
 "        \"dhcp-ddns\": {\n"
@@ -5533,7 +5735,7 @@ const char* UNPARSED_CONFIGS[] = {
 "            }\n"
 "        ]\n"
 "    }\n",
-    // CONFIGURATION 54
+    // CONFIGURATION 56
 "{\n"
 "        \"decline-probation-period\": 86400,\n"
 "        \"dhcp-ddns\": {\n"

+ 1 - 1
src/bin/dhcp4/tests/get_config_unittest.cc.skel

@@ -340,6 +340,6 @@ TEST_P(Dhcp4GetConfigTest, run) {
 
 /// Define the parametrized test loop
 INSTANTIATE_TEST_CASE_P(Dhcp4GetConfigTest, Dhcp4GetConfigTest,
-                        ::testing::Range(0UL, max_config_counter));
+                        ::testing::Range(static_cast<size_t>(0), max_config_counter));
 
 };

+ 3 - 1
src/lib/dhcpsrv/cfg_subnets4.cc

@@ -335,7 +335,9 @@ CfgSubnets4::toElement() const {
             if (!isNull(context)) {
                 pool_map->set("user-context", context);
             }
-            // Set pool options (not yet supported)
+            // Set pool options
+            ConstCfgOptionPtr opts = (*pool)->getCfgOption();
+            pool_map->set("option-data", opts->toElement());
             // Push on the pool list
             pool_list->add(pool_map);
         }

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

@@ -749,12 +749,6 @@ PoolParser::parse(PoolStoragePtr pools,
     ConstElementPtr option_data = pool_structure->get("option-data");
     if (option_data) {
         try {
-            // Currently we don't support specifying options for the DHCPv4 server.
-            if (address_family == AF_INET) {
-                isc_throw(DhcpConfigError, "option-data is not supported for DHCPv4"
-                          " address pools");
-            }
-
             CfgOptionPtr cfg = pool->getCfgOption();
             OptionDataListParser option_parser(address_family);
             option_parser.parse(cfg, option_data);

+ 53 - 1
src/lib/dhcpsrv/tests/pool_unittest.cc

@@ -120,6 +120,58 @@ TEST(Pool4Test, toText) {
     EXPECT_EQ("type=V4, 192.0.2.128-192.0.2.143", pool2.toText());
 }
 
+// This test checks that it is possible to specify pool specific options.
+TEST(Pool4Test, addOptions) {
+    // Create a pool to add options to it.
+    Pool4Ptr pool(new Pool4(IOAddress("192.0.2.0"),
+                            IOAddress("192.0.2.255")));
+
+    // Differentiate options by their codes (100-109)
+    for (uint16_t code = 100; code < 110; ++code) {
+        OptionPtr option(new Option(Option::V4, code, OptionBuffer(10, 0xFF)));
+        ASSERT_NO_THROW(pool->getCfgOption()->add(option, false, "dhcp4"));
+    }
+
+    // Add 7 options to another option space. The option codes partially overlap
+    // with option codes that we have added to dhcp4 option space.
+    for (uint16_t code = 105; code < 112; ++code) {
+        OptionPtr option(new Option(Option::V6, code, OptionBuffer(10, 0xFF)));
+        ASSERT_NO_THROW(pool->getCfgOption()->add(option, false, "isc"));
+    }
+
+    // Get options from the pool and check if all 10 are there.
+    OptionContainerPtr options = pool->getCfgOption()->getAll("dhcp4");
+    ASSERT_TRUE(options);
+    ASSERT_EQ(10, options->size());
+
+    // Validate codes of options added to dhcp4 option space.
+    uint16_t expected_code = 100;
+    for (OptionContainer::const_iterator option_desc = options->begin();
+         option_desc != options->end(); ++option_desc) {
+        ASSERT_TRUE(option_desc->option_);
+        EXPECT_EQ(expected_code, option_desc->option_->getType());
+        ++expected_code;
+    }
+
+    options = pool->getCfgOption()->getAll("isc");
+    ASSERT_TRUE(options);
+    ASSERT_EQ(7, options->size());
+
+    // Validate codes of options added to isc option space.
+    expected_code = 105;
+    for (OptionContainer::const_iterator option_desc = options->begin();
+         option_desc != options->end(); ++option_desc) {
+        ASSERT_TRUE(option_desc->option_);
+        EXPECT_EQ(expected_code, option_desc->option_->getType());
+        ++expected_code;
+    }
+
+    // Try to get options from a non-existing option space.
+    options = pool->getCfgOption()->getAll("abcd");
+    ASSERT_TRUE(options);
+    EXPECT_TRUE(options->empty());
+}
+
 TEST(Pool6Test, constructor_first_last) {
 
     // let's construct 2001:db8:1:: - 2001:db8:1::ffff:ffff:ffff:ffff pool
@@ -329,7 +381,7 @@ TEST(Pool6Test, unique_id) {
 }
 
 // Simple check if toText returns reasonable values
-TEST(Pool6Test,toText) {
+TEST(Pool6Test, toText) {
     Pool6 pool1(Lease::TYPE_NA, IOAddress("2001:db8::1"),
                 IOAddress("2001:db8::2"));
     EXPECT_EQ("type=IA_NA, 2001:db8::1-2001:db8::2, delegated_len=128",