Browse Source

[2898] Second part of changes after review

Tomek Mrugalski 12 years ago
parent
commit
468687262b

+ 40 - 29
doc/guide/bind10-guide.xml

@@ -4871,43 +4871,54 @@ should include options from the isc option space:
       <section id="dhcp6-relays">
       <section id="dhcp6-relays">
         <title>DHCPv6 Relays</title>
         <title>DHCPv6 Relays</title>
         <para>
         <para>
-          DHCPv6 server supports remote clients connected via relays. Server
-          that has many subnets defined and receives a request from client, must
-          select appropriate subnet for it. Remote clients there are two
-          mechanisms that can be used here.  The first one is based on
-          interface-id options. While forwarding client's message, relays may
-          insert interface-id option that identifies the interface on which the
-          client message was received on. Some relays allow configuration of
-          that parameter, but it is sometimes hardcoded. This may range from
-          very simple (e.g. "vlan100") to very cryptic. One example used by real
-          hardware was "ISAM144|299|ipv6|nt:vp:1:110"). This may seem
-          meaningless, but that information is sufficient for its
-          purpose. Server may use it to select appropriate subnet and the relay
-          will know which interface to use for response transmission when it
-          gets that value back. This value must be unique in the whole
-          network. Server configuration must match whatever values are inserted
-          by the relays.
+          A DHCPv6 server with multiple subnets defined must select the
+          appropriate subnet when it receives a request from client.  For clients
+          connected via relays, two mechanisms are used.
         </para>
         </para>
         <para>
         <para>
-          The second way in which server may pick the proper subnet is by using
-          linkaddr field in the RELAY_FORW message. Name of this field is somewhat
-          misleading. It does not contain link-layer address, but an address that
-          is used to identify a link. This typically is a global address. Kea
-          server checks if that address belongs to any defined subnet6. If it does,
-          that subnet is selected for client's request.
+          The first uses the linkaddr field in the RELAY_FORW message. The name
+          of this field is somewhat misleading in that it does not contain link-layer
+          address: instead, it holds an address (typically a global address) that is
+          used to identify a link. The DHCPv6 server checks if the address belongs
+          to a defined subnet and, if it does, that subnet is selected for the client's
+          request.
         </para>
         </para>
         <para>
         <para>
-          It should be noted that "interface" that defines which local network
-          interface can be used to access a given subnet and "interface-id" that
-          specify the content of the interface-id option used by relays are mutually
-          exclusive. A subnet cannot be both reachable locally (direct traffic) and 
-          via relays (remote traffic). Specifying both is a configuration error and
-          Kea server will refuse such configuration.
+          The second mechanism is based on interface-id options. While forwarding client's
+          message, relays may insert an interface-id option into the message that
+          identifies the interface on the relay that received client message. (Some
+          relays allow configuration of that parameter, but it is sometimes
+          hardcoded and may range from very simple (e.g. "vlan100") to very cryptic:
+          one example seen on real hardware was "ISAM144|299|ipv6|nt:vp:1:110"). The
+          server can use this information to select the appropriate subnet.
+          The information is also returned to the relay which then knows which
+          interface to use to transmit the response to the client. In order to
+          use this successfully, the relay interface IDs must be unique within
+          the network, and the server configuration must match those values.
         </para>
         </para>
         <para>
         <para>
+          When configuring the DHCPv6 server, it should be noted that two
+          similarly-named parameters can be configured for a subnet:
+          <itemizedlist>
+            <listitem><simpara>
+              "interface" defines which local network interface can be used
+              to access a given subnet.
+            </simpara></listitem>
+            <listitem><simpara>
+              "interface-id" specifies the content of the interface-id option
+              used by relays to identify the interface on the relay to which
+              the response packet is sent.
+            </simpara></listitem>
+          </itemizedlist>
+          The two are mutually exclusive: a subnet cannot be both reachable locally
+          (direct traffic) and via relays (remote traffic). Specifying both is a
+          configuration error and the DHCPv6 server will refuse such a configuration.
+        </para>
+
+        <para>
           To specify interface-id with value "vlan123", the following commands can
           To specify interface-id with value "vlan123", the following commands can
           be used:
           be used:
