Browse Source

[3705] Address review comments.

Marcin Siodelski 10 years ago
parent
commit
b55dd296af

+ 7 - 4
doc/guide/dhcp6-srv.xml

@@ -885,6 +885,9 @@ temporarily override a list of interface names and listen on all interfaces.
 <row><entry>clt-time</entry><entry>46</entry><entry>uint32</entry><entry>false</entry></row>
 <row><entry>lq-relay-data</entry><entry>47</entry><entry>record</entry><entry>false</entry></row>
 <row><entry>lq-client-link</entry><entry>48</entry><entry>ipv6-address</entry><entry>true</entry></row>
+<row><entry>erp-local-domain-name</entry><entry>65</entry><entry>fqdn</entry><entry>false</entry></row>
+<row><entry>rsoo</entry><entry>66</entry><entry>empty</entry><entry>false</entry></row>
+<row><entry>client-linklayer-addr</entry><entry>79</entry><entry>binary</entry><entry>false</entry></row>
         </tbody>
         </tgroup>
       </table>
@@ -1371,7 +1374,7 @@ should include options from the isc option space:
     <section id="dhcp6-rsoo">
       <title>Relay-Supplied Options</title>
       <para><ulink url="http://tools.ietf.org/html/rfc6422">RFC 6422</ulink>
-      defines a mechanism called Relay supplied options. In certain cases relay
+      defines a mechanism called Relay-Supplied DHCP Options. In certain cases relay
       agents are the only entities that may have specific information. They can
       insert options when relaying messages from the client to the server. The
       server will then do certain checks and copy those options to the response
@@ -1381,11 +1384,11 @@ should include options from the isc option space:
       included. First, the server must not provide the option by itself. In
       other words, if both relay and server provide an option, the server always
       takes precedence. Second, the option must be RSOO-enabled. IANA mantains a
-      list of RSOO-enabled options here: <ulink url="http://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xhtml#options-relay-supplied">List of RSOO-enabled options</ulink>.
-      However, there may cases when system administrators want to echo other
+      list of RSOO-enabled options <ulink url="http://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xhtml#options-relay-supplied">here</ulink>.
+      However, there may be cases when system administrators want to echo other
       options. Kea can be instructed to treat other options as RSOO-enabled.
       For example, to mark options 110, 120 and 130 as RSOO-enabled, the following
