summaryrefslogtreecommitdiffstats
path: root/src/lib/dhcp/option6_dnr.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/dhcp/option6_dnr.cc')
-rw-r--r--src/lib/dhcp/option6_dnr.cc145
1 files changed, 145 insertions, 0 deletions
diff --git a/src/lib/dhcp/option6_dnr.cc b/src/lib/dhcp/option6_dnr.cc
new file mode 100644
index 0000000..3d7bb0f
--- /dev/null
+++ b/src/lib/dhcp/option6_dnr.cc
@@ -0,0 +1,145 @@
+// Copyright (C) 2023 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 <config.h>
+
+#include <dhcp/option6_dnr.h>
+
+using namespace isc::asiolink;
+
+namespace isc {
+namespace dhcp {
+
+Option6Dnr::Option6Dnr(OptionBufferConstIter begin, OptionBufferConstIter end)
+ : Option(V6, D6O_V6_DNR), DnrInstance(V6) {
+ unpack(begin, end);
+}
+
+OptionPtr
+Option6Dnr::clone() const {
+ return (cloneInternal<Option6Dnr>());
+}
+
+void
+Option6Dnr::pack(util::OutputBuffer& buf, bool check) const {
+ packHeader(buf, check);
+
+ buf.writeUint16(service_priority_);
+ buf.writeUint16(adn_length_);
+ packAdn(buf);
+ if (adn_only_mode_) {
+ return;
+ }
+
+ buf.writeUint16(addr_length_);
+ packAddresses(buf);
+ packSvcParams(buf);
+}
+
+void
+Option6Dnr::packAddresses(util::OutputBuffer& buf) const {
+ for (const auto& address : ip_addresses_) {
+ if (!address.isV6()) {
+ isc_throw(isc::BadValue, getLogPrefix()
+ << address.toText() << " is not an IPv6 address");
+ }
+
+ buf.writeData(&address.toBytes()[0], V6ADDRESS_LEN);
+ }
+}
+
+void
+Option6Dnr::unpack(OptionBufferConstIter begin, OptionBufferConstIter end) {
+ if (std::distance(begin, end) < getMinimalLength()) {
+ isc_throw(OutOfRange, getLogPrefix()
+ << "data truncated to size " << std::distance(begin, end));
+ }
+
+ setData(begin, end);
+
+ // First two octets of Option data is Service Priority - this is mandatory field.
+ unpackServicePriority(begin);
+
+ // Next come two octets of ADN Length plus the ADN data itself (variable length).
+ // This is Opaque Data Tuple so let's use this class to retrieve the ADN data.
+ unpackAdn(begin, end);
+
+ if (begin == end) {
+ // ADN only mode, other fields are not included.
+ return;
+ }
+
+ adn_only_mode_ = false;
+
+ unpackAddresses(begin, end);
+
+ // SvcParams (variable length) field is last.
+ unpackSvcParams(begin, end);
+}
+
+std::string
+Option6Dnr::toText(int indent) const {
+ std::ostringstream stream;
+ std::string in(indent, ' '); // base indentation
+ stream << in << "type=" << type_ << "(V6_DNR), "
+ << "len=" << (len() - getHeaderLen()) << ", " << getDnrInstanceAsText();
+ return (stream.str());
+}
+
+uint16_t
+Option6Dnr::len() const {
+ return (OPTION6_HDR_LEN + dnrInstanceLen());
+}
+
+void
+Option6Dnr::unpackAddresses(OptionBufferConstIter& begin, OptionBufferConstIter end) {
+ if (std::distance(begin, end) < getAddrLengthSize()) {
+ isc_throw(OutOfRange, getLogPrefix() << "after"
+ " ADN field, there should be at least "
+ "2 bytes long Addr Length field");
+ }
+
+ // Next come two octets of Addr Length.
+ addr_length_ = isc::util::readUint16(&(*begin), getAddrLengthSize());
+ begin += getAddrLengthSize();
+ // It MUST be a multiple of 16.
+ if ((addr_length_ % V6ADDRESS_LEN) != 0) {
+ isc_throw(OutOfRange, getLogPrefix()
+ << "Addr Len=" << addr_length_ << " is not divisible by 16");
+ }
+
+ // As per draft-ietf-add-dnr 3.1.8:
+ // If additional data is supplied (i.e. not ADN only mode),
+ // the option includes at least one valid IP address.
+ if (addr_length_ == 0) {
+ isc_throw(OutOfRange, getLogPrefix()
+ << "Addr Len=" << addr_length_
+ << " but it must contain at least one valid IP address");
+ }
+
+ // Check if IPv6 Address(es) field is not truncated.
+ if (std::distance(begin, end) < addr_length_) {
+ isc_throw(OutOfRange, getLogPrefix() << "Addr Len=" << addr_length_
+ << " but IPv6 address(es) are truncated to len="
+ << std::distance(begin, end));
+ }
+
+ // Let's unpack the ipv6-address(es).
+ auto addr_end = begin + addr_length_;
+ while (begin != addr_end) {
+ try {
+ ip_addresses_.push_back(IOAddress::fromBytes(AF_INET6, &(*begin)));
+ } catch (const Exception& ex) {
+ isc_throw(BadValue, getLogPrefix() << "failed to parse IPv6 address"
+ << " - " << ex.what());
+ }
+
+ begin += V6ADDRESS_LEN;
+ }
+}
+
+} // namespace dhcp
+} // namespace isc