-<screen>
+          <screen>
 &gt; <userinput>config add Dhcp6/subnet6</userinput>
 &gt; <userinput>config add Dhcp6/subnet6</userinput>
 &gt; <userinput>config set Dhcp6/subnet6[0]/subnet "2001:db8:beef::/48"</userinput>
 &gt; <userinput>config set Dhcp6/subnet6[0]/subnet "2001:db8:beef::/48"</userinput>
 &gt; <userinput>config set Dhcp6/subnet6[0]/pool [ "2001:db8:beef::/48" ]</userinput>
 &gt; <userinput>config set Dhcp6/subnet6[0]/pool [ "2001:db8:beef::/48" ]</userinput>

+ 36 - 33
src/lib/dhcp/pkt6.cc

@@ -73,56 +73,58 @@ uint16_t Pkt6::len() {
 }
 }
 
 
 OptionPtr Pkt6::getAnyRelayOption(uint16_t opt_type, RelaySearchOrder order) {
 OptionPtr Pkt6::getAnyRelayOption(uint16_t opt_type, RelaySearchOrder order) {
-    int start = 0;
-    int end = 0;
-    int direction = 0;
 
 
     if (relay_info_.empty()) {
     if (relay_info_.empty()) {
-        // there's no relay info, this is a direct message
+        // There's no relay info, this is a direct message
         return (OptionPtr());
         return (OptionPtr());
     }
     }
 
 
+    int start = 0; // First relay to check
+    int end = 0;   // Last relay to check
+    int direction = 0; // How we going to iterate: forward or backward?
+
     switch (order) {
     switch (order) {
     case RELAY_SEARCH_FROM_CLIENT:
     case RELAY_SEARCH_FROM_CLIENT:
-        // search backwards
+        // Search backwards
         start = relay_info_.size() - 1;
         start = relay_info_.size() - 1;
         end = 0;
         end = 0;
         direction = -1;
         direction = -1;
         break;
         break;
     case RELAY_SEARCH_FROM_SERVER:
     case RELAY_SEARCH_FROM_SERVER:
-        // search forward
+        // Search forward
         start = 0;
         start = 0;
         end = relay_info_.size() - 1;
         end = relay_info_.size() - 1;
         direction = 1;
         direction = 1;
         break;
         break;
-    case RELAY_SEARCH_FIRST:
-        // look at the innermost relay only
+    case RELAY_GET_FIRST:
+        // Look at the innermost relay only
         start = relay_info_.size() - 1;
         start = relay_info_.size() - 1;
         end = start;
         end = start;
-        direction = 0;
+        direction = 1;
         break;
         break;
-    case RELAY_SEARCH_LAST:
-        // look at the outermost relay only
+    case RELAY_GET_LAST:
+        // Look at the outermost relay only
         start = 0;
         start = 0;
         end = 0;
         end = 0;
-        direction = 0;
+        direction = 1;
     }
     }
 
 
-    // this is a tricky loop. It must go from start to end, but it must work in
+    // This is a tricky loop. It must go from start to end, but it must work in
     // both directions (start > end; or start < end). We can't use regular
     // both directions (start > end; or start < end). We can't use regular
-    // exit condition, because we don't know whether to use i <= end or i >= end
-    for (int i = start; ; i += direction) {
+    // exit condition, because we don't know whether to use i <= end or i >= end.
+    // That's why we check if in the next iteration we would go past the
+    // list (end + direction). It is similar to STL concept of end pointing
+    // to a place after the last element
+    for (int i = start; i != end + direction; i += direction) {
         OptionPtr opt = getRelayOption(opt_type, i);
         OptionPtr opt = getRelayOption(opt_type, i);
         if (opt) {
         if (opt) {
             return (opt);
             return (opt);
         }
         }
-
-        if (i == end) {
-            break;
-        }
     }
     }
 
 
-    return getRelayOption(opt_type, end);
+    // We iterated over specified relays and haven't found what we were
+    // looking for
+    return (OptionPtr());
 }
 }
 
 
 
 