-      syntax may be used:
+      syntax should be used:
 <screen>
 "Dhcp6": {
     <userinput>"relay-supplied-options": [ "110", "120", "130" ],</userinput>

+ 16 - 27
src/bin/dhcp6/dhcp6_srv.cc

@@ -2739,39 +2739,28 @@ void Dhcpv6Srv::processRSOO(const Pkt6Ptr& query, const Pkt6Ptr& rsp) {
         return;
     }
 
-    // Get the global options info. We'll use it to check whether an
-    // option is RSOO-enabled or not.
-    ConstCfgOptionPtr global_opts = CfgMgr::instance().getCurrentCfg()->
-        getCfgOption();
+    // Get RSOO configuration.
+    ConstCfgRSOOPtr cfg_rsoo  = CfgMgr::instance().getCurrentCfg()->getCfgRSOO();
 
     // Let's get over all relays (encapsulation levels). We need to do
     // it in the same order as the client packet traversed the relays.
     for (int i = query->relay_info_.size(); i > 0 ; --i) {
         OptionPtr rsoo_container = query->getRelayOption(D6O_RSOO, i - 1);
-        if (!rsoo_container) {
-            // No relay-supplied options by this relay? Ok, carry on.
-            continue;
-        }
-
-        // There are RSOO options. Let's get through them one by one
-        // and if it's RSOO-enabled and there's no such option provided yet,
-        // copy it to the server's response
-        const OptionCollection& rsoo = rsoo_container->getOptions();
-        for (OptionCollection::const_iterator opt = rsoo.begin(); opt != rsoo.end();
-             ++opt) {
-            if (!global_opts->isRSOOEnabled(opt->second->getType())) {
-                // We didn't copy this option, because it's not RSOO-enabled.
-                continue;
-            }
-
-            if (rsp->getOption(opt->second->getType())) {
-                // There is such an option in the server's response already,
-                // we'll skip relay's option
-                continue;
+        if (rsoo_container) {
+            // There are RSOO options. Let's get through them one by one
+            // and if it's RSOO-enabled and there's no such option provided yet,
+            // copy it to the server's response
+            const OptionCollection& rsoo = rsoo_container->getOptions();
+            for (OptionCollection::const_iterator opt = rsoo.begin();
+                 opt != rsoo.end(); ++opt) {
+
+                // Echo option if it is RSOO enabled option and there is no such
+                // option added yet.
+                if (cfg_rsoo->enabled(opt->second->getType()) &&
+                    !rsp->getOption(opt->second->getType())) {
+                    rsp->addOption(opt->second);
+                }
             }
-
-            // All checks went ok, let's add this option.
-            rsp->addOption(opt->second);
         }
     }
 }

+ 46 - 28
src/bin/dhcp6/json_config_parser.cc

@@ -41,6 +41,7 @@
 #include <boost/shared_ptr.hpp>
 
 #include <iostream>
+#include <limits>
 #include <map>
 #include <vector>
 
@@ -575,10 +576,14 @@ public:
     ParserCollection subnets_;
 };
 
-/// @brief parser for list of RSOO options
+/// @brief Parser for list of RSOO options
 ///
-/// This parser handles Dhcp6/relay-supplied-options entry.
-/// It contains a list of option codes.
+/// This parser handles Dhcp6/relay-supplied-options entry. It contains a
+/// list of RSOO-enabled options which should be sent back to the client.
+///
+/// The option on this list can be specified using an option code or option
+/// name. Therefore, the values on the list should always be enclosed in
+/// "quotes".
 class RSOOListConfigParser : public DhcpConfigParser {
 public:
 
@@ -603,34 +608,47 @@ public:
     ///
     /// @param value pointer to the content of parsed values
     virtual void build(isc::data::ConstElementPtr value) {
+        try {
+            BOOST_FOREACH(ConstElementPtr source_elem, value->listValue()) {
+                std::string option_str = source_elem->stringValue();
+                // This option can be either code (integer) or name. Let's try code first
+                int64_t code = 0;
+                try {
+                    code = boost::lexical_cast<int64_t>(option_str);
+                    // Protect against the negative value and too high value.
+                    if (code < 0) {
+                        isc_throw(BadValue, "invalid option code value specified '"
+                                  << option_str << "', the option code must be a"
+                                  " non-negative value");
+
+                    } else if (code > std::numeric_limits<uint16_t>::max()) {
+                        isc_throw(BadValue, "invalid option code value specified '"
+                                  << option_str << "', the option code must not be"
+                                  " greater than '" << std::numeric_limits<uint16_t>::max()
+                                  << "'");
+                    }
+
+                } catch (const boost::bad_lexical_cast &) {
+                    // Oh well, it's not a number
+                }
 
-        // By default, there's only one RSOO option defined: 65
-        // http://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xhtml
-        CfgMgr::instance().getStagingCfg()->getCfgOption()->clearRSOO();
-        CfgMgr::instance().getStagingCfg()->getCfgOption()->addRSOO(D6O_ERP_LOCAL_DOMAIN_NAME);
-
-        BOOST_FOREACH(ConstElementPtr source_elem, value->listValue()) {
-
-            std::string option_str = source_elem->stringValue();
-            // This option can be either code (integer) or name. Let's try code first
-            uint16_t code = 0;
-            try {
-                code = boost::lexical_cast<uint16_t>(option_str);
-            } catch (const boost::bad_lexical_cast &) {
-                // Oh well, it's not a number
-            }
-
-            if (!code) {
-                OptionDefinitionPtr def = LibDHCP::getOptionDef(Option::V6, option_str);
-                if (def) {
-                    code = def->getCode();
-                } else {
-                    isc_throw(BadValue, "Unable to convert '" << option_str
-                              << "' to option code while parsing allowed"
-                              << "relay-supplied-options");
+                if (!code) {
+                    OptionDefinitionPtr def = LibDHCP::getOptionDef(Option::V6, option_str);
+                    if (def) {
+                        code = def->getCode();
+                    } else {
+                        isc_throw(BadValue, "unable to find option code for the "
+                                  " specified option name '" << option_str << "'"
+                                  " while parsing the list of enabled"
+                                  " relay-supplied-options");
+                    }
                 }
+                CfgMgr::instance().getStagingCfg()->getCfgRSOO()->enable(code);
             }
-            CfgMgr::instance().getStagingCfg()->getCfgOption()->addRSOO(code);
+        } catch (const std::exception& ex) {
+            // Rethrow exception with the appended position of the parsed
+            // element.
+            isc_throw(DhcpConfigError, ex.what() << " (" << value->getPosition() << ")");
         }
     }
 

+ 50 - 21
src/bin/dhcp6/tests/config_parser_unittest.cc

@@ -3798,18 +3798,18 @@ TEST_F(Dhcp6ParserTest, rsooNumbers) {
     checkResult(status, 0);
 
     // The following codes should be enabled now
-    EXPECT_TRUE(CfgMgr::instance().getStagingCfg()->getCfgOption()->isRSOOEnabled(10));
-    EXPECT_TRUE(CfgMgr::instance().getStagingCfg()->getCfgOption()->isRSOOEnabled(20));
-    EXPECT_TRUE(CfgMgr::instance().getStagingCfg()->getCfgOption()->isRSOOEnabled(30));
+    EXPECT_TRUE(CfgMgr::instance().getStagingCfg()->getCfgRSOO()->enabled(10));
+    EXPECT_TRUE(CfgMgr::instance().getStagingCfg()->getCfgRSOO()->enabled(20));
+    EXPECT_TRUE(CfgMgr::instance().getStagingCfg()->getCfgRSOO()->enabled(30));
 
     // This option is on the IANA list, so it should be allowed all the time
     // (http://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xhtml)
-    EXPECT_TRUE(CfgMgr::instance().getStagingCfg()->getCfgOption()
-                ->isRSOOEnabled(D6O_ERP_LOCAL_DOMAIN_NAME));
+    EXPECT_TRUE(CfgMgr::instance().getStagingCfg()->getCfgRSOO()
+                ->enabled(D6O_ERP_LOCAL_DOMAIN_NAME));
 
     // Those options are not enabled
-    EXPECT_FALSE(CfgMgr::instance().getStagingCfg()->getCfgOption()->isRSOOEnabled(25));
-    EXPECT_FALSE(CfgMgr::instance().getStagingCfg()->getCfgOption()->isRSOOEnabled(1));
+    EXPECT_FALSE(CfgMgr::instance().getStagingCfg()->getCfgRSOO()->enabled(25));
+    EXPECT_FALSE(CfgMgr::instance().getStagingCfg()->getCfgRSOO()->enabled(1));
 }
 
 /// The goal of this test is to verify that configuration can include
@@ -3831,39 +3831,68 @@ TEST_F(Dhcp6ParserTest, rsooNames) {
     checkResult(status, 0);
 
     for (uint16_t code = 0; code < D6O_NAME_SERVERS; ++code) {
-        EXPECT_FALSE(CfgMgr::instance().getStagingCfg()->getCfgOption()
-                     ->isRSOOEnabled(code)) << " for option code " << code;
+        EXPECT_FALSE(CfgMgr::instance().getStagingCfg()->getCfgRSOO()
+                     ->enabled(code)) << " for option code " << code;
     }
 
     // The following codes should be enabled now
-    EXPECT_TRUE(CfgMgr::instance().getStagingCfg()->getCfgOption()
-                ->isRSOOEnabled(D6O_NAME_SERVERS));
+    EXPECT_TRUE(CfgMgr::instance().getStagingCfg()->getCfgRSOO()
+                ->enabled(D6O_NAME_SERVERS));
 
     for (uint16_t code = D6O_NAME_SERVERS + 1; code < D6O_REMOTE_ID; ++code) {
-        EXPECT_FALSE(CfgMgr::instance().getStagingCfg()->getCfgOption()
-                     ->isRSOOEnabled(code)) << " for option code " << code;
+        EXPECT_FALSE(CfgMgr::instance().getStagingCfg()->getCfgRSOO()
+                     ->enabled(code)) << " for option code " << code;
     }
 
     // Check remote-id. It should be enabled.
-    EXPECT_TRUE(CfgMgr::instance().getStagingCfg()->getCfgOption()
-                ->isRSOOEnabled(D6O_REMOTE_ID));
+    EXPECT_TRUE(CfgMgr::instance().getStagingCfg()->getCfgRSOO()
+                ->enabled(D6O_REMOTE_ID));
     for (uint16_t code = D6O_REMOTE_ID + 1; code < D6O_ERP_LOCAL_DOMAIN_NAME; ++code) {
-        EXPECT_FALSE(CfgMgr::instance().getStagingCfg()->getCfgOption()
-                     ->isRSOOEnabled(code)) << " for option code " << code;
+        EXPECT_FALSE(CfgMgr::instance().getStagingCfg()->getCfgRSOO()
+                     ->enabled(code)) << " for option code " << code;
     }
 
     // This option is on the IANA list, so it should be allowed all the time
     // (http://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xhtml)
-    EXPECT_TRUE(CfgMgr::instance().getStagingCfg()->getCfgOption()
-                ->isRSOOEnabled(D6O_ERP_LOCAL_DOMAIN_NAME));
+    EXPECT_TRUE(CfgMgr::instance().getStagingCfg()->getCfgRSOO()
+                ->enabled(D6O_ERP_LOCAL_DOMAIN_NAME));
 
     for (uint16_t code = D6O_ERP_LOCAL_DOMAIN_NAME + 1; code < 300; ++code) {
-        EXPECT_FALSE(CfgMgr::instance().getStagingCfg()->getCfgOption()
-                     ->isRSOOEnabled(code)) << " for option code " << code;
+        EXPECT_FALSE(CfgMgr::instance().getStagingCfg()->getCfgRSOO()
+                     ->enabled(code)) << " for option code " << code;
     }
+}
 
+TEST_F(Dhcp6ParserTest, rsooNegativeNumber) {
+    ConstElementPtr status;
+    EXPECT_NO_THROW(status = configureDhcp6Server(srv_,
+        Element::fromJSON("{ " + genIfaceConfig() + ","
+                          "\"relay-supplied-options\": [ \"80\", \"-2\" ],"
+                          "\"preferred-lifetime\": 3000,"
+                          "\"rebind-timer\": 2000, "
+                          "\"renew-timer\": 1000, "
+                          "\"subnet6\": [  ], "
+                          "\"valid-lifetime\": 4000 }")));
 
+    // returned value should be 0 (success)
+    checkResult(status, 1);
+    EXPECT_TRUE(errorContainsPosition(status, "<string>"));
 }
 
+TEST_F(Dhcp6ParserTest, rsooBogusName) {
+    ConstElementPtr status;
+    EXPECT_NO_THROW(status = configureDhcp6Server(srv_,
+        Element::fromJSON("{ " + genIfaceConfig() + ","
+                          "\"relay-supplied-options\": [ \"bogus\", \"dns-servers\" ],"
+                          "\"preferred-lifetime\": 3000,"
+                          "\"rebind-timer\": 2000, "
+                          "\"renew-timer\": 1000, "
+                          "\"subnet6\": [  ], "
+                          "\"valid-lifetime\": 4000 }")));
+
+    // returned value should be 0 (success)
+    checkResult(status, 1);
+    EXPECT_TRUE(errorContainsPosition(status, "<string>"));
+}
 
 };

+ 15 - 0
src/bin/dhcp6/tests/dhcp6_client.h

@@ -110,6 +110,21 @@ public:
             status_code_ = 0;
             received_status_code_ = false;
         }
+
+        /// @brief Finds an option with the specific code in the received
+        /// configuration.
+        ///
+        /// @param code Option code.
+        ///
+        /// @return Pointer to the option if the option exists, or NULL if
+        /// the option doesn't exist.
+        OptionPtr findOption(const uint16_t code) const {
+            std::multimap<unsigned int, OptionPtr>::const_iterator it = options_.find(code);
+            if (it != options_.end()) {
+                return (it->second);
+            }
+            return (OptionPtr());
+        }
     };
 
     /// @brief Holds the DHCPv6 messages taking part in transaction between

+ 74 - 0
src/bin/dhcp6/tests/dhcp6_srv_unittest.cc

@@ -2277,6 +2277,80 @@ TEST_F(Dhcpv6SrvTest, rsoo2relays) {
     EXPECT_EQ(expected, opt120->getData());
 }
 
+// This test verifies that the server will send the option for which it
+// has a candidate, rather than the option sent by the relay in the RSOO.
+TEST_F(Dhcpv6SrvTest, rsooOverride) {
+    Dhcp6Client client;
+    // The client will be requesting specific options.
+    client.useORO(true);
+
+    // The following configuration enables RSOO options: 110 and 120.
+    // It also configures the server with option 120 which should
+    // "override" the option 120 sent in the RSOO by the relay.
+    string config =
+        "{"
+        "    \"relay-supplied-options\": [ \"110\", \"120\" ],"
+        "    \"option-def\": [ {"
+        "      \"name\": \"foo\","
+        "      \"code\": 120,"
+        "      \"type\": \"binary\","
+        "      \"array\": False,"
+        "      \"record-types\": \"\","
+        "      \"space\": \"dhcp6\","
+        "      \"encapsulate\": \"\""
+        "    } ],"
+        "    \"option-data\": [ {"
+        "      \"code\": 120,"
+        "      \"data\": \"05\""
+        "    } ],"
+        "    \"preferred-lifetime\": 3000,"
+        "    \"rebind-timer\": 2000, "
+        "    \"renew-timer\": 1000, "
+        "    \"subnet6\": [ { "
+        "        \"pools\": [ { \"pool\": \"2001:db8::/64\" } ],"
+        "        \"subnet\": \"2001:db8::/48\" "
+        "     } ],"
+        "    \"valid-lifetime\": 4000"
+        "}";
+
+    EXPECT_NO_THROW(configure(config, *client.getServer()));
+
+    // Fabricate the relay.
+    Pkt6::RelayInfo relay;
+    relay.msg_type_ = DHCPV6_RELAY_FORW;
+    relay.hop_count_ = 1;
+    relay.linkaddr_ = IOAddress("2001:db8::1");
+    relay.peeraddr_ = IOAddress("fe80::1");
+    vector<uint16_t> rsoo;
+    // The relay will send 2 options: 110, 120
+    rsoo.push_back(110);
+    rsoo.push_back(120);
+    // Use 0x1 as payload
+    OptionPtr opt = createRSOO(rsoo, 1);
+    relay.options_.insert(make_pair(opt->getType(), opt));
+    client.relay_info_.push_back(relay);
+
+    // Client should request option 120 in the ORO so as the server
+    // sends the configured option 120 to the client.
+    client.requestOption(120);
+    client.doSARR();
+
+    // The option 110 should be the one injected by the relay.
+    opt = client.config_.findOption(110);
+    ASSERT_TRUE(opt);
+    // We check that this is the option injected by the relay by
+    // checking option length. It should has 10 bytes long payload.
+    ASSERT_EQ(10, opt->getData().size());
+
+    // The second option should be the one configured on the server,
+    // rather than the one injected by the relay.
+    opt = client.config_.findOption(120);
+    ASSERT_TRUE(opt);
+    // It should have the size of 1.
+    ASSERT_EQ(1, opt->getData().size());
+}
+
+
 /// @todo: Add more negative tests for processX(), e.g. extend sanityCheck() test
 /// to call processX() methods.
 

+ 1 - 1
src/lib/dhcp/std_option_defs.h

@@ -327,7 +327,7 @@ const OptionDefParams OPTION_DEF_PARAMS6[] = {
       RECORD_DEF(LQ_RELAY_DATA_RECORDS), "" },
     { "lq-client-link", D6O_LQ_CLIENT_LINK, OPT_IPV6_ADDRESS_TYPE, true,
       NO_RECORD_DEF, "" },
-    { "rsoo", D6O_RSOO, OPT_EMPTY_TYPE, false, NO_RECORD_DEF, "dhcp4" },
+    { "rsoo", D6O_RSOO, OPT_EMPTY_TYPE, false, NO_RECORD_DEF, "rsoo-opts" },
     { "client-linklayer-addr", D6O_CLIENT_LINKLAYER_ADDR, OPT_BINARY_TYPE, false,
         NO_RECORD_DEF, "" }
 

+ 1 - 0
src/lib/dhcp/tests/pkt_captures6.cc

@@ -351,6 +351,7 @@ Pkt6Ptr isc::test::PktCaptures::captureCableLabsShortVendorClass() {
 ///          - rsoo (66)
 ///              - option 255 (len 4)
 ///              - option 256 (len 9)
+///          - remote-id option (37)
 ///          - RELAY-FORW
 ///             - SOLICIT
 ///                  - client-id option

+ 1 - 0
src/lib/dhcpsrv/Makefile.am

@@ -70,6 +70,7 @@ libkea_dhcpsrv_la_SOURCES += cfg_hosts.cc cfg_hosts.h
 libkea_dhcpsrv_la_SOURCES += cfg_iface.cc cfg_iface.h
 libkea_dhcpsrv_la_SOURCES += cfg_option.cc cfg_option.h
 libkea_dhcpsrv_la_SOURCES += cfg_option_def.cc cfg_option_def.h
+libkea_dhcpsrv_la_SOURCES += cfg_rsoo.cc cfg_rsoo.h
 libkea_dhcpsrv_la_SOURCES += cfg_subnets4.cc cfg_subnets4.h
 libkea_dhcpsrv_la_SOURCES += cfg_subnets6.cc cfg_subnets6.h
 libkea_dhcpsrv_la_SOURCES += cfg_mac_source.cc cfg_mac_source.h

+ 0 - 19
src/lib/dhcpsrv/cfg_option.cc

@@ -29,10 +29,6 @@ OptionDescriptor::equals(const OptionDescriptor& other) const {
 }
 
 CfgOption::CfgOption() {
-
-    // By default, the only allowed Relay-Supplied Options option is
-    // ERP local domain name. Other options may be added in configuration.
-    rsoo_options_.insert(D6O_ERP_LOCAL_DOMAIN_NAME);
 }
 
 bool
@@ -198,20 +194,5 @@ CfgOption::optionSpaceToVendorId(const std::string& option_space) {
     return (static_cast<uint32_t>(check));
 }
 
-void CfgOption::clearRSOO() {
-    rsoo_options_.clear();
-}
-
-bool CfgOption::isRSOOEnabled(uint16_t code) const {
-    return (rsoo_options_.find(code) != rsoo_options_.end());
-}
-
-void CfgOption::addRSOO(uint16_t code) {
-    if (rsoo_options_.find(code) == rsoo_options_.end()) {
-        // If there's no such code added yet, let's add it
-        rsoo_options_.insert(code);
-    }
-}
-
 } // end of namespace isc::dhcp
 } // end of namespace isc

+ 0 - 31
src/lib/dhcpsrv/cfg_option.h

@@ -195,11 +195,6 @@ typedef OptionContainer::nth_index<2>::type OptionContainerPersistIndex;
 /// options is useful when the client requests stateless configuration from
 /// the DHCP server and no subnet is selected for this client. This client
 /// will only receive global options.
-///
-/// isRSSOEnabled(), addRSOO(), clearRSOO() are methods related to
-/// Relay-Supplied Options option. This information does not provide any values
-/// about the options themselves, but rather contain a list of options that
-/// are allowed in RSOO ("RSOO-enabled").
 class CfgOption {
 public:
 
@@ -357,23 +352,6 @@ public:
     /// @return vendor id.
     static uint32_t optionSpaceToVendorId(const std::string& option_space);
 
-    /// @brief Removes designation of all options as RSOO-enabled.
-    ///
-    /// This method removes all designations of all options as being RSOO-enabled.
-    /// Note that the list is maintained by IANA and option 65 is officially
-    /// RSOO-enabled. This list may be extended in the future. Also, the user may
-    /// add extra options here.
-    void clearRSOO();
-
-    /// @brief Returns whether specific option code is RSOO-enabled.
-    /// @param code option code to check
-    /// @return true, if it is allowed in Relay-Supplied Options option
-    bool isRSOOEnabled(uint16_t code) const;
-
-    /// @brief Marks specified option code as RSOO-enabled.
-    /// @param code option to be enabled in RSOO
-    void addRSOO(uint16_t code);
-
 private:
 
     /// @brief Appends encapsulated options to the options in an option space.
@@ -419,15 +397,6 @@ private:
                                  uint32_t> VendorOptionSpaceCollection;
     /// @brief Container holding options grouped by vendor id.
     VendorOptionSpaceCollection vendor_options_;
-
-    /// @brief Contains a list of options that are allowed in RSOO option
-    ///
-    /// RSOO stands for Relay-Supplied Options option. This is an option that
-    /// is inserted by the relay agent with the intention that the server will
-    /// echo those options back to the client. Only those options marked as
-    /// RSOO-enabled may appear in the RSOO. Currently only option 65 is marked
-    /// as such, but more options may be added in the future. See RFC6422 for details.
-    std::set<uint16_t> rsoo_options_;
 };
 
 /// @name Pointers to the @c CfgOption objects.

+ 46 - 0
src/lib/dhcpsrv/cfg_rsoo.cc

@@ -0,0 +1,46 @@
+// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <dhcp/dhcp6.h>
+#include <dhcpsrv/cfg_rsoo.h>
+
+namespace isc {
+namespace dhcp {
+
+CfgRSOO::CfgRSOO()
+    : rsoo_options_() {
+    rsoo_options_.insert(D6O_ERP_LOCAL_DOMAIN_NAME);
+}
+
+void
+CfgRSOO::clear() {
+    rsoo_options_.clear();
+}
+
+bool
+CfgRSOO::enabled(const uint16_t code) const {
+    return (rsoo_options_.find(code) != rsoo_options_.end());
+}
+
+void
+CfgRSOO::enable(const uint16_t code) {
+    if (rsoo_options_.find(code) == rsoo_options_.end()) {
+        // If there's no such code added yet, let's add it
+        rsoo_options_.insert(code);
+    }
+}
+
+
+}
+}

+ 82 - 0
src/lib/dhcpsrv/cfg_rsoo.h

@@ -0,0 +1,82 @@
+// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef CFG_RSOO_H
+#define CFG_RSOO_H
+
+#include <boost/shared_ptr.hpp>
+#include <set>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Represents configuration of the RSOO options for the DHCP server.
+///
+/// This class holds the set of RSOO-enabled options (see RFC6422). The list
+/// of RSOO-enabled options is maintained by IANA and currently the option
+/// 65 is officially RSSO-enabled. The list may be extended in the future
+/// and this class allows for specifying any future RSOO-enabled options.
+/// The administrator may also use existing options as RSOO-enabled.
+class CfgRSOO {
+public:
+
+    /// @brief Constructor.
+    ///
+    /// It adds the default (officially) RSOO-enabled options:
+    /// - OPTION_ERP_LOCAL_DOMAIN_NAME
+    CfgRSOO();
+
+    /// @brief Removes designation of all options as RSOO_enabled.
+    ///
+    /// This method removes all designations of all options as being RSOO-enabled.
+    void clear();
+
+    /// @brief Returns whether specific option code is RSOO-enabled.
+    ///
+    /// @param code Option code to check
+    /// @return true, if it is allowed in Relay-Supplied Options option
+    bool enabled(const uint16_t code) const;
+
+    /// @brief Marks specified option code as RSOO-enabled.
+    ///
+    /// @param code option to be enabled in RSOO
+    void enable(const uint16_t code);
+
+private:
+
+    /// @brief Contains a set of options that are allowed in RSOO option
+    ///
+    /// RSOO stands for Relay-Supplied Options option. This is an option that
+    /// is inserted by the relay agent with the intention that the server will
+    /// echo those options back to the client. Only those options marked as
+    /// RSOO-enabled may appear in the RSOO. Currently only option 65 is marked
+    /// as such, but more options may be added in the future. See RFC6422 for details.
+    std::set<uint16_t> rsoo_options_;
+
+};
+
+/// @name Pointers to the @c CfgRSOO objects.
+//@{
+/// @brief Pointer to the Non-const object.
+typedef boost::shared_ptr<CfgRSOO> CfgRSOOPtr;
+
+/// @brief Pointer to the const object.
+typedef boost::shared_ptr<const CfgRSOO> ConstCfgRSOOPtr;
+
+//@}
+
+}
+}
+
+#endif // CFG_RSOO_H

