addr_utilities.cc 7.37 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14
// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.

15
#include <dhcpsrv/addr_utilities.h>
16
#include <exceptions/exceptions.h>
17 18

#include <string.h>
19

20
using namespace isc;
Tomek Mrugalski's avatar
Tomek Mrugalski committed
21 22
using namespace isc::asiolink;

Tomek Mrugalski's avatar
Tomek Mrugalski committed
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
namespace {

/// @brief mask used for first/last address calculation in a IPv4 prefix
///
/// Using a static mask is faster than calculating it dynamically every time.
const uint32_t bitMask4[] = { 0xffffffff, 0x7fffffff, 0x3fffffff, 0x1fffffff,
                              0x0fffffff, 0x07ffffff, 0x03ffffff, 0x01ffffff,
                              0x00ffffff, 0x007fffff, 0x003fffff, 0x001fffff,
                              0x000fffff, 0x0007ffff, 0x0003ffff, 0x0001ffff,
                              0x0000ffff, 0x00007fff, 0x00003fff, 0x00001fff,
                              0x00000fff, 0x000007ff, 0x000003ff, 0x000001ff,
                              0x000000ff, 0x0000007f, 0x0000003f, 0x0000001f,
                              0x0000000f, 0x00000007, 0x00000003, 0x00000001,
                              0x00000000 };

/// @brief mask used for first/last address calculation in a IPv6 prefix
const uint8_t bitMask6[]= { 0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff };
40

41 42 43 44 45 46 47
/// @brief calculates the first IPv6 address in a IPv6 prefix
///
/// Note: This is a private function. Do not use it directly.
/// Please use firstAddrInPrefix() instead.
///
/// @param prefix IPv6 prefix
/// @param len prefix length
48
isc::asiolink::IOAddress firstAddrInPrefix6(const isc::asiolink::IOAddress& prefix,
Tomek Mrugalski's avatar
Tomek Mrugalski committed
49
                                            uint8_t len) {
50
    if (len > 128) {
51 52
        isc_throw(isc::BadValue,
                  "Too large netmask. 0..128 is allowed in IPv6");
53 54
    }

Tomek Mrugalski's avatar
Tomek Mrugalski committed
55
    // First we copy the whole address as 16 bytes.
56 57 58
    // We don't check that it is a valid IPv6 address and thus has
    // the required length because it is already checked by
    // the calling function.
59
    uint8_t packed[V6ADDRESS_LEN];
60
    memcpy(packed, &prefix.toBytes()[0], V6ADDRESS_LEN);
61

Tomek Mrugalski's avatar
Tomek Mrugalski committed
62 63 64
    // If the length is divisible by 8, it is simple. We just zero out the host
    // part. Otherwise we need to handle the byte that has to be partially
    // zeroed.
65
    if (len % 8 != 0) {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
66 67 68

        // Get the appropriate mask. It has relevant bits (those that should
        // stay) set and irrelevant (those that should be wiped) cleared.
Tomek Mrugalski's avatar
Tomek Mrugalski committed
69
        uint8_t mask = bitMask6[len % 8];
Tomek Mrugalski's avatar
Tomek Mrugalski committed
70 71

        // Let's leave only whatever the mask says should not be cleared.
72
        packed[len / 8] = packed[len / 8] & mask;
Tomek Mrugalski's avatar
Tomek Mrugalski committed
73 74 75 76

        // Since we have just dealt with this byte, let's move the prefix length
        // to the beginning of the next byte (len is expressed in bits).
        len = (len / 8 + 1) * 8;
77
    }
Tomek Mrugalski's avatar
Tomek Mrugalski committed
78 79 80

    // Clear out the remaining bits.
    for (int i = len / 8; i < sizeof(packed); ++i) {
81 82 83
        packed[i] = 0x0;
    }

Tomek Mrugalski's avatar
Tomek Mrugalski committed
84
    // Finally, let's wrap this into nice and easy IOAddress object.
85
    return (isc::asiolink::IOAddress::fromBytes(AF_INET6, packed));
86 87
}

88
/// @brief calculates the first IPv4 address in a IPv4 prefix
Tomek Mrugalski's avatar
Tomek Mrugalski committed
89
///
90 91 92 93
/// Note: This is a private function. Do not use it directly.
/// Please use firstAddrInPrefix() instead.
///
/// @param prefix IPv4 prefix
Tomek Mrugalski's avatar
Tomek Mrugalski committed
94
/// @param len netmask length (0-32)
95 96
isc::asiolink::IOAddress firstAddrInPrefix4(const isc::asiolink::IOAddress& prefix,
                                            uint8_t len) {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
97
    if (len > 32) {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
98
        isc_throw(isc::BadValue, "Too large netmask. 0..32 is allowed in IPv4");
99 100
    }

101 102 103
    // We don't check that it is a valid IPv4 address and thus has
    // a required length of 4 bytes because it has been already
    // checked by the calling function.
104
    uint32_t addr = prefix;
Tomek Mrugalski's avatar
Tomek Mrugalski committed
105
    return (IOAddress(addr & (~bitMask4[len])));
106 107
}

108 109 110 111 112 113 114
/// @brief calculates the last IPv4 address in a IPv4 prefix
///
/// Note: This is a private function. Do not use it directly.
/// Please use firstAddrInPrefix() instead.
///
/// @param prefix IPv4 prefix that we calculate first address for
/// @param len netmask length (0-32)
115 116
isc::asiolink::IOAddress lastAddrInPrefix4(const isc::asiolink::IOAddress& prefix,
                                           uint8_t len) {
117
    if (len > 32) {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
118
        isc_throw(isc::BadValue, "Too large netmask. 0..32 is allowed in IPv4");
119 120
    }

121
    uint32_t addr = prefix;
Tomek Mrugalski's avatar
Tomek Mrugalski committed
122
    return (IOAddress(addr | bitMask4[len]));
123 124
}

125 126 127 128 129 130 131
/// @brief calculates the last IPv6 address in a IPv6 prefix
///
/// Note: This is a private function. Do not use it directly.
/// Please use lastAddrInPrefix() instead.
///
/// @param prefix IPv6 prefix that we calculate first address for
/// @param len netmask length (0-128)
132
isc::asiolink::IOAddress lastAddrInPrefix6(const isc::asiolink::IOAddress& prefix,
Tomek Mrugalski's avatar
Tomek Mrugalski committed
133
                                           uint8_t len) {
134
    if (len > 128) {
135 136
        isc_throw(isc::BadValue,
                  "Too large netmask. 0..128 is allowed in IPv6");
137
    }
138

Tomek Mrugalski's avatar
Tomek Mrugalski committed
139
    // First we copy the whole address as 16 bytes.
140
    uint8_t packed[V6ADDRESS_LEN];
141
    memcpy(packed, &prefix.toBytes()[0], 16);
142

Tomek Mrugalski's avatar
Tomek Mrugalski committed
143 144 145
    // if the length is divisible by 8, it is simple. We just fill the host part
    // with ones. Otherwise we need to handle the byte that has to be partially
    // zeroed.
146
    if (len % 8 != 0) {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
147 148
        // Get the appropriate mask. It has relevant bits (those that should
        // stay) set and irrelevant (those that should be set to 1) cleared.
Tomek Mrugalski's avatar
Tomek Mrugalski committed
149
        uint8_t mask = bitMask6[len % 8];
Tomek Mrugalski's avatar
Tomek Mrugalski committed
150 151

        // Let's set those irrelevant bits with 1. It would be perhaps
Tomek Mrugalski's avatar
Tomek Mrugalski committed
152
        // easier to not use negation here and invert bitMask6 content. However,
Tomek Mrugalski's avatar
Tomek Mrugalski committed
153 154
        // with this approach, we can use the same mask in first and last
        // address calculations.
155
        packed[len / 8] = packed[len / 8] | ~mask;
Tomek Mrugalski's avatar
Tomek Mrugalski committed
156 157 158 159

        // Since we have just dealt with this byte, let's move the prefix length
        // to the beginning of the next byte (len is expressed in bits).
        len = (len / 8 + 1) * 8;
160
    }
Tomek Mrugalski's avatar
Tomek Mrugalski committed
161 162 163

    // Finally set remaining bits to 1.
    for (int i = len / 8; i < sizeof(packed); ++i) {
164 165 166
        packed[i] = 0xff;
    }

Tomek Mrugalski's avatar
Tomek Mrugalski committed
167
    // Finally, let's wrap this into nice and easy IOAddress object.
168
    return (isc::asiolink::IOAddress::fromBytes(AF_INET6, packed));
169 170
}

Tomek Mrugalski's avatar
Tomek Mrugalski committed
171 172 173 174 175 176
}; // end of anonymous namespace

namespace isc {
namespace dhcp {

isc::asiolink::IOAddress firstAddrInPrefix(const isc::asiolink::IOAddress& prefix,
177
                                           uint8_t len) {
178 179 180
    if (prefix.isV4()) {
        return (firstAddrInPrefix4(prefix, len));

Tomek Mrugalski's avatar
Tomek Mrugalski committed
181
    } else {
182 183
        return (firstAddrInPrefix6(prefix, len));

Tomek Mrugalski's avatar
Tomek Mrugalski committed
184 185 186
    }
}

187 188
isc::asiolink::IOAddress lastAddrInPrefix(const isc::asiolink::IOAddress& prefix,
                                           uint8_t len) {
189 190 191
    if (prefix.isV4()) {
        return (lastAddrInPrefix4(prefix, len));

192
    } else {
193 194
        return (lastAddrInPrefix6(prefix, len));

195 196 197
    }
}

198 199
};
};