From: Andrei Pavel Date: Thu, 16 Jun 2016 09:16:05 +0000 (+0300) Subject: added PDEXCLUDE option and PSID data type X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=9d8280078c6ef1331bebbd6d79fd44b19c236cb9;p=thirdparty%2Fkea.git added PDEXCLUDE option and PSID data type --- diff --git a/src/bin/dhcp6/dhcp6_srv.cc b/src/bin/dhcp6/dhcp6_srv.cc index b64739051e..34aa64863f 100644 --- a/src/bin/dhcp6/dhcp6_srv.cc +++ b/src/bin/dhcp6/dhcp6_srv.cc @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -1504,6 +1505,36 @@ Dhcpv6Srv::assignIA_PD(const Pkt6Ptr& query, const Pkt6Ptr& answer, (*l)->prefixlen_, (*l)->preferred_lft_, (*l)->valid_lft_)); ia_rsp->addOption(addr); + + // Client requests some options using ORO option. Try to + // get this option from client's message. + boost::shared_ptr > option_oro = + boost::dynamic_pointer_cast > + (query->getOption(D6O_ORO)); + + if (option_oro) { + + // Get the list of options that client requested. + const std::vector& requested_opts = option_oro->getValues(); + + bool pdExcludeFound = false; + + BOOST_FOREACH(uint16_t opt, requested_opts) { + if (opt == D6O_PD_EXCLUDE) { + pdExcludeFound = true; + break; + } + } + + Pool6Ptr pool = boost::dynamic_pointer_cast + (ctx.subnet_->getPool(ctx.type_, (*l)->addr_, false)); + + if (pdExcludeFound && pool && pool->getPrefixExcludedLength() > 0) { + OptionPtr opt(new Option6PDExclude((*l)->addr_, (*l)->prefixlen_, + pool->getPrefixExcluded(), pool->getPrefixExcludedLength())); + addr->addOption(opt); + } + } } // It would be possible to insert status code=0(success) as well, @@ -1778,6 +1809,37 @@ Dhcpv6Srv::extendIA_PD(const Pkt6Ptr& query, (*l)->addr_, (*l)->prefixlen_, (*l)->preferred_lft_, (*l)->valid_lft_)); ia_rsp->addOption(prf); + + // Client requests some options using ORO option. Try to + // get this option from client's message. + boost::shared_ptr > option_oro = + boost::dynamic_pointer_cast > + (query->getOption(D6O_ORO)); + + if (option_oro) { + + // Get the list of options that client requested. + const std::vector& requested_opts = option_oro->getValues(); + + bool pdExcludeFound = false; + + BOOST_FOREACH(uint16_t opt, requested_opts) { + if (opt == D6O_PD_EXCLUDE) { + pdExcludeFound = true; + break; + } + } + + Pool6Ptr pool = boost::dynamic_pointer_cast + (ctx.subnet_->getPool(ctx.type_, (*l)->addr_, false)); + + if (pdExcludeFound && pool && pool->getPrefixExcludedLength() > 0) { + OptionPtr opt(new Option6PDExclude((*l)->addr_, (*l)->prefixlen_, + pool->getPrefixExcluded(), pool->getPrefixExcludedLength())); + prf->addOption(opt); + } + } + LOG_INFO(lease6_logger, DHCP6_PD_LEASE_RENEW) .arg(query->getLabel()) .arg((*l)->addr_.toText()) diff --git a/src/bin/dhcp6/json_config_parser.cc b/src/bin/dhcp6/json_config_parser.cc index b2a2790ac2..7a6e6ba1cd 100644 --- a/src/bin/dhcp6/json_config_parser.cc +++ b/src/bin/dhcp6/json_config_parser.cc @@ -172,11 +172,12 @@ public: BOOST_FOREACH(ConfigPair param, pd_pool_->mapValue()) { std::string entry(param.first); ParserPtr parser; - if (entry == "prefix") { + if (entry == "prefix" || entry =="excluded-prefix") { StringParserPtr str_parser(new StringParser(entry, string_values_)); parser = str_parser; - } else if (entry == "prefix-len" || entry == "delegated-len") { + } else if (entry == "prefix-len" || entry == "delegated-len" || + entry == "excluded-prefix-len") { Uint32ParserPtr code_parser(new Uint32Parser(entry, uint32_values_)); parser = code_parser; @@ -195,10 +196,17 @@ public: std::string addr_str = string_values_->getParam("prefix"); uint32_t prefix_len = uint32_values_->getParam("prefix-len"); uint32_t delegated_len = uint32_values_->getParam("delegated-len"); - + std::string excluded_prefix_str = "::"; + uint32_t excluded_prefix_len = 0; + try { + excluded_prefix_str = string_values_->getParam("excluded-prefix"); + excluded_prefix_len = uint32_values_->getParam("excluded-prefix-len"); + } catch (DhcpConfigError& ex) { + } // Attempt to construct the local pool. - pool_.reset(new Pool6(Lease::TYPE_PD, IOAddress(addr_str), - prefix_len, delegated_len)); + pool_.reset(new Pool6(Lease::TYPE_PD, IOAddress(addr_str), prefix_len, + delegated_len, IOAddress(excluded_prefix_str), + excluded_prefix_len)); } catch (const std::exception& ex) { // Some parameters don't exist or are invalid. Since we are not // aware whether they don't exist or are invalid, let's append diff --git a/src/lib/dhcp/Makefile.am b/src/lib/dhcp/Makefile.am index 72c4083545..12787157de 100644 --- a/src/lib/dhcp/Makefile.am +++ b/src/lib/dhcp/Makefile.am @@ -31,6 +31,7 @@ libkea_dhcp___la_SOURCES += option4_client_fqdn.cc option4_client_fqdn.h libkea_dhcp___la_SOURCES += option6_ia.cc option6_ia.h libkea_dhcp___la_SOURCES += option6_iaaddr.cc option6_iaaddr.h libkea_dhcp___la_SOURCES += option6_iaprefix.cc option6_iaprefix.h +libkea_dhcp___la_SOURCES += option6_pdexclude.cc option6_pdexclude.h libkea_dhcp___la_SOURCES += option6_addrlst.cc option6_addrlst.h libkea_dhcp___la_SOURCES += option6_client_fqdn.cc option6_client_fqdn.h libkea_dhcp___la_SOURCES += option6_status_code.cc option6_status_code.h diff --git a/src/lib/dhcp/dhcp6.h b/src/lib/dhcp/dhcp6.h index bcc5cabc1f..21f43a7e16 100644 --- a/src/lib/dhcp/dhcp6.h +++ b/src/lib/dhcp/dhcp6.h @@ -82,7 +82,7 @@ #define D6O_AFTR_NAME 64 /* RFC6334 */ #define D6O_ERP_LOCAL_DOMAIN_NAME 65 /* RFC6440 */ #define D6O_RSOO 66 /* RFC6422 */ -//#define D6O_PD_EXCLUDE 67 /* RFC6603 */ +#define D6O_PD_EXCLUDE 67 /* RFC6603 */ //#define D6O_VSS 68 /* RFC6607 */ //#define D6O_MIP6_IDINF 69 /* RFC6610 */ //#define D6O_MIP6_UDINF 70 /* RFC6610 */ diff --git a/src/lib/dhcp/option6_pdexclude.cc b/src/lib/dhcp/option6_pdexclude.cc new file mode 100644 index 0000000000..34fbbd183f --- /dev/null +++ b/src/lib/dhcp/option6_pdexclude.cc @@ -0,0 +1,102 @@ +// Copyright (C) 2011-2015 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include + +using namespace std; +using namespace isc; +using namespace isc::dhcp; +using namespace isc::asiolink; +using namespace isc::util; + +namespace isc { +namespace dhcp { + +Option6PDExclude::Option6PDExclude(const isc::asiolink::IOAddress& addr, + uint8_t prefix_len, + const isc::asiolink::IOAddress& prefix_excluded, + uint8_t prefix_excluded_len + ) + :Option(V6, D6O_PD_EXCLUDE) + ,addr_(addr), prefix_len_(prefix_len) + ,prefix_excluded_(prefix_excluded) + ,prefix_excluded_len_(prefix_excluded_len) { + +} + +void +Option6PDExclude::pack(isc::util::OutputBuffer& buf) { + // Header = option code and length. + packHeader(buf); + + uint8_t excludedPrefLenBytes = excludedPrefixLenBytes(); + + buf.writeData(&prefix_excluded_len_, sizeof(prefix_excluded_len_)); + + std::vector addrV6 = prefix_excluded_.toBytes(); + boost::dynamic_bitset bits(addrV6.rbegin(), addrV6.rend()); + bits = bits << prefix_len_; + + for (int i = 0; i < excludedPrefLenBytes; i++) { + boost::dynamic_bitset tmp = bits >> 120; + + uint8_t val = static_cast(tmp.to_ulong()); + + //Zero padded bits follow when prefix_excluded_len_ is not divided exactly by 8 + if (i == excludedPrefLenBytes - 1) { + uint8_t excluded_prefix_bits_no = prefix_excluded_len_ - prefix_len_; + + uint8_t unusedBits = 0xFF; + unusedBits <<= (8 - (excluded_prefix_bits_no % 8)) % 8; + + val = val & unusedBits; + } + bits = bits << 8; + buf.writeData(&val, sizeof(val)); + } +} + +void Option6PDExclude::unpack(OptionBufferConstIter begin, + OptionBufferConstIter end) { + prefix_len_ = 0; + prefix_excluded_len_ = *begin; + begin += sizeof(uint8_t); + addr_ = IOAddress::IPV6_ZERO_ADDRESS(); + prefix_excluded_ = IOAddress::IPV6_ZERO_ADDRESS(); + begin = end; +} + +uint16_t +Option6PDExclude::len() +{ + return getHeaderLen() + sizeof (prefix_excluded_len_) + + excludedPrefixLenBytes(); +} + +uint8_t +Option6PDExclude::excludedPrefixLenBytes() +{ + uint8_t excludedPrefLenBits = prefix_excluded_len_ - prefix_len_ - 1; + uint8_t excludedPrefLenBytes = (excludedPrefLenBits / 8) + 1; + return excludedPrefLenBytes; +} + +} // end of namespace isc::dhcp +} // end of namespace isc diff --git a/src/lib/dhcp/option6_pdexclude.h b/src/lib/dhcp/option6_pdexclude.h new file mode 100644 index 0000000000..677d41f67f --- /dev/null +++ b/src/lib/dhcp/option6_pdexclude.h @@ -0,0 +1,70 @@ +// Copyright (C) 2011-2015 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef OPTION6_PDEXCLUDE_H +#define OPTION6_PDEXCLUDE_H + +#include +#include + +namespace isc { +namespace dhcp { + +/// @brief DHCPv6 Option class for handling list of IPv6 addresses. +/// +/// This class handles a list of IPv6 addresses. An example of such option +/// is dns-servers option. It can also be used to handle single address. +class Option6PDExclude: public Option { + +public: + + Option6PDExclude(const isc::asiolink::IOAddress& addr, + uint8_t prefix_len, + const isc::asiolink::IOAddress& prefix_excluded, + uint8_t prefix_excluded_len); + + /// @brief Writes option in wire-format to a buffer. + /// + /// Writes option in wire-format to buffer, returns pointer to first unused + /// byte after stored option (that is useful for writing options one after + /// another). + /// + /// @param buf pointer to a buffer + /// + /// @throw BadValue Universe of the option is neither V4 nor V6. + virtual void pack(isc::util::OutputBuffer& buf); + + /// @brief Parses received buffer. + /// + /// @param begin iterator to first byte of option data + /// @param end iterator to end of option data (first byte after option end) + virtual void unpack(OptionBufferConstIter begin, + OptionBufferConstIter end); + + /// Returns length of the complete option (data length + DHCPv6 + /// option header) + /// + /// @return length of the option + virtual uint16_t len(); + +protected: + uint8_t excludedPrefixLenBytes(); + +protected: + isc::asiolink::IOAddress addr_; + uint8_t prefix_len_; + + isc::asiolink::IOAddress prefix_excluded_; + uint8_t prefix_excluded_len_; +}; + +/// @brief Pointer to the @c Option6PDExclude object. +typedef boost::shared_ptr Option6PDExcludePtr; + +} // isc::dhcp namespace +} // isc namespace + +#endif // OPTION6_PDEXCLUDE_H diff --git a/src/lib/dhcp/option_custom.cc b/src/lib/dhcp/option_custom.cc index 79d0aca57e..d78654280a 100644 --- a/src/lib/dhcp/option_custom.cc +++ b/src/lib/dhcp/option_custom.cc @@ -194,6 +194,15 @@ OptionCustom::createBuffers(const OptionBuffer& data_buf) { // that the validate() function in OptionDefinition object // should have checked wheter it is a case for this option. data_size = std::distance(data, data_buf.end()); + } else if ( *field == OPT_IPV6_PREFIX_TYPE ) { + // The size of the IPV6 prefix type is determined as + // one byte (which is the size of the prefix in bits) + // followed by the prefix bits (right-padded with + // zeros to the nearest octet boundary) + + uint8_t lenBits = *data; + uint8_t lenBytes = (lenBits + 7) / 8; + data_size = lenBytes + sizeof(lenBits); } else { // If we reached the end of buffer we assume that this option is // truncated because there is no remaining data to initialize diff --git a/src/lib/dhcp/option_data_types.cc b/src/lib/dhcp/option_data_types.cc index 8a8fdf622d..47f080d987 100644 --- a/src/lib/dhcp/option_data_types.cc +++ b/src/lib/dhcp/option_data_types.cc @@ -24,6 +24,8 @@ OptionDataTypeUtil::OptionDataTypeUtil() { data_types_["uint32"] = OPT_UINT32_TYPE; data_types_["ipv4-address"] = OPT_IPV4_ADDRESS_TYPE; data_types_["ipv6-address"] = OPT_IPV6_ADDRESS_TYPE; + data_types_["ipv6-prefix"] = OPT_IPV6_PREFIX_TYPE; + data_types_["psid"] = OPT_PSID_TYPE; data_types_["string"] = OPT_STRING_TYPE; data_types_["fqdn"] = OPT_FQDN_TYPE; data_types_["record"] = OPT_RECORD_TYPE; @@ -39,6 +41,8 @@ OptionDataTypeUtil::OptionDataTypeUtil() { data_type_names_[OPT_UINT32_TYPE] = "uint32"; data_type_names_[OPT_IPV4_ADDRESS_TYPE] = "ipv4-address"; data_type_names_[OPT_IPV6_ADDRESS_TYPE] = "ipv6-address"; + data_type_names_[OPT_IPV6_PREFIX_TYPE] = "ipv6-prefix"; + data_type_names_[OPT_PSID_TYPE] = "psid"; data_type_names_[OPT_STRING_TYPE] = "string"; data_type_names_[OPT_FQDN_TYPE] = "fqdn"; data_type_names_[OPT_RECORD_TYPE] = "record"; @@ -86,6 +90,9 @@ OptionDataTypeUtil::getDataTypeLen(const OptionDataType data_type) { case OPT_IPV6_ADDRESS_TYPE: return (asiolink::V6ADDRESS_LEN); + case OPT_PSID_TYPE: + return (3); + default: ; } diff --git a/src/lib/dhcp/option_data_types.h b/src/lib/dhcp/option_data_types.h index 4bb688f12c..7670362312 100644 --- a/src/lib/dhcp/option_data_types.h +++ b/src/lib/dhcp/option_data_types.h @@ -54,6 +54,7 @@ enum OptionDataType { OPT_IPV4_ADDRESS_TYPE, OPT_IPV6_ADDRESS_TYPE, OPT_IPV6_PREFIX_TYPE, + OPT_PSID_TYPE, OPT_STRING_TYPE, OPT_FQDN_TYPE, OPT_RECORD_TYPE, diff --git a/src/lib/dhcp/option_definition.cc b/src/lib/dhcp/option_definition.cc index b40f8e0fe9..af8198291c 100644 --- a/src/lib/dhcp/option_definition.cc +++ b/src/lib/dhcp/option_definition.cc @@ -625,6 +625,67 @@ OptionDefinition::writeToBuffer(const std::string& value, return; } + case OPT_PSID_TYPE: + { + std::string txt = value; + + // first let's remove any whitespaces + boost::erase_all(txt, " "); // space + boost::erase_all(txt, "\t"); // tabulation + + // Is this prefix/len notation? + size_t pos = txt.find("/"); + + if (pos == string::npos) { + isc_throw(BadDataTypeCast, "provided psid-len/psid " + << value + << " is not valid."); + } + + std::string txt_psid = txt.substr(0, pos); + std::string txt_psid_len = txt.substr(pos + 1); + + uint16_t psid = 0; + uint8_t psid_len = 0; + + try { + psid = lexicalCastWithRangeCheck(txt_psid); + } catch (...) { + isc_throw(BadDataTypeCast, "provided psid " + << txt_psid + << " is not valid."); + } + + try { + psid_len = lexicalCastWithRangeCheck(txt_psid_len); + } catch (...) { + isc_throw(BadDataTypeCast, "provided psid-len " + << txt_psid_len + << " is not valid."); + } + + if (psid >= (1 << psid_len)) { + isc_throw(BadDataTypeCast, "provided psid " + << txt_psid + << " is not valid."); + } + + if (psid_len > sizeof(uint16_t) * 8) { + isc_throw(BadDataTypeCast, "provided psid-len " + << txt_psid_len + << " is not valid."); + } + + psid = psid << (sizeof(uint16_t) * 8 - psid_len); + + // Write the psid length + OptionDataTypeUtil::writeInt(psid_len, buf); + + // Write the psid + OptionDataTypeUtil::writeInt(psid, buf); + + return; + } case OPT_STRING_TYPE: OptionDataTypeUtil::writeString(value, buf); return; diff --git a/src/lib/dhcp/std_option_defs.h b/src/lib/dhcp/std_option_defs.h index 1d6a68d03b..366f48bc9a 100644 --- a/src/lib/dhcp/std_option_defs.h +++ b/src/lib/dhcp/std_option_defs.h @@ -251,7 +251,7 @@ RECORD_DECL(S46_RULE, OPT_UINT8_TYPE, OPT_UINT8_TYPE, OPT_UINT8_TYPE, // s46-v4v6bind RECORD_DECL(S46_V4V6BIND, OPT_IPV4_ADDRESS_TYPE, OPT_IPV6_PREFIX_TYPE); // s46-portparams -RECORD_DECL(S46_PORTPARAMS, OPT_UINT8_TYPE, OPT_UINT8_TYPE, OPT_UINT16_TYPE); +RECORD_DECL(S46_PORTPARAMS, OPT_UINT8_TYPE, OPT_PSID_TYPE); // status-code RECORD_DECL(STATUS_CODE_RECORDS, OPT_UINT16_TYPE, OPT_STRING_TYPE); // vendor-class @@ -361,6 +361,7 @@ const OptionDefParams STANDARD_V6_OPTION_DEFINITIONS[] = { { "erp-local-domain-name", D6O_ERP_LOCAL_DOMAIN_NAME, OPT_FQDN_TYPE, false, NO_RECORD_DEF, "" }, { "rsoo", D6O_RSOO, OPT_EMPTY_TYPE, false, NO_RECORD_DEF, "rsoo-opts" }, + { "pd-exclude", D6O_PD_EXCLUDE, OPT_BINARY_TYPE, false, NO_RECORD_DEF, "" }, { "client-linklayer-addr", D6O_CLIENT_LINKLAYER_ADDR, OPT_BINARY_TYPE, false, NO_RECORD_DEF, "" }, { "dhcpv4-message", D6O_DHCPV4_MSG, OPT_BINARY_TYPE, false, NO_RECORD_DEF, "" }, diff --git a/src/lib/dhcpsrv/pool.cc b/src/lib/dhcpsrv/pool.cc index fa89aac580..136cea6d9e 100644 --- a/src/lib/dhcpsrv/pool.cc +++ b/src/lib/dhcpsrv/pool.cc @@ -76,7 +76,9 @@ Pool4::Pool4( const isc::asiolink::IOAddress& prefix, uint8_t prefix_len) Pool6::Pool6(Lease::Type type, const isc::asiolink::IOAddress& first, const isc::asiolink::IOAddress& last) - :Pool(type, first, last), prefix_len_(128) { + :Pool(type, first, last), prefix_len_(128) + ,prefix_excluded_(isc::asiolink::IOAddress::IPV6_ZERO_ADDRESS()) + ,prefix_excluded_len_(0) { // check if specified address boundaries are sane if (!first.isV6() || !last.isV6()) { @@ -117,8 +119,11 @@ Pool6::Pool6(Lease::Type type, const isc::asiolink::IOAddress& first, } Pool6::Pool6(Lease::Type type, const isc::asiolink::IOAddress& prefix, - uint8_t prefix_len, uint8_t delegated_len /* = 128 */) - :Pool(type, prefix, IOAddress("::")), prefix_len_(delegated_len) { + uint8_t prefix_len, uint8_t delegated_len /* = 128 */, + const isc::asiolink::IOAddress& prefix_excluded /*= IOAddress::IPV6_ZERO_ADDRESS()*/, + uint8_t prefix_excluded_len /* = 0 */) + :Pool(type, prefix, IOAddress("::")), prefix_len_(delegated_len) + , prefix_excluded_(prefix_excluded), prefix_excluded_len_(prefix_excluded_len){ // check if the prefix is sane if (!prefix.isV6()) { diff --git a/src/lib/dhcpsrv/pool.h b/src/lib/dhcpsrv/pool.h index d5b26aa28f..d0bdb2e76e 100644 --- a/src/lib/dhcpsrv/pool.h +++ b/src/lib/dhcpsrv/pool.h @@ -199,7 +199,9 @@ public: /// @param prefix_len specifies prefix length of the pool /// @param delegated_len specifies lenght of the delegated prefixes Pool6(Lease::Type type, const isc::asiolink::IOAddress& prefix, - uint8_t prefix_len, uint8_t delegated_len = 128); + uint8_t prefix_len, uint8_t delegated_len = 128, + const isc::asiolink::IOAddress& prefix_excluded = isc::asiolink::IOAddress::IPV6_ZERO_ADDRESS(), + uint8_t prefix_excluded_len = 0); /// @brief returns pool type /// @@ -217,6 +219,22 @@ public: return (prefix_len_); } + /// @brief returns excluded prefix + /// + /// This is useful for prefix definitions exclude pools. + /// @return excluded prefix + const isc::asiolink::IOAddress& getPrefixExcluded() const{ + return (prefix_excluded_); + } + + /// @brief returns excluded prefix length + /// + /// This is useful for prefix definitions exclude pools. + /// @return excluded prefix length (2-128) + uint8_t getPrefixExcludedLength() const{ + return (prefix_excluded_len_); + } + /// @brief returns textual representation of the pool /// /// @return textual representation @@ -225,6 +243,12 @@ public: private: /// @brief Defines prefix length (for TYPE_PD only) uint8_t prefix_len_; + + /// @brief The excluded prefix (for TYPE_PD only) + isc::asiolink::IOAddress prefix_excluded_; + + /// @brief The excluded prefix length (for TYPE_PD only) + uint8_t prefix_excluded_len_; }; /// @brief a pointer an IPv6 Pool