Kea 2.7.1
option6_pdexclude.cc
Go to the documentation of this file.
1// Copyright (C) 2016-2024 Internet Systems Consortium, Inc. ("ISC")
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7#include <config.h>
8
10#include <dhcp/dhcp6.h>
13#include <util/encode/encode.h>
14
15#include <boost/dynamic_bitset.hpp>
16#include <iostream>
17#include <stdint.h>
18
19using namespace std;
20using namespace isc;
21using namespace isc::dhcp;
22using namespace isc::asiolink;
23using namespace isc::util;
24
25namespace isc {
26namespace dhcp {
27
29 const uint8_t delegated_prefix_length,
30 const isc::asiolink::IOAddress& excluded_prefix,
31 const uint8_t excluded_prefix_length)
33 excluded_prefix_length_(excluded_prefix_length),
34 subnet_id_() {
35
36 // Expecting v6 prefixes of sane length.
37 if (!delegated_prefix.isV6() || !excluded_prefix.isV6() ||
38 (delegated_prefix_length > 128) || (excluded_prefix_length_ > 128)) {
39 isc_throw(BadValue, "invalid delegated or excluded prefix values specified: "
40 << delegated_prefix << "/"
41 << static_cast<int>(delegated_prefix_length) << ", "
42 << excluded_prefix << "/"
43 << static_cast<int>(excluded_prefix_length_));
44 }
45
46 // Excluded prefix must be longer than the delegated prefix length.
47 if (excluded_prefix_length_ <= delegated_prefix_length) {
48 isc_throw(BadValue, "length of the excluded prefix "
49 << excluded_prefix << "/"
50 << static_cast<int>(excluded_prefix_length_)
51 << " must be greater than the length of the"
52 " delegated prefix " << delegated_prefix << "/"
53 << static_cast<int>(delegated_prefix_length));
54 }
55
56 // Both prefixes must share common part with a length equal to the
57 // delegated prefix length.
58 std::vector<uint8_t> delegated_prefix_bytes = delegated_prefix.toBytes();
59 boost::dynamic_bitset<uint8_t> delegated_prefix_bits(delegated_prefix_bytes.rbegin(),
60 delegated_prefix_bytes.rend());
61
62 std::vector<uint8_t> excluded_prefix_bytes = excluded_prefix.toBytes();
63 boost::dynamic_bitset<uint8_t> excluded_prefix_bits(excluded_prefix_bytes.rbegin(),
64 excluded_prefix_bytes.rend());
65
66
67 // See RFC6603, section 4.2: assert(p1>>s == p2>>s)
68 const uint8_t delta = 128 - delegated_prefix_length;
69
70 if ((delegated_prefix_bits >> delta) != (excluded_prefix_bits >> delta)) {
71 isc_throw(BadValue, "excluded prefix "
72 << excluded_prefix << "/"
73 << static_cast<int>(excluded_prefix_length_)
74 << " must have the same common prefix part of "
75 << static_cast<int>(delegated_prefix_length)
76 << " as the delegated prefix "
77 << delegated_prefix << "/"
78 << static_cast<int>(delegated_prefix_length));
79 }
80
81
82 // Shifting prefix by delegated prefix length leaves us with only a
83 // subnet id part of the excluded prefix.
84 excluded_prefix_bits <<= delegated_prefix_length;
85
86 // Calculate subnet id length.
87 const uint8_t subnet_id_length = getSubnetIDLength(delegated_prefix_length,
88 excluded_prefix_length);
89 for (uint8_t i = 0; i < subnet_id_length; ++i) {
90 // Retrieve bit representation of the current byte.
91 const boost::dynamic_bitset<uint8_t> first_byte = excluded_prefix_bits >> 120;
92
93 // Convert it to a numeric value.
94 uint8_t val = static_cast<uint8_t>(first_byte.to_ulong());
95
96 // Zero padded excluded_prefix_bits follow when excluded_prefix_length_ is
97 // not divisible by 8.
98 if (i == subnet_id_length - 1) {
99 uint8_t length_delta = excluded_prefix_length_ - delegated_prefix_length;
100 if (length_delta % 8 != 0) {
101 uint8_t mask = 0xFF;
102 mask <<= (8 - (length_delta % 8));
103 val &= mask;
104 }
105 }
106 // Store calculated value in a buffer.
107 subnet_id_.push_back(val);
108
109 // Go to the next byte.
110 excluded_prefix_bits <<= 8;
111 }
112}
113
116 : Option(V6, D6O_PD_EXCLUDE),
117 excluded_prefix_length_(0),
118 subnet_id_() {
119 unpack(begin, end);
120}
121
124 return (cloneInternal<Option6PDExclude>());
125}
126
127void
129 // Make sure that the subnet identifier is valid. It should never
130 // be empty.
131 if ((excluded_prefix_length_ == 0) || subnet_id_.empty()) {
132 isc_throw(BadValue, "subnet identifier of a Prefix Exclude option"
133 " must not be empty");
134 }
135
136 // Header = option code and length.
137 packHeader(buf);
138
139 // Excluded prefix length is always 1 byte long field.
140 buf.writeUint8(excluded_prefix_length_);
141
142 // Write the subnet identifier.
143 buf.writeData(static_cast<const void*>(&subnet_id_[0]), subnet_id_.size());
144}
145
146void
149
150 // At this point we don't know the excluded prefix length, but the
151 // minimum requirement is that reminder of this option includes the
152 // excluded prefix length and at least 1 byte of the IPv6 subnet id.
153 if (std::distance(begin, end) < 2) {
154 isc_throw(BadValue, "truncated Prefix Exclude option");
155 }
156
157 // We can safely read the excluded prefix length and move forward.
158 uint8_t excluded_prefix_length = *begin++;
159 if (excluded_prefix_length == 0) {
160 isc_throw(BadValue, "excluded prefix length must not be 0");
161 }
162
163 std::vector<uint8_t> subnet_id_bytes(begin, end);
164
165 // Subnet id parsed, proceed to the end of the option.
166 begin = end;
167
168 uint8_t last_bits_num = excluded_prefix_length % 8;
169 if (last_bits_num > 0) {
170 *subnet_id_bytes.rbegin() = (*subnet_id_bytes.rbegin() >> (8 - last_bits_num)
171 << (8 - (last_bits_num)));
172 }
173
174 excluded_prefix_length_ = excluded_prefix_length;
175 subnet_id_.swap(subnet_id_bytes);
176}
177
178uint16_t
180 return (getHeaderLen() + sizeof(excluded_prefix_length_) + subnet_id_.size());
181}
182
183std::string
184Option6PDExclude::toText(int indent) const {
185 std::ostringstream s;
186 s << headerToText(indent) << ": ";
187 s << "excluded-prefix-len=" << static_cast<unsigned>(excluded_prefix_length_)
188 << ", subnet-id=0x" << util::encode::encodeHex(subnet_id_);
189 return (s.str());
190}
191
194 const uint8_t delegated_prefix_length) const {
195 // Get binary representation of the delegated prefix.
196 std::vector<uint8_t> delegated_prefix_bytes = delegated_prefix.toBytes();
197 // We need to calculate how many bytes include the useful data and assign
198 // zeros to remaining bytes (beyond the prefix length).
199 const uint8_t bytes_length = (delegated_prefix_length / 8) +
200 static_cast<uint8_t>(delegated_prefix_length % 8 != 0);
201 std::fill(delegated_prefix_bytes.begin() + bytes_length,
202 delegated_prefix_bytes.end(), 0);
203
204 // Convert the delegated prefix to bit format.
205 boost::dynamic_bitset<uint8_t> bits(delegated_prefix_bytes.rbegin(),
206 delegated_prefix_bytes.rend());
207
208 boost::dynamic_bitset<uint8_t> subnet_id_bits(subnet_id_.rbegin(),
209 subnet_id_.rend());
210
211 // Concatenate the delegated prefix with subnet id. The resulting prefix
212 // is an excluded prefix in bit format.
213 for (int i = subnet_id_bits.size() - 1; i >= 0; --i) {
214 bits.set(128 - delegated_prefix_length - subnet_id_bits.size() + i,
215 subnet_id_bits.test(i));
216 }
217
218 // Convert the prefix to binary format.
219 std::vector<uint8_t> bytes(V6ADDRESS_LEN);
220 boost::to_block_range(bits, bytes.rbegin());
221
222 // And create a prefix object from bytes.
223 return (IOAddress::fromBytes(AF_INET6, &bytes[0]));
224}
225
226uint8_t
227Option6PDExclude::getSubnetIDLength(const uint8_t delegated_prefix_length,
228 const uint8_t excluded_prefix_length) const {
229 uint8_t subnet_id_length_bits = excluded_prefix_length -
230 delegated_prefix_length - 1;
231 uint8_t subnet_id_length = (subnet_id_length_bits / 8) + 1;
232 return (subnet_id_length);
233}
234
235} // end of namespace isc::dhcp
236} // end of namespace isc
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
virtual OptionPtr clone() const
Copies this option and returns a pointer to the copy.
asiolink::IOAddress getExcludedPrefix(const asiolink::IOAddress &delegated_prefix, const uint8_t delegated_prefix_length) const
Returns excluded prefix.
virtual std::string toText(int indent=0) const
Returns Prefix Exclude option in textual format.
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)
Constructor.
virtual uint16_t len() const
Returns length of the complete option (data length + DHCPv6 option header)
virtual void pack(isc::util::OutputBuffer &buf, bool check=true) const
Writes option in wire-format to a buffer.
virtual void unpack(OptionBufferConstIter begin, OptionBufferConstIter end)
Parses received buffer.
std::string headerToText(const int indent=0, const std::string &type_name="") const
Returns option header in the textual format.
Definition option.cc:294
virtual uint16_t getHeaderLen() const
Returns length of header (2 for v4, 4 for v6)
Definition option.cc:327
void packHeader(isc::util::OutputBuffer &buf, bool check=true) const
Store option's header in a buffer.
Definition option.cc:119
The OutputBuffer class is a buffer abstraction for manipulating mutable data.
Definition buffer.h:343
@ D6O_PD_EXCLUDE
Definition dhcp6.h:87
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
OptionBuffer::const_iterator OptionBufferConstIter
const_iterator for walking over OptionBuffer
Definition option.h:30
boost::shared_ptr< Option > OptionPtr
Definition option.h:37
string encodeHex(const vector< uint8_t > &binary)
Encode binary data in the base16 format.
Definition encode.cc:361
Defines the logger used by the top-level component of kea-lfc.