+ 2 - 2
src/lib/dhcpsrv/srv_config.cc

@@ -29,14 +29,14 @@ SrvConfig::SrvConfig()
     : sequence_(0), cfg_iface_(new CfgIface()),
       cfg_option_def_(new CfgOptionDef()), cfg_option_(new CfgOption()),
       cfg_subnets4_(new CfgSubnets4()), cfg_subnets6_(new CfgSubnets6()),
-      cfg_hosts_(new CfgHosts()) {
+      cfg_hosts_(new CfgHosts()), cfg_rsoo_(new CfgRSOO()) {
 }
 
 SrvConfig::SrvConfig(const uint32_t sequence)
     : sequence_(sequence), cfg_iface_(new CfgIface()),
       cfg_option_def_(new CfgOptionDef()), cfg_option_(new CfgOption()),
       cfg_subnets4_(new CfgSubnets4()), cfg_subnets6_(new CfgSubnets6()),
-      cfg_hosts_(new CfgHosts()) {
+      cfg_hosts_(new CfgHosts()), cfg_rsoo_(new CfgRSOO()) {
 }
 
 std::string

+ 25 - 0
src/lib/dhcpsrv/srv_config.h

@@ -19,6 +19,7 @@
 #include <dhcpsrv/cfg_iface.h>
 #include <dhcpsrv/cfg_option.h>
 #include <dhcpsrv/cfg_option_def.h>
+#include <dhcpsrv/cfg_rsoo.h>
 #include <dhcpsrv/cfg_subnets4.h>
 #include <dhcpsrv/cfg_subnets6.h>
 #include <dhcpsrv/cfg_mac_source.h>
@@ -239,6 +240,24 @@ public:
         return (cfg_hosts_);
     }
 
+    /// @brief Returns pointer to the non-const object representing
+    /// set of RSOO-enabled options.
+    ///
+    /// @return Pointer to the non-const object holding RSOO-enabled
+    /// options.
+    CfgRSOOPtr getCfgRSOO() {
+        return (cfg_rsoo_);
+    }
+
+    /// @brief Returns pointer to the const object representing set
+    /// of RSOO-enabled options.
+    ///
+    /// @return Pointer to the const object holding RSOO-enabled
+    /// options.
+    ConstCfgRSOOPtr getCfgRSOO() const {
+        return (cfg_rsoo_);
+    }
+
     //@}
 
     /// @brief Returns non-const reference to an array that stores
@@ -371,6 +390,12 @@ private:
 
     /// @brief A list of configured MAC sources.
     CfgMACSource cfg_mac_source_;
+
+    /// @brief Pointer to the configuration for RSOO-enabled options.
+    ///
+    /// This object holds a set of RSOO-enabled options. See the
+    /// RFC 6422 for the definition of RSOO-enabled option.
+    CfgRSOOPtr cfg_rsoo_;
 };
 
 /// @name Pointers to the @c SrvConfig object.

