summaryrefslogtreecommitdiffstats
path: root/src/lib/dhcp/option6_pdexclude.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/dhcp/option6_pdexclude.cc')
-rw-r--r--src/lib/dhcp/option6_pdexclude.cc237
1 files changed, 237 insertions, 0 deletions
diff --git a/src/lib/dhcp/option6_pdexclude.cc b/src/lib/dhcp/option6_pdexclude.cc
new file mode 100644
index 0000000..b71a820
--- /dev/null
+++ b/src/lib/dhcp/option6_pdexclude.cc
@@ -0,0 +1,237 @@
+// Copyright (C) 2016-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 <asiolink/io_address.h>
+#include <dhcp/dhcp6.h>
+#include <dhcp/option6_pdexclude.h>
+#include <exceptions/exceptions.h>
+#include <util/encode/hex.h>
+#include <util/io_utilities.h>
+
+#include <boost/dynamic_bitset.hpp>
+#include <iostream>
+#include <stdint.h>
+
+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& delegated_prefix,
+ const uint8_t delegated_prefix_length,
+ const isc::asiolink::IOAddress& excluded_prefix,
+ const uint8_t excluded_prefix_length)
+ : Option(V6, D6O_PD_EXCLUDE),
+ excluded_prefix_length_(excluded_prefix_length),
+ subnet_id_() {
+
+ // Expecting v6 prefixes of sane length.
+ if (!delegated_prefix.isV6() || !excluded_prefix.isV6() ||
+ (delegated_prefix_length > 128) || (excluded_prefix_length_ > 128)) {
+ isc_throw(BadValue, "invalid delegated or excluded prefix values specified: "
+ << delegated_prefix << "/"
+ << static_cast<int>(delegated_prefix_length) << ", "
+ << excluded_prefix << "/"
+ << static_cast<int>(excluded_prefix_length_));
+ }
+
+ // Excluded prefix must be longer than the delegated prefix length.
+ if (excluded_prefix_length_ <= delegated_prefix_length) {
+ isc_throw(BadValue, "length of the excluded prefix "
+ << excluded_prefix << "/"
+ << static_cast<int>(excluded_prefix_length_)
+ << " must be greater than the length of the"
+ " delegated prefix " << delegated_prefix << "/"
+ << static_cast<int>(delegated_prefix_length));
+ }
+
+ // Both prefixes must share common part with a length equal to the
+ // delegated prefix length.
+ std::vector<uint8_t> delegated_prefix_bytes = delegated_prefix.toBytes();
+ boost::dynamic_bitset<uint8_t> delegated_prefix_bits(delegated_prefix_bytes.rbegin(),
+ delegated_prefix_bytes.rend());
+
+ std::vector<uint8_t> excluded_prefix_bytes = excluded_prefix.toBytes();
+ boost::dynamic_bitset<uint8_t> excluded_prefix_bits(excluded_prefix_bytes.rbegin(),
+ excluded_prefix_bytes.rend());
+
+
+ // See RFC6603, section 4.2: assert(p1>>s == p2>>s)
+ const uint8_t delta = 128 - delegated_prefix_length;
+
+ if ((delegated_prefix_bits >> delta) != (excluded_prefix_bits >> delta)) {
+ isc_throw(BadValue, "excluded prefix "
+ << excluded_prefix << "/"
+ << static_cast<int>(excluded_prefix_length_)
+ << " must have the same common prefix part of "
+ << static_cast<int>(delegated_prefix_length)
+ << " as the delegated prefix "
+ << delegated_prefix << "/"
+ << static_cast<int>(delegated_prefix_length));
+ }
+
+
+ // Shifting prefix by delegated prefix length leaves us with only a
+ // subnet id part of the excluded prefix.
+ excluded_prefix_bits <<= delegated_prefix_length;
+
+ // Calculate subnet id length.
+ const uint8_t subnet_id_length = getSubnetIDLength(delegated_prefix_length,
+ excluded_prefix_length);
+ for (uint8_t i = 0; i < subnet_id_length; ++i) {
+ // Retrieve bit representation of the current byte.
+ const boost::dynamic_bitset<uint8_t> first_byte = excluded_prefix_bits >> 120;
+
+ // Convert it to a numeric value.
+ uint8_t val = static_cast<uint8_t>(first_byte.to_ulong());
+
+ // Zero padded excluded_prefix_bits follow when excluded_prefix_length_ is
+ // not divisible by 8.
+ if (i == subnet_id_length - 1) {
+ uint8_t length_delta = excluded_prefix_length_ - delegated_prefix_length;
+ if (length_delta % 8 != 0) {
+ uint8_t mask = 0xFF;
+ mask <<= (8 - (length_delta % 8));
+ val &= mask;
+ }
+ }
+ // Store calculated value in a buffer.
+ subnet_id_.push_back(val);
+
+ // Go to the next byte.
+ excluded_prefix_bits <<= 8;
+ }
+}
+
+Option6PDExclude::Option6PDExclude(OptionBufferConstIter begin,
+ OptionBufferConstIter end)
+ : Option(V6, D6O_PD_EXCLUDE),
+ excluded_prefix_length_(0),
+ subnet_id_() {
+ unpack(begin, end);
+}
+
+OptionPtr
+Option6PDExclude::clone() const {
+ return (cloneInternal<Option6PDExclude>());
+}
+
+void
+Option6PDExclude::pack(isc::util::OutputBuffer& buf, bool) const {
+ // Make sure that the subnet identifier is valid. It should never
+ // be empty.
+ if ((excluded_prefix_length_ == 0) || subnet_id_.empty()) {
+ isc_throw(BadValue, "subnet identifier of a Prefix Exclude option"
+ " must not be empty");
+ }
+
+ // Header = option code and length.
+ packHeader(buf);
+
+ // Excluded prefix length is always 1 byte long field.
+ buf.writeUint8(excluded_prefix_length_);
+
+ // Write the subnet identifier.
+ buf.writeData(static_cast<const void*>(&subnet_id_[0]), subnet_id_.size());
+}
+
+void
+Option6PDExclude::unpack(OptionBufferConstIter begin,
+ OptionBufferConstIter end) {
+
+ // At this point we don't know the excluded prefix length, but the
+ // minimum requirement is that reminder of this option includes the
+ // excluded prefix length and at least 1 byte of the IPv6 subnet id.
+ if (std::distance(begin, end) < 2) {
+ isc_throw(BadValue, "truncated Prefix Exclude option");
+ }
+
+ // We can safely read the excluded prefix length and move forward.
+ uint8_t excluded_prefix_length = *begin++;
+ if (excluded_prefix_length == 0) {
+ isc_throw(BadValue, "excluded prefix length must not be 0");
+ }
+
+ std::vector<uint8_t> subnet_id_bytes(begin, end);
+
+ // Subnet id parsed, proceed to the end of the option.
+ begin = end;
+
+ uint8_t last_bits_num = excluded_prefix_length % 8;
+ if (last_bits_num > 0) {
+ *subnet_id_bytes.rbegin() = (*subnet_id_bytes.rbegin() >> (8 - last_bits_num)
+ << (8 - (last_bits_num)));
+ }
+
+ excluded_prefix_length_ = excluded_prefix_length;
+ subnet_id_.swap(subnet_id_bytes);
+}
+
+uint16_t
+Option6PDExclude::len() const {
+ return (getHeaderLen() + sizeof(excluded_prefix_length_) + subnet_id_.size());
+}
+
+std::string
+Option6PDExclude::toText(int indent) const {
+ std::ostringstream s;
+ s << headerToText(indent) << ": ";
+ s << "excluded-prefix-len=" << static_cast<unsigned>(excluded_prefix_length_)
+ << ", subnet-id=0x" << util::encode::encodeHex(subnet_id_);
+ return (s.str());
+}
+
+asiolink::IOAddress
+Option6PDExclude::getExcludedPrefix(const IOAddress& delegated_prefix,
+ const uint8_t delegated_prefix_length) const {
+ // Get binary representation of the delegated prefix.
+ std::vector<uint8_t> delegated_prefix_bytes = delegated_prefix.toBytes();
+ // We need to calculate how many bytes include the useful data and assign
+ // zeros to remaining bytes (beyond the prefix length).
+ const uint8_t bytes_length = (delegated_prefix_length / 8) +
+ static_cast<uint8_t>(delegated_prefix_length % 8 != 0);
+ std::fill(delegated_prefix_bytes.begin() + bytes_length,
+ delegated_prefix_bytes.end(), 0);
+
+ // Convert the delegated prefix to bit format.
+ boost::dynamic_bitset<uint8_t> bits(delegated_prefix_bytes.rbegin(),
+ delegated_prefix_bytes.rend());
+
+ boost::dynamic_bitset<uint8_t> subnet_id_bits(subnet_id_.rbegin(),
+ subnet_id_.rend());
+
+ // Concatenate the delegated prefix with subnet id. The resulting prefix
+ // is an excluded prefix in bit format.
+ for (int i = subnet_id_bits.size() - 1; i >= 0; --i) {
+ bits.set(128 - delegated_prefix_length - subnet_id_bits.size() + i,
+ subnet_id_bits.test(i));
+ }
+
+ // Convert the prefix to binary format.
+ std::vector<uint8_t> bytes(V6ADDRESS_LEN);
+ boost::to_block_range(bits, bytes.rbegin());
+
+ // And create a prefix object from bytes.
+ return (IOAddress::fromBytes(AF_INET6, &bytes[0]));
+}
+
+uint8_t
+Option6PDExclude::getSubnetIDLength(const uint8_t delegated_prefix_length,
+ const uint8_t excluded_prefix_length) const {
+ uint8_t subnet_id_length_bits = excluded_prefix_length -
+ delegated_prefix_length - 1;
+ uint8_t subnet_id_length = (subnet_id_length_bits / 8) + 1;
+ return (subnet_id_length);
+}
+
+} // end of namespace isc::dhcp
+} // end of namespace isc