summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPiotrek Zadroga <piotrek@isc.org>2024-02-16 20:25:21 +0100
committerPiotrek Zadroga <piotrek@isc.org>2024-02-23 17:14:05 +0100
commitf5b0a059cbaf6941d7a67b18b2e7bac8bef1d218 (patch)
treeaa4ca1165f125e889008c293690c135d38cb3da9 /src
parent[#3141] DNRv4 config parser (diff)
downloadkea-f5b0a059cbaf6941d7a67b18b2e7bac8bef1d218.tar.xz
kea-f5b0a059cbaf6941d7a67b18b2e7bac8bef1d218.zip
[#3141] unpack SvcParams from hex data
Diffstat (limited to 'src')
-rw-r--r--src/lib/dhcp/option4_dnr.cc184
-rw-r--r--src/lib/dhcp/option4_dnr.h25
2 files changed, 185 insertions, 24 deletions
diff --git a/src/lib/dhcp/option4_dnr.cc b/src/lib/dhcp/option4_dnr.cc
index c07f708df0..0976152481 100644
--- a/src/lib/dhcp/option4_dnr.cc
+++ b/src/lib/dhcp/option4_dnr.cc
@@ -156,18 +156,19 @@ Option4Dnr::parseConfigData(const std::string& config_txt) {
const std::unordered_set<std::string> DnrInstance::FORBIDDEN_SVC_PARAMS = {"ipv4hint", "ipv6hint"};
-const std::map<std::string, uint16_t> DnrInstance::SVC_PARAMS = {
- {"mandatory", 0}, // RFC 9460, Section 14.3.2, not used in DNR
- {"alpn", 1}, // RFC 9460, Section 14.3.2, mandatory in DNR
- {"no-default-alpn", 2}, // RFC 9460, Section 14.3.2, not used in DNR
- {"port", 3}, // RFC 9460, Section 14.3.2, optional in DNR
- {"ipv4hint", 4}, // RFC 9460, Section 14.3.2, forbidden in DNR
- {"ech", 5}, // RFC 9460, Section 14.3.2, not used in DNR
- {"ipv6hint", 6}, // RFC 9460, Section 14.3.2, forbidden in DNR
- {"dohpath", 7}, // RFC 9461, optional in DNR
- {"ohttp", 8} // https://datatracker.ietf.org/doc/draft-ietf-ohai-svcb-config,
- // not used in DNR
-};
+const DnrInstance::SvcParamsMap DnrInstance::SVC_PARAMS =
+ boost::assign::list_of<DnrInstance::SvcParamsMap::relation>
+ ("mandatory", 0) // RFC 9460, Section 14.3.2, not used in DNR
+ ("alpn", 1) // RFC 9460, Section 14.3.2, mandatory in DNR
+ ("no-default-alpn", 2) // RFC 9460, Section 14.3.2, not used in DNR
+ ("port", 3) // RFC 9460, Section 14.3.2, optional in DNR
+ ("ipv4hint", 4) // RFC 9460, Section 14.3.2, forbidden in DNR
+ ("ech", 5) // RFC 9460, Section 14.3.2, not used in DNR
+ ("ipv6hint", 6) // RFC 9460, Section 14.3.2, forbidden in DNR
+ ("dohpath", 7) // RFC 9461, optional in DNR
+ ("ohttp", 8) // https://datatracker.ietf.org/doc/draft-ietf-ohai-svcb-config,
+ // not used in DNR
+ ;
const std::set<uint8_t> DnrInstance::SUPPORTED_SVC_PARAMS = {1, 3, 7};
@@ -459,6 +460,58 @@ DnrInstance::checkFields() {
}
std::string
+DnrInstance::svcParamValAsText(const std::pair<uint16_t, OpaqueDataTuple>& svc_param) const {
+ OptionBufferConstIter alpn_begin;
+ OptionBufferConstIter alpn_end;
+ std::ostringstream stream;
+ OpaqueDataTuple alpn_id_tuple(OpaqueDataTuple::LENGTH_1_BYTE);
+ bool first = true;
+ std::string ret;
+
+ switch (svc_param.first) {
+ case 1:
+ // alpn
+ // read all protocols and concatenate them with comma
+ alpn_begin = svc_param.second.getData().begin();
+ alpn_end = svc_param.second.getData().end();
+ while (alpn_begin != alpn_end) {
+ try {
+ alpn_id_tuple.unpack(alpn_begin, alpn_end);
+ } catch (const Exception& e) {
+ isc_throw(BadValue, getLogPrefix()
+ << "Exception happened when tried to parse ALPN IDs"
+ << ". Error: " << e.what());
+ }
+
+ if (first) {
+ first = false;
+ } else {
+ stream << ",";
+ }
+
+ stream << alpn_id_tuple.getText();
+ alpn_begin += alpn_id_tuple.getTotalLength();
+ }
+
+ ret = stream.str();
+ break;
+ case 3:
+ // port
+ // read uint16 from data buffer and return as string
+ ret = std::to_string(
+ readUint16(svc_param.second.getData().data(), svc_param.second.getLength()));
+ break;
+ case 7:
+ // dohpath
+ // convertion not needed, let's return data as string
+ ret = svc_param.second.getText();
+ break;
+ }
+
+ return (ret);
+}
+
+std::string
DnrInstance::getDnrInstanceAsText() const {
std::ostringstream stream;
stream << "service_priority=" << service_priority_ << ", adn_length=" << adn_length_ << ", "
@@ -470,7 +523,20 @@ DnrInstance::getDnrInstanceAsText() const {
}
if (svc_params_length_ > 0) {
- stream << ", svc_params='" + svc_params_ + "'";
+ stream << ", svc_params='";
+ bool first = true;
+ for (auto const& it : svc_params_map_) {
+ auto const& k = SVC_PARAMS.right.at(it.first);
+ if (first) {
+ first = false;
+ } else {
+ stream << " ";
+ }
+
+ stream << k << "=" << svcParamValAsText(it);
+ }
+
+ stream << "'";
}
}
@@ -552,11 +618,91 @@ void
DnrInstance::unpackSvcParams(OptionBufferConstIter& begin, OptionBufferConstIter end) {
svc_params_length_ = std::distance(begin, end);
if (svc_params_length_ > 0) {
- // This is used only when upacking hex bin option data.
- // We only assign the data to svc_params_buf_ buffer.
- // We do exact SvcParam syntax check when unpacking convenient option config notation
- // in parseDnrInstanceConfigData().
svc_params_buf_.assign(begin, end);
+
+ // used to check correct order of SvcParams
+ int prev_svc_param_key = -1;
+
+ // When the list of SvcParams is non-empty, it contains a series of
+ // SvcParamKey=SvcParamValue pairs, represented as:
+ // - a 2-octet field containing the SvcParamKey as an integer in network byte order.
+ // - a 2-octet field containing the length of the SvcParamValue as an integer
+ // between 0 and 65535 in network byte order. (uint16)
+ // - an octet string of this length whose contents are the SvcParamValue in a format
+ // determined by the SvcParamKey.
+ while (begin != end) {
+ // Minimum SvcParams len shall be 4:
+ // 2 octets SvcParamKey + 2 octets SvcParamValue Len
+ if (std::distance(begin, end) < 4) {
+ isc_throw(OutOfRange, getLogPrefix() << "DNR SvcParams data truncated to size "
+ << std::distance(begin, end));
+ }
+
+ uint16_t num_svc_param_key = readUint16(&*begin, 2);
+ begin += 2;
+
+ // Check if SvcParamKey is known in
+ // https://www.iana.org/assignments/dns-svcb/dns-svcb.xhtml
+ auto it = SVC_PARAMS.right.find(num_svc_param_key);
+ if (it == SVC_PARAMS.right.end()) {
+ isc_throw(InvalidOptionDnrSvcParams,
+ getLogPrefix() << "Wrong Svc Params syntax - key " << num_svc_param_key
+ << " not found in SvcParamKeys registry");
+ }
+
+ std::string svc_param_key = it->second;
+
+ // As per RFC9463 Section 3.1.8:
+ // The service parameters do not include "ipv4hint" or "ipv6hint" parameters.
+ if (FORBIDDEN_SVC_PARAMS.find(svc_param_key) != FORBIDDEN_SVC_PARAMS.end()) {
+ isc_throw(InvalidOptionDnrSvcParams, getLogPrefix()
+ << "Wrong Svc Params syntax - key "
+ << svc_param_key << " must not be used");
+ }
+
+ // Check if SvcParamKey usage is supported by DNR DHCP option.
+ // Note that SUPPORTED_SVC_PARAMS set may expand in future.
+ if (SUPPORTED_SVC_PARAMS.find(num_svc_param_key) == SUPPORTED_SVC_PARAMS.end()) {
+ isc_throw(InvalidOptionDnrSvcParams,
+ getLogPrefix() << "Wrong Svc Params syntax - key " << svc_param_key
+ << " not supported in DNR option SvcParams");
+ }
+
+ // As per RFC9460 Section 2.2:
+ // SvcParamKeys SHALL appear in increasing numeric order. (...)
+ // There are no duplicate SvcParamKeys.
+ //
+ // We check for duplicates here.
+ if (svc_params_map_.find(num_svc_param_key) != svc_params_map_.end()) {
+ isc_throw(InvalidOptionDnrSvcParams, getLogPrefix()
+ << "Wrong Svc Params syntax - key "
+ << svc_param_key << " is duplicated.");
+ }
+
+ // And we check correct order here.
+ if (num_svc_param_key <= prev_svc_param_key) {
+ isc_throw(InvalidOptionDnrSvcParams,
+ getLogPrefix() << "Wrong Svc Params syntax - SvcParamKeys"
+ << " SHALL appear in increasing numeric order.");
+ }
+
+ prev_svc_param_key = num_svc_param_key;
+
+ // Let's try to unpack SvcParamVal into a tuple.
+ OpaqueDataTuple svc_param_tuple(OpaqueDataTuple::LENGTH_2_BYTES);
+ try {
+ svc_param_tuple.unpack(begin, end);
+ } catch (const Exception& e) {
+ isc_throw(InvalidOptionDnrSvcParams,
+ getLogPrefix()
+ << "Wrong Svc Params syntax - failed to unpack SvcParamVal for "
+ << "SvcParamKey " << svc_param_key << ". Error: " << e.what());
+ }
+
+ svc_params_map_.insert(std::make_pair(num_svc_param_key, svc_param_tuple));
+ begin += svc_param_tuple.getTotalLength();
+ }
+
begin += svc_params_length_;
}
}
@@ -694,8 +840,8 @@ DnrInstance::parseDnrInstanceConfigData(const std::string& config_txt) {
// Check if SvcParamKey is known in
// https://www.iana.org/assignments/dns-svcb/dns-svcb.xhtml
- auto svc_params_iterator = SVC_PARAMS.find(svc_param_key);
- if (svc_params_iterator == SVC_PARAMS.end()) {
+ auto svc_params_iterator = SVC_PARAMS.left.find(svc_param_key);
+ if (svc_params_iterator == SVC_PARAMS.left.end()) {
isc_throw(InvalidOptionDnrSvcParams,
getLogPrefix() << "Wrong Svc Params syntax - key " << svc_param_key
<< " not found in SvcParamKeys registry");
diff --git a/src/lib/dhcp/option4_dnr.h b/src/lib/dhcp/option4_dnr.h
index 8afa80c0e5..682f68a710 100644
--- a/src/lib/dhcp/option4_dnr.h
+++ b/src/lib/dhcp/option4_dnr.h
@@ -8,6 +8,8 @@
#define OPTION4_DNR_H
#include <asiolink/io_address.h>
+#include <boost/assign.hpp>
+#include <boost/bimap.hpp>
#include <dhcp/dhcp4.h>
#include <dhcp/dhcp6.h>
#include <dhcp/option.h>
@@ -57,6 +59,9 @@ public:
/// @brief A Type defined for container holding IP addresses.
typedef std::vector<isc::asiolink::IOAddress> AddressContainer;
+ /// @brief A Type defined for boost Bimap holding SvcParamKeys.
+ typedef boost::bimap<std::string, uint16_t> SvcParamsMap;
+
/// @brief Size in octets of Service Priority field.
static const uint8_t SERVICE_PRIORITY_SIZE = 2;
@@ -70,7 +75,7 @@ public:
/// @brief Service parameters, used in DNR options in DHCPv4 and DHCPv6, but also in RA and DNS
///
/// The IANA registry is maintained at https://www.iana.org/assignments/dns-svcb/dns-svcb.xhtml
- static const std::map<std::string, uint16_t> SVC_PARAMS;
+ static const SvcParamsMap SVC_PARAMS;
/// @brief Ordered set of supported SvcParamKeys.
///
@@ -195,11 +200,11 @@ public:
return (ip_addresses_);
}
- /// @brief Getter of the @c svc_params_ field.
+ /// @brief Returns a reference to the buffer holding SvcParam data.
///
- /// @return Returns Service Parameters as a string.
- const std::string& getSvcParams() const {
- return (svc_params_);
+ /// @return a reference to the Service Parameters buffer
+ const OptionBuffer& getSvcParams() const {
+ return (svc_params_buf_);
}
/// @brief Returns minimal length of the DNR instance data (without headers) in octets.
@@ -499,6 +504,16 @@ private:
///
/// @note It must be called in all types of constructors of class @c DnrInstance .
void initMembers();
+
+ /// @brief Returns string representation of SvcParamVal.
+ ///
+ /// @param svc_param a key-val pair from the @c svc_params_map_ map
+ ///
+ /// @return string representation of the SvcParamVal
+ ///
+ /// @throw BadValue thrown when there is a problem with reading alpn SvcParamVal from
+ /// @c svc_params_map_
+ std::string svcParamValAsText(const std::pair<uint16_t, OpaqueDataTuple>& svc_param) const;
};
/// @brief Represents DHCPv4 Encrypted DNS %Option (code 162).