+ 1 - 0
src/lib/dhcpsrv/tests/Makefile.am

@@ -64,6 +64,7 @@ libdhcpsrv_unittests_SOURCES += cfg_iface_unittest.cc
 libdhcpsrv_unittests_SOURCES += cfg_mac_source_unittest.cc
 libdhcpsrv_unittests_SOURCES += cfg_option_unittest.cc
 libdhcpsrv_unittests_SOURCES += cfg_option_def_unittest.cc
+libdhcpsrv_unittests_SOURCES += cfg_rsoo_unittest.cc
 libdhcpsrv_unittests_SOURCES += cfg_subnets4_unittest.cc
 libdhcpsrv_unittests_SOURCES += cfg_subnets6_unittest.cc
 libdhcpsrv_unittests_SOURCES += cfgmgr_unittest.cc

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

@@ -477,33 +477,5 @@ TEST(CfgOptionTest, addVendorOptions) {
     EXPECT_TRUE(options->empty());
 }
 
-// This test verifies that Relay-Supplied Options option (RSOO) is handled
-// properly.
-TEST(CfgOptionTest, rsoo) {
-    CfgOption cfg;
-
-    // All options from 0..64 are not RSOO-enabled
-    for (uint16_t code = 0; code < D6O_ERP_LOCAL_DOMAIN_NAME; ++code) {
-        EXPECT_FALSE(cfg.isRSOOEnabled(code));
-    }
-
-    // Option 65 is the only one so far that is enabled
-    EXPECT_TRUE(cfg.isRSOOEnabled(D6O_ERP_LOCAL_DOMAIN_NAME));
-
-    // Let's check other options. They should not be enabled.
-    for (uint16_t code = D6O_ERP_LOCAL_DOMAIN_NAME + 1; code < 300; ++code) {
-        EXPECT_FALSE(cfg.isRSOOEnabled(code)) << " for option code " << code;
-    }
-
-    // Let's clear it.
-    cfg.clearRSOO();
-
-    // Now not even option 65 is enabled.
-    EXPECT_FALSE(cfg.isRSOOEnabled(D6O_ERP_LOCAL_DOMAIN_NAME));
-
-    // Should be possible to specify that an option is RSOO-enabled
-    EXPECT_NO_THROW(cfg.addRSOO(200));
-    EXPECT_TRUE(cfg.isRSOOEnabled(200));
-}
 
 } // end of anonymous namespace

