Kea 1.5.0
addr_utilities.cc
Go to the documentation of this file.
1// Copyright (C) 2012-2018 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
11
12#include <vector>
13#include <limits>
14#include <string.h>
15
16using namespace isc;
17using namespace isc::asiolink;
18
19namespace {
20
24const uint32_t bitMask4[] = { 0xffffffff, 0x7fffffff, 0x3fffffff, 0x1fffffff,
25 0x0fffffff, 0x07ffffff, 0x03ffffff, 0x01ffffff,
26 0x00ffffff, 0x007fffff, 0x003fffff, 0x001fffff,
27 0x000fffff, 0x0007ffff, 0x0003ffff, 0x0001ffff,
28 0x0000ffff, 0x00007fff, 0x00003fff, 0x00001fff,
29 0x00000fff, 0x000007ff, 0x000003ff, 0x000001ff,
30 0x000000ff, 0x0000007f, 0x0000003f, 0x0000001f,
31 0x0000000f, 0x00000007, 0x00000003, 0x00000001,
32 0x00000000 };
33
35const uint8_t bitMask6[]= { 0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff };
36
38const uint8_t revMask6[]= { 0xff, 0x7f, 0x3f, 0x1f, 0xf, 0x7, 0x3, 0x1 };
39
40
48isc::asiolink::IOAddress firstAddrInPrefix6(const isc::asiolink::IOAddress& prefix,
49 uint8_t len) {
50 if (len > 128) {
52 "Too large netmask. 0..128 is allowed in IPv6");
53 }
54
55 // First we copy the whole address as 16 bytes.
56 // We don't check that it is a valid IPv6 address and thus has
57 // the required length because it is already checked by
58 // the calling function.
59 uint8_t packed[V6ADDRESS_LEN];
60 memcpy(packed, &prefix.toBytes()[0], V6ADDRESS_LEN);
61
62 // If the length is divisible by 8, it is simple. We just zero out the host
63 // part. Otherwise we need to handle the byte that has to be partially
64 // zeroed.
65 if (len % 8 != 0) {
66
67 // Get the appropriate mask. It has relevant bits (those that should
68 // stay) set and irrelevant (those that should be wiped) cleared.
69 uint8_t mask = bitMask6[len % 8];
70
71 // Let's leave only whatever the mask says should not be cleared.
72 packed[len / 8] = packed[len / 8] & mask;
73
74 // Since we have just dealt with this byte, let's move the prefix length
75 // to the beginning of the next byte (len is expressed in bits).
76 len = (len / 8 + 1) * 8;
77 }
78
79 // Clear out the remaining bits.
80 for (int i = len / 8; i < sizeof(packed); ++i) {
81 packed[i] = 0x0;
82 }
83
84 // Finally, let's wrap this into nice and easy IOAddress object.
85 return (isc::asiolink::IOAddress::fromBytes(AF_INET6, packed));
86}
87
95isc::asiolink::IOAddress firstAddrInPrefix4(const isc::asiolink::IOAddress& prefix,
96 uint8_t len) {
97 if (len > 32) {
98 isc_throw(isc::BadValue, "Too large netmask. 0..32 is allowed in IPv4");
99 }
100
101 // We don't check that it is a valid IPv4 address and thus has
102 // a required length of 4 bytes because it has been already
103 // checked by the calling function.
104 uint32_t addr = prefix.toUint32();
105 return (IOAddress(addr & (~bitMask4[len])));
106}
107
115isc::asiolink::IOAddress lastAddrInPrefix4(const isc::asiolink::IOAddress& prefix,
116 uint8_t len) {
117 if (len > 32) {
118 isc_throw(isc::BadValue, "Too large netmask. 0..32 is allowed in IPv4");
119 }
120
121 uint32_t addr = prefix.toUint32();
122 return (IOAddress(addr | bitMask4[len]));
123}
124
132isc::asiolink::IOAddress lastAddrInPrefix6(const isc::asiolink::IOAddress& prefix,
133 uint8_t len) {
134 if (len > 128) {
136 "Too large netmask. 0..128 is allowed in IPv6");
137 }
138
139 // First we copy the whole address as 16 bytes.
140 uint8_t packed[V6ADDRESS_LEN];
141 memcpy(packed, &prefix.toBytes()[0], 16);
142
143 // if the length is divisible by 8, it is simple. We just fill the host part
144 // with ones. Otherwise we need to handle the byte that has to be partially
145 // zeroed.
146 if (len % 8 != 0) {
147 // Get the appropriate mask. It has relevant bits (those that should
148 // stay) set and irrelevant (those that should be set to 1) cleared.
149 uint8_t mask = bitMask6[len % 8];
150
151 // Let's set those irrelevant bits with 1. It would be perhaps
152 // easier to not use negation here and invert bitMask6 content. However,
153 // with this approach, we can use the same mask in first and last
154 // address calculations.
155 packed[len / 8] = packed[len / 8] | ~mask;
156
157 // Since we have just dealt with this byte, let's move the prefix length
158 // to the beginning of the next byte (len is expressed in bits).
159 len = (len / 8 + 1) * 8;
160 }
161
162 // Finally set remaining bits to 1.
163 for (int i = len / 8; i < sizeof(packed); ++i) {
164 packed[i] = 0xff;
165 }
166
167 // Finally, let's wrap this into nice and easy IOAddress object.
168 return (isc::asiolink::IOAddress::fromBytes(AF_INET6, packed));
169}
170
171}; // end of anonymous namespace
172
173namespace isc {
174namespace dhcp {
175
177 uint8_t len) {
178 if (prefix.isV4()) {
179 return (firstAddrInPrefix4(prefix, len));
180
181 } else {
182 return (firstAddrInPrefix6(prefix, len));
183
184 }
185}
186
188 uint8_t len) {
189 if (prefix.isV4()) {
190 return (lastAddrInPrefix4(prefix, len));
191
192 } else {
193 return (lastAddrInPrefix6(prefix, len));
194
195 }
196}
197
199 if (len > 32) {
200 isc_throw(BadValue, "Invalid netmask size "
201 << static_cast<unsigned>(len) << ", allowed range is 0..32");
202 }
203 uint32_t x = ~bitMask4[len];
204
205 return (IOAddress(x));
206}
207
208uint64_t
210 const isc::asiolink::IOAddress& max) {
211 if (min.getFamily() != max.getFamily()) {
212 isc_throw(BadValue, "Both addresses have to be the same family");
213 }
214
215 if (max < min) {
216 isc_throw(BadValue, min.toText() << " must not be greater than "
217 << max.toText());
218 }
219
220 if (min.isV4()) {
221 // Let's explicitly cast last_ and first_ (IOAddress). This conversion is
222 // automatic, but let's explicitly cast it show that we moved to integer
223 // domain and addresses are now substractable.
224 uint64_t max_numeric = static_cast<uint64_t>(max.toUint32());
225 uint64_t min_numeric = static_cast<uint64_t>(min.toUint32());
226
227 // We can simply subtract the values. We need to increase the result
228 // by one, as both min and max are included in the range. So even if
229 // min == max, there's one address.
230 return (max_numeric - min_numeric + 1);
231 } else {
232
233 // Calculating the difference in v6 is more involved. Let's subtract
234 // one from the other. By subtracting min from max, we move the
235 // [a, b] range to the [0, (b-a)] range. We don't care about the beginning
236 // of the new range (it's always zero). The upper bound now specifies
237 // the number of addresses minus one.
238 IOAddress count = IOAddress::subtract(max, min);
239
240 // There's one very special case. Someone is trying to check how many
241 // IPv6 addresses are in IPv6 address space. He called this method
242 // with ::, ffff:ffff:ffff:fffff:ffff:ffff:ffff:ffff. The diff is also
243 // all 1s. Had we increased it by one, the address would flip to all 0s.
244 // This will not happen in a real world. Apparently, unit-tests are
245 // sometimes nastier then a real world.
246 static IOAddress max6("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
247 if (count == max6) {
248 return (std::numeric_limits<uint64_t>::max());
249 }
250
251 // Increase it by one (a..a range still contains one address, even though
252 // a subtracted from a is zero).
253 count = IOAddress::increase(count);
254
255 // We don't have uint128, so for anything greater than 2^64, we'll just
256 // assume numeric_limits<uint64_t>::max. Let's do it the manual way.
257 const std::vector<uint8_t>& bin(count.toBytes());
258
259 // If any of the most significant 64 bits is set, we have more than
260 // 2^64 addresses and can't represent it even on uint64_t.
261 for (int i = 0 ; i < 8; i++) {
262 if (bin[i]) {
263 return (std::numeric_limits<uint64_t>::max());
264 }
265 }
266
267 // Ok, we're good. The pool is sanely sized. It may be huge, but at least
268 // that's something we can represent on uint64_t.
269 uint64_t numeric = 0;
270 for (int i = 8; i < 16; i++) {
271 numeric <<= 8;
272 numeric += bin[i];
273 }
274
275 return (numeric);
276 }
277}
278
279int
281 const isc::asiolink::IOAddress& max) {
282 if (min.getFamily() != max.getFamily()) {
283 isc_throw(BadValue, "Both addresses have to be the same family");
284 }
285
286 if (max < min) {
287 isc_throw(BadValue, min.toText() << " must not be greater than "
288 << max.toText());
289 }
290
291 if (min.isV4()) {
292 // Get addresses as integers
293 uint32_t max_numeric = max.toUint32();
294 uint32_t min_numeric = min.toUint32();
295
296 // Get the exclusive or which must be one of the bit masks
297 uint32_t xor_numeric = max_numeric ^ min_numeric;
298 for (uint8_t prefix_len = 0; prefix_len <= 32; ++prefix_len) {
299 if (xor_numeric == bitMask4[prefix_len]) {
300 // Got it: the wanted value is also the index
301 return (static_cast<int>(prefix_len));
302 }
303 }
304
305 // If it was not found the range is not from a prefix / prefix_len
306 return (-1);
307 } else {
308 // Get addresses as 16 bytes
309 uint8_t min_packed[V6ADDRESS_LEN];
310 memcpy(min_packed, &min.toBytes()[0], 16);
311 uint8_t max_packed[V6ADDRESS_LEN];
312 memcpy(max_packed, &max.toBytes()[0], 16);
313
314 // Scan the exclusive or of addresses to find a difference
315 int candidate = 128;
316 bool zeroes = true;
317 for (uint8_t i = 0; i < 16; ++i) {
318 uint8_t xor_byte = min_packed[i] ^ max_packed[i];
319 if (zeroes) {
320 // Skipping zero bits searching for one bits
321 if (xor_byte == 0) {
322 continue;
323 }
324 // Found a one bit: note the fact
325 zeroes = false;
326 // Compare the exclusive or to masks
327 for (uint8_t j = 0; j < 8; ++j) {
328 if (xor_byte == revMask6[j]) {
329 // Got it the prefix length: note it
330 candidate = static_cast<int>((i * 8) + j);
331 }
332 }
333 if (candidate == 128) {
334 // Not found? The range is not from a prefix / prefix_len
335 return (-1);
336 }
337 } else {
338 // Checking that trailing bits are on bits
339 if (xor_byte == 0xff) {
340 continue;
341 }
342 // Not all ones is bad
343 return (-1);
344 }
345 }
346 return (candidate);
347 }
348}
349
350uint64_t prefixesInRange(const uint8_t pool_len, const uint8_t delegated_len) {
351 if (delegated_len < pool_len) {
352 return (0);
353 }
354
355 uint64_t count = delegated_len - pool_len;
356
357 if (count == 0) {
358 // If we want to delegate /64 out of /64 pool, we have only
359 // one prefix.
360 return (1);
361 } else if (count >= 64) {
362 // If the difference is greater than or equal 64, e.g. we want to
363 // delegate /96 out of /16 pool, the number is bigger than we can
364 // express, so we'll stick with maximum value of uint64_t.
365 return (std::numeric_limits<uint64_t>::max());
366 } else {
367 // Now count specifies the exponent (e.g. if the difference between the
368 // delegated and pool length is 4, we have 16 prefixes), so we need
369 // to calculate 2^(count - 1)
370 return ((static_cast<uint64_t>(2)) << (count - 1));
371 }
372}
373
374};
375};
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
uint64_t addrsInRange(const isc::asiolink::IOAddress &min, const isc::asiolink::IOAddress &max)
Returns number of available addresses in the specified range (min - max).
uint64_t prefixesInRange(const uint8_t pool_len, const uint8_t delegated_len)
Returns number of available IPv6 prefixes in the specified prefix.
isc::asiolink::IOAddress firstAddrInPrefix(const isc::asiolink::IOAddress &prefix, uint8_t len)
This code is based on similar code from the Dibbler project.
isc::asiolink::IOAddress getNetmask4(uint8_t len)
Generates an IPv4 netmask of specified length.
int prefixLengthFromRange(const isc::asiolink::IOAddress &min, const isc::asiolink::IOAddress &max)
Returns prefix length from the specified range (min - max).
isc::asiolink::IOAddress lastAddrInPrefix(const isc::asiolink::IOAddress &prefix, uint8_t len)
returns a last address in a given prefix
Defines the logger used by the top-level component of kea-dhcp-ddns.