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