+ 99 - 0
src/lib/dhcpsrv/tests/cfg_rsoo_unittest.cc

@@ -0,0 +1,99 @@
+// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+#include <dhcp/dhcp6.h>
+#include <dhcpsrv/cfg_rsoo.h>
+
+#include <gtest/gtest.h>
+
+using namespace isc;
+using namespace isc::dhcp;
+
+namespace {
+
+// This test verifies that the RSOO configuration holds the default
+// RSOO-enabled options.
+TEST(CfgRSOOTest, defaults) {
+    CfgRSOO rsoo;
+    EXPECT_TRUE(rsoo.enabled(D6O_ERP_LOCAL_DOMAIN_NAME));
+    for (uint16_t code = 0; code < 200; ++code) {
+        if (code != D6O_ERP_LOCAL_DOMAIN_NAME) {
+            EXPECT_FALSE(rsoo.enabled(code))
+                << "expected that the option with code "
+                << code << " is by default RSOO-disabled, but"
+                " it is enabled";
+        }
+    }
+
+    // Now, let's see if we can remove the default options.
+    ASSER_NO_THROW(rsoo.clear());
+    EXPECT_FALSE(rsoo.enabled(D6O_ERP_LOCAL_DOMAIN_NAME));
+
+    // Make sure it can be added again.
+    ASSERT_NO_THROW(rsoo.enable(D6O_ERP_LOCAL_DOMAIN_NAME));
+    EXPECT_TRUE(rsoo.enabled(D6O_ERP_LOCAL_DOMAIN_NAME));
+}
+
+// This test verifies that it is possible to enable more RSOO options
+// and later remove all of them.
+TEST(CfgRSOOTest, enableAndClear) {
+    CfgRSOO rsoo;
+    EXPECT_TRUE(rsoo.enabled(D6O_ERP_LOCAL_DOMAIN_NAME));
+
+    // Enable option 88.
+    ASSERT_FALSE(rsoo.enabled(88));
+    ASSERT_NO_THROW(rsoo.enable(88));
+    EXPECT_TRUE(rsoo.enabled(88));
+
+    // Enable option 89.
+    ASSERT_FALSE(rsoo.enabled(89));
+    ASSERT_NO_THROW(rsoo.enable(89));
+    EXPECT_TRUE(rsoo.enabled(89));
+
+    // Remove them and make sure they have been removed.
+    ASSERT_NO_THROW(rsoo.clear());
+    for (uint16_t code = 0; code < 200; ++code) {
+        EXPECT_FALSE(rsoo.enabled(code))
+            << "expected that the option with code "
+            << code << " is RSOO-disabled after clearing"
+            " the RSOO configuration, but it is not";
+    }
+}
+
+// This test verfies that the same option may be specified
+// multiple times and that the code doesn't fail.
+TEST(CfgRSOOTest, enableTwice) {
+    CfgRSOO rsoo;
+    // By default there should be the default option enabled.
+    // Let's try to enable it again. It should pass.
+    ASSERT_NO_THROW(rsoo.enable(D6O_ERP_LOCAL_DOMAIN_NAME));
+    EXPECT_TRUE(rsoo.enabled(D6O_ERP_LOCAL_DOMAIN_NAME));
+
+    // Enable option 88.
+    ASSERT_FALSE(rsoo.enabled(88));
+    ASSERT_NO_THROW(rsoo.enable(88));
+    EXPECT_TRUE(rsoo.enabled);
+
+    // And enable it again.
+    ASSERT_NO_THROW(rsoo.enabled(88));
+    EXPECT_TRUE(rsoo.enabled(88));
+
+    // Remove all.
+    ASSERT_NO_THROW(rsoo.clear());
+    ASSERT_FALSE(rsoo.enabled(D6O_ERP_LOCAL_DOMAIN_NAME));
+    ASSERT_FALSE(rsoo.enabled(88));
+}
+
+} // end of anonymous namespace