@@ -539,27 +541,28 @@ const char* Pkt6::getName() const {
 
 
 void Pkt6::copyRelayInfo(const Pkt6Ptr& question) {
 void Pkt6::copyRelayInfo(const Pkt6Ptr& question) {
 
 
-    // we use index rather than iterator, because we need that as a parameter
+    // We use index rather than iterator, because we need that as a parameter
     // passed to getRelayOption()
     // passed to getRelayOption()
     for (int i = 0; i < question->relay_info_.size(); ++i) {
     for (int i = 0; i < question->relay_info_.size(); ++i) {
-        RelayInfo x;
-        x.msg_type_ = DHCPV6_RELAY_REPL;
-        x.hop_count_ = question->relay_info_[i].hop_count_;
-        x.linkaddr_ = question->relay_info_[i].linkaddr_;
-        x.peeraddr_ = question->relay_info_[i].peeraddr_;
-
-        // Is there interface-id option in this nesting level if there is,
-        // we need to echo it back
+        RelayInfo info;
+        info.msg_type_ = DHCPV6_RELAY_REPL;
+        info.hop_count_ = question->relay_info_[i].hop_count_;
+        info.linkaddr_ = question->relay_info_[i].linkaddr_;
+        info.peeraddr_ = question->relay_info_[i].peeraddr_;
+
+        // Is there an interface-id option in this nesting level?
+        // If there is, we need to echo it back
         OptionPtr opt = question->getRelayOption(D6O_INTERFACE_ID, i);
         OptionPtr opt = question->getRelayOption(D6O_INTERFACE_ID, i);
         // taken from question->RelayInfo_[i].options_
         // taken from question->RelayInfo_[i].options_
         if (opt) {
         if (opt) {
-            x.options_.insert(pair<int, boost::shared_ptr<Option> >(opt->getType(), opt));
+            info.options_.insert(make_pair(opt->getType(), opt));
         }
         }
 
 
-        /// @todo: implement support for ERO (Echo Request Option, RFC4994)
+        /// @todo: Implement support for ERO (Echo Request Option, RFC4994)
 
 
-        // add this relay-repl info to our message
-        relay_info_.push_back(x);
+        // Add this relay-forw info (client's message) to our relay-repl
+        // message (server's response)
+        relay_info_.push_back(info);
     }
     }
 }
 }
 
 

+ 10 - 10
src/lib/dhcp/pkt6.h

@@ -60,14 +60,14 @@ public:
     /// relay closest to the server (i.e. outermost in the encapsulated
     /// relay closest to the server (i.e. outermost in the encapsulated
     /// message, which also means it was the last relay that relayed
     /// message, which also means it was the last relay that relayed
     /// the received message and will be the first one to process
     /// the received message and will be the first one to process
-    /// server's response). RELAY_SEARCH_FIRST is option from the first
-    /// relay (closest to the client), RELAY_SEARCH_LAST is the
-    /// last relay (closest to the server).
+    /// server's response). RELAY_GET_FIRST will try to get option from
+    /// the first relay only (closest to the client), RELAY_GET_LAST will
+    /// try to get option form the the last relay (closest to the server).
     enum RelaySearchOrder {
     enum RelaySearchOrder {
         RELAY_SEARCH_FROM_CLIENT = 1,
         RELAY_SEARCH_FROM_CLIENT = 1,
         RELAY_SEARCH_FROM_SERVER = 2,
         RELAY_SEARCH_FROM_SERVER = 2,
-        RELAY_SEARCH_FIRST = 3,
-        RELAY_SEARCH_LAST = 4
+        RELAY_GET_FIRST = 3,
+        RELAY_GET_LAST = 4
     };
     };
 
 
     /// @brief structure that describes a single relay information
     /// @brief structure that describes a single relay information
@@ -226,12 +226,12 @@ public:
     /// @return pointer to the option (or NULL if there is no such option)
     /// @return pointer to the option (or NULL if there is no such option)
     OptionPtr getRelayOption(uint16_t option_code, uint8_t nesting_level);
     OptionPtr getRelayOption(uint16_t option_code, uint8_t nesting_level);
 
 
-    /// @brief returns first instance of a specified option
+    /// @brief Return first instance of a specified option
     ///
     ///
-    /// When client's packet traverses multiple relays, each passing relay
-    /// may insert extra options. This method allows getting specific instance
-    /// of a given option (closest to the client, closest to the server, etc.)
-    /// See @ref RelaySearchOrder for detailed description.
+    /// When a client's packet traverses multiple relays, each passing relay may
+    /// insert extra options. This method allows the specific instance of a given
+    /// option to be obtained (e.g. closest to the client, closest to the server,
+    /// etc.) See @ref RelaySearchOrder for a detailed description.
     ///
     ///
     /// @param option_code searched option
     /// @param option_code searched option
     /// @param order option search order (see @ref RelaySearchOrder)
     /// @param order option search order (see @ref RelaySearchOrder)

+ 23 - 5
src/lib/dhcp/tests/pkt6_unittest.cc

@@ -609,8 +609,14 @@ TEST_F(Pkt6Test, getAnyRelayOption) {
 
 
     // First check that the getAnyRelayOption does not confuse client options
     // First check that the getAnyRelayOption does not confuse client options
     // and relay options
     // and relay options
-    OptionPtr opt = msg->getAnyRelayOption(300, Pkt6::RELAY_SEARCH_FROM_CLIENT);
     // 300 is a client option, present in the message itself.
     // 300 is a client option, present in the message itself.
+    OptionPtr opt = msg->getAnyRelayOption(300, Pkt6::RELAY_SEARCH_FROM_CLIENT);
+    EXPECT_FALSE(opt);
+    opt = msg->getAnyRelayOption(300, Pkt6::RELAY_SEARCH_FROM_SERVER);
+    EXPECT_FALSE(opt);
+    opt = msg->getAnyRelayOption(300, Pkt6::RELAY_GET_FIRST);
+    EXPECT_FALSE(opt);
+    opt = msg->getAnyRelayOption(300, Pkt6::RELAY_GET_LAST);
     EXPECT_FALSE(opt);
     EXPECT_FALSE(opt);
 
 
     // Option 200 is added in every relay.
     // Option 200 is added in every relay.
@@ -628,12 +634,12 @@ TEST_F(Pkt6Test, getAnyRelayOption) {
     EXPECT_TRUE(opt->equal(relay1_opt1));
     EXPECT_TRUE(opt->equal(relay1_opt1));
 
 
     // We just want option from the first relay (closest to the client)
     // We just want option from the first relay (closest to the client)
-    opt = msg->getAnyRelayOption(200, Pkt6::RELAY_SEARCH_FIRST);
+    opt = msg->getAnyRelayOption(200, Pkt6::RELAY_GET_FIRST);
     ASSERT_TRUE(opt);
     ASSERT_TRUE(opt);
     EXPECT_TRUE(opt->equal(relay3_opt1));
     EXPECT_TRUE(opt->equal(relay3_opt1));
 
 
     // We just want option from the last relay (closest to the server)
     // We just want option from the last relay (closest to the server)
-    opt = msg->getAnyRelayOption(200, Pkt6::RELAY_SEARCH_LAST);
+    opt = msg->getAnyRelayOption(200, Pkt6::RELAY_GET_LAST);
     ASSERT_TRUE(opt);
     ASSERT_TRUE(opt);
     EXPECT_TRUE(opt->equal(relay1_opt1));
     EXPECT_TRUE(opt->equal(relay1_opt1));
 
 
@@ -647,12 +653,24 @@ TEST_F(Pkt6Test, getAnyRelayOption) {
     ASSERT_TRUE(opt);
     ASSERT_TRUE(opt);
     EXPECT_TRUE(opt->equal(relay2_opt1));
     EXPECT_TRUE(opt->equal(relay2_opt1));
 
 
-    opt = msg->getAnyRelayOption(100, Pkt6::RELAY_SEARCH_FIRST);
+    opt = msg->getAnyRelayOption(100, Pkt6::RELAY_GET_FIRST);
+    EXPECT_FALSE(opt);
+
+    opt = msg->getAnyRelayOption(100, Pkt6::RELAY_GET_LAST);
+    EXPECT_FALSE(opt);
+
+    // Finally, try to get an option that does not exist
+    opt = msg->getAnyRelayOption(500, Pkt6::RELAY_GET_FIRST);
     EXPECT_FALSE(opt);
     EXPECT_FALSE(opt);
 
 
-    opt = msg->getAnyRelayOption(100, Pkt6::RELAY_SEARCH_LAST);
+    opt = msg->getAnyRelayOption(500, Pkt6::RELAY_GET_LAST);
     EXPECT_FALSE(opt);
     EXPECT_FALSE(opt);
 
 
+    opt = msg->getAnyRelayOption(500, Pkt6::RELAY_SEARCH_FROM_SERVER);
+    EXPECT_FALSE(opt);
+
+    opt = msg->getAnyRelayOption(500, Pkt6::RELAY_SEARCH_FROM_CLIENT);
+    EXPECT_FALSE(opt);
 }
 }
 
 
 }
 }

