From: Tomek Mrugalski Date: Wed, 8 May 2013 09:21:59 +0000 (+0200) Subject: [2898] Second part of changes after review X-Git-Tag: bind10-1.2.0beta1-release~470^2~4 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=468687262b865e264f2bdc235ec4aff45fffaf29;p=thirdparty%2Fkea.git [2898] Second part of changes after review --- diff --git a/doc/guide/bind10-guide.xml b/doc/guide/bind10-guide.xml index e76839bfcc..5e08648cea 100644 --- a/doc/guide/bind10-guide.xml +++ b/doc/guide/bind10-guide.xml @@ -4871,43 +4871,54 @@ should include options from the isc option space:
DHCPv6 Relays - 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. - 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. - 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. + + When configuring the DHCPv6 server, it should be noted that two + similarly-named parameters can be configured for a subnet: + + + "interface" defines which local network interface can be used + to access a given subnet. + + + "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. + + + 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. + + To specify interface-id with value "vlan123", the following commands can be used: - + > config add Dhcp6/subnet6 > config set Dhcp6/subnet6[0]/subnet "2001:db8:beef::/48" > config set Dhcp6/subnet6[0]/pool [ "2001:db8:beef::/48" ] diff --git a/src/lib/dhcp/pkt6.cc b/src/lib/dhcp/pkt6.cc index 921b8e6ea0..27c8ca67ca 100644 --- a/src/lib/dhcp/pkt6.cc +++ b/src/lib/dhcp/pkt6.cc @@ -73,56 +73,58 @@ uint16_t Pkt6::len() { } OptionPtr Pkt6::getAnyRelayOption(uint16_t opt_type, RelaySearchOrder order) { - int start = 0; - int end = 0; - int direction = 0; 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()); } + 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) { case RELAY_SEARCH_FROM_CLIENT: - // search backwards + // Search backwards start = relay_info_.size() - 1; end = 0; direction = -1; break; case RELAY_SEARCH_FROM_SERVER: - // search forward + // Search forward start = 0; end = relay_info_.size() - 1; direction = 1; 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; end = start; - direction = 0; + direction = 1; break; - case RELAY_SEARCH_LAST: - // look at the outermost relay only + case RELAY_GET_LAST: + // Look at the outermost relay only start = 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 - // 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); if (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) { - // 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() 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); // taken from question->RelayInfo_[i].options_ if (opt) { - x.options_.insert(pair >(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); } } diff --git a/src/lib/dhcp/pkt6.h b/src/lib/dhcp/pkt6.h index 09050c5666..c65142ef52 100644 --- a/src/lib/dhcp/pkt6.h +++ b/src/lib/dhcp/pkt6.h @@ -60,14 +60,14 @@ public: /// relay closest to the server (i.e. outermost in the encapsulated /// message, which also means it was the last relay that relayed /// 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 { RELAY_SEARCH_FROM_CLIENT = 1, 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 @@ -226,12 +226,12 @@ public: /// @return pointer to the option (or NULL if there is no such option) 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 order option search order (see @ref RelaySearchOrder) diff --git a/src/lib/dhcp/tests/pkt6_unittest.cc b/src/lib/dhcp/tests/pkt6_unittest.cc index 5dee36c3d5..a7a366dc10 100644 --- a/src/lib/dhcp/tests/pkt6_unittest.cc +++ b/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 // and relay options - OptionPtr opt = msg->getAnyRelayOption(300, Pkt6::RELAY_SEARCH_FROM_CLIENT); // 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); // Option 200 is added in every relay. @@ -628,12 +634,12 @@ TEST_F(Pkt6Test, getAnyRelayOption) { EXPECT_TRUE(opt->equal(relay1_opt1)); // 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); EXPECT_TRUE(opt->equal(relay3_opt1)); // 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); EXPECT_TRUE(opt->equal(relay1_opt1)); @@ -647,12 +653,24 @@ TEST_F(Pkt6Test, getAnyRelayOption) { ASSERT_TRUE(opt); 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); - opt = msg->getAnyRelayOption(100, Pkt6::RELAY_SEARCH_LAST); + opt = msg->getAnyRelayOption(500, Pkt6::RELAY_GET_LAST); 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); } } diff --git a/src/lib/dhcpsrv/cfgmgr.cc b/src/lib/dhcpsrv/cfgmgr.cc index 745322ecd2..f1181ddcb9 100644 --- a/src/lib/dhcpsrv/cfgmgr.cc +++ b/src/lib/dhcpsrv/cfgmgr.cc @@ -183,7 +183,8 @@ Subnet6Ptr CfgMgr::getSubnet6(OptionPtr iface_id_option) { 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(); subnet != subnets6_.end(); ++subnet) { if ( (*subnet)->getInterfaceId() && diff --git a/src/lib/dhcpsrv/dhcpsrv_messages.mes b/src/lib/dhcpsrv/dhcpsrv_messages.mes index ff8c36e13c..b2a780706a 100644 --- a/src/lib/dhcpsrv/dhcpsrv_messages.mes +++ b/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 given interface. This particular subnet was selected, because it 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 This is a debug message reporting that the DHCP configuration manager has returned the specified IPv6 subnet for a received packet. This particular subnet was selected, because value of interface-id option matched what was 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 This is a debug message, issued when the DHCP server closes the currently diff --git a/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc b/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc index 2ffe5da68b..ba2f290270 100644 --- a/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc +++ b/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc @@ -49,7 +49,7 @@ TEST(ValueStorageTest, BooleanTesting) { EXPECT_FALSE(testStore.getParam("firstBool")); EXPECT_TRUE(testStore.getParam("secondBool")); - // Verify that we can update paramaters. + // Verify that we can update parameters. testStore.setParam("firstBool", true); 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 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 // address, let's use that subnet EXPECT_EQ(subnet1, cfg_mgr.getSubnet6(IOAddress("fe80::dead:beef")));