+ 2 - 1
src/lib/dhcpsrv/cfgmgr.cc

@@ -183,7 +183,8 @@ Subnet6Ptr CfgMgr::getSubnet6(OptionPtr iface_id_option) {
         return (Subnet6Ptr());
         return (Subnet6Ptr());
     }
     }
 
 
-    // If there is more than one, we need to choose the proper one
+    // Let's iterate over all subnets and for those that have interface-id
+    // defined, check if the interface-id is equal to what we are looking for
     for (Subnet6Collection::iterator subnet = subnets6_.begin();
     for (Subnet6Collection::iterator subnet = subnets6_.begin();
          subnet != subnets6_.end(); ++subnet) {
          subnet != subnets6_.end(); ++subnet) {
         if ( (*subnet)->getInterfaceId() &&
         if ( (*subnet)->getInterfaceId() &&

+ 2 - 2
src/lib/dhcpsrv/dhcpsrv_messages.mes

@@ -105,14 +105,14 @@ This is a debug message reporting that the DHCP configuration manager
 has returned the specified IPv6 subnet for a packet received over
 has returned the specified IPv6 subnet for a packet received over
 given interface.  This particular subnet was selected, because it
 given interface.  This particular subnet was selected, because it
 was specified as being directly reachable over given interface. (see
 was specified as being directly reachable over given interface. (see
-'interface' parameter in subnet6 definition).
+'interface' parameter in the subnet6 definition).
 
 
 % DHCPSRV_CFGMGR_SUBNET6_IFACE_ID selected subnet %1 (interface-id match) for incoming packet
 % DHCPSRV_CFGMGR_SUBNET6_IFACE_ID selected subnet %1 (interface-id match) for incoming packet
 This is a debug message reporting that the DHCP configuration manager
 This is a debug message reporting that the DHCP configuration manager
 has returned the specified IPv6 subnet for a received packet. This particular
 has returned the specified IPv6 subnet for a received packet. This particular
 subnet was selected, because value of interface-id option matched what was
 subnet was selected, because value of interface-id option matched what was
 configured in server's interface-id option for that selected subnet6.
 configured in server's interface-id option for that selected subnet6.
-(see 'interface-id' parameter in subnet6 definition).
+(see 'interface-id' parameter in the subnet6 definition).
 
 
 % DHCPSRV_CLOSE_DB closing currently open %1 database
 % DHCPSRV_CLOSE_DB closing currently open %1 database
 This is a debug message, issued when the DHCP server closes the currently
 This is a debug message, issued when the DHCP server closes the currently

+ 5 - 1
src/lib/dhcpsrv/tests/cfgmgr_unittest.cc

@@ -49,7 +49,7 @@ TEST(ValueStorageTest, BooleanTesting) {
     EXPECT_FALSE(testStore.getParam("firstBool"));
     EXPECT_FALSE(testStore.getParam("firstBool"));
     EXPECT_TRUE(testStore.getParam("secondBool"));
     EXPECT_TRUE(testStore.getParam("secondBool"));
 
 
-    // Verify that we can update paramaters.
+    // Verify that we can update parameters.
     testStore.setParam("firstBool", true);
     testStore.setParam("firstBool", true);
     testStore.setParam("secondBool", false);
     testStore.setParam("secondBool", false);
 
 
@@ -437,6 +437,10 @@ TEST_F(CfgMgrTest, subnet6Interface) {
     // Now we have only one subnet, any request will be served from it
     // Now we have only one subnet, any request will be served from it
     EXPECT_EQ(subnet1, cfg_mgr.getSubnet6("foo"));
     EXPECT_EQ(subnet1, cfg_mgr.getSubnet6("foo"));
 
 
+    // Check that the interface name is checked even when there is
+    // only one subnet defined.
+    EXPECT_FALSE(cfg_mgr.getSubnet6("bar"));
+
     // If we have only a single subnet and the request came from a local
     // If we have only a single subnet and the request came from a local
     // address, let's use that subnet
     // address, let's use that subnet
     EXPECT_EQ(subnet1, cfg_mgr.getSubnet6(IOAddress("fe80::dead:beef")));
     EXPECT_EQ(subnet1, cfg_mgr.getSubnet6(IOAddress("fe80::dead:beef")));