ip_check.h 15.5 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Copyright (C) 2011  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.

#ifndef __IP_CHECK_H
#define __IP_CHECK_H

18
#include <algorithm>
Stephen Morris's avatar
Stephen Morris committed
19
#include <functional>
20
#include <iterator>
Stephen Morris's avatar
Stephen Morris committed
21
#include <utility>
22
23
#include <vector>

Stephen Morris's avatar
Stephen Morris committed
24
#include <boost/lexical_cast.hpp>
25
#include <boost/static_assert.hpp>
26
#include <boost/scoped_ptr.hpp>
Stephen Morris's avatar
Stephen Morris committed
27

28
29
30
31
32
33
34
35
36
37
#include <stdint.h>
#include <arpa/inet.h>
#include <netinet/in.h>

#include <acl/check.h>
#include <exceptions/exceptions.h>

namespace isc {
namespace acl {

38
39
40
41
42
// Free functions.  These are not supposed to be used outside this module,
// but are declared public for testing.  To try to conceal them, they are
// put in an "internal" namespace.

namespace internal {
Stephen Morris's avatar
Stephen Morris committed
43

44
/// \brief Convert prefix length to mask
45
///
46
47
/// Given a prefix length and a data type, return a value of that data type
/// with the most significant "prefix length" bits set.  For example, if the
48
/// data type is an uint8_t and the prefix length is 3, the function would
49
50
/// return a uint8_t holding the binary value 11100000.  This value is used as
/// a mask in the address checks.
51
///
Stephen Morris's avatar
Stephen Morris committed
52
/// The function is templated on the data type of the mask.
53
///
54
55
/// \param prefixlen number of bits to be set in the mask.  This must be
///        between 0 and 8*sizeof(T).
56
///
57
58
59
/// \return Value with the most significant "prefixlen" bits set.
///
/// \exception OutOfRange prefixlen is too large for the data type.
60
61

template <typename T>
62
T createMask(size_t prefixlen) {
Stephen Morris's avatar
Stephen Morris committed
63

64
    if (prefixlen == 0) {
Stephen Morris's avatar
Stephen Morris committed
65
66
        return (0);

67
    } else if (prefixlen <= 8 * sizeof(T)) {
68

Stephen Morris's avatar
Stephen Morris committed
69
        // In the following discussion:
70
        //
Stephen Morris's avatar
Stephen Morris committed
71
        // w is the width of the data type T in bits.
72
        // m is the value of prefixlen, the number of most signifcant bits we
Stephen Morris's avatar
Stephen Morris committed
73
        // want to set.
Stephen Morris's avatar
Stephen Morris committed
74
        // ** is exponentiation (i.e. 2**n is 2 raised to the power of n).
75
        //
Stephen Morris's avatar
Stephen Morris committed
76
77
        // We note that the value of 2**m - 1 gives a value with the least
        // significant m bits set.  For a data type of width w, this means that
78
        // the most signficant (w-m) bits are clear.
79
        //
Stephen Morris's avatar
Stephen Morris committed
80
        // Hence the value 2**(w-m) - 1 gives a result with the least signficant
Stephen Morris's avatar
Stephen Morris committed
81
82
        // w-m bits set and the most significant m bits clear.  The 1's
        // complement of this value gives is the result we want.
83
        //
84
        // Final note: at this point in the logic, m is non-zero, so w-m < w.
Stephen Morris's avatar
Stephen Morris committed
85
86
87
        // This means 1<<(w-m) will fit into a variable of width w bits.  In
        // other words, in the expression below, no term will cause an integer
        // overflow.
88
        return (~((1 << (8 * sizeof(T) - prefixlen)) - 1));
Stephen Morris's avatar
Stephen Morris committed
89
90
    }

91
    // Mask size is too large. (Note that prefixlen is unsigned, so can't be
Stephen Morris's avatar
Stephen Morris committed
92
    // negative.)
93
    isc_throw(isc::OutOfRange, "prefixlen argument must be between 0 and " <<
Stephen Morris's avatar
Stephen Morris committed
94
95
96
                               8 * sizeof(T));
}

97
/// \brief Split IP Address Prefix
Stephen Morris's avatar
Stephen Morris committed
98
///
99
100
101
/// Splits an IP address prefix (given in the form of "xxxxxx/n" or "xxxxx" into
/// a string representing the IP address and a number giving the length of the
/// prefix. (In the latter case, the prefix is equal in length to the width in
102
103
/// width in bits of the data type holding the address.) An exception will be
/// thrown if the string format is invalid or if the prefix length is invalid.
Stephen Morris's avatar
Stephen Morris committed
104
///
Stephen Morris's avatar
Stephen Morris committed
105
106
/// N.B. This function does NOT check that the address component is a valid IP
/// address; this is done elsewhere in the address parsing process.
Stephen Morris's avatar
Stephen Morris committed
107
///
108
/// \param ipprefix Address or address prefix.  The string should be passed
Stephen Morris's avatar
Stephen Morris committed
109
110
///                 without leading or trailing spaces.
///
111
112
/// \return Pair of (string, int) holding the address string and the prefix
///         length.  The second element is -1 if no prefix was given.
113
///
114
/// \exception InvalidParameter Address prefix not of the expected syntax
Stephen Morris's avatar
Stephen Morris committed
115

116
std::pair<std::string, int>
117
splitIPAddress(const std::string& ipprefix);
118

119
120
} // namespace internal

Stephen Morris's avatar
Stephen Morris committed
121

122

123
/// \brief IP Check
124
///
125
/// This class performs a match between an IP address prefix specified in an ACL
126
/// and a given IP address.  The check works for both IPv4 and IPv6 addresses.
127
///
128
129
130
/// The class is templated on the type of a context structure passed to the
/// matches() method, and a template specialisation for that method must be
/// supplied for the class to be used.
131

Stephen Morris's avatar
Stephen Morris committed
132
template <typename Context>
133
134
class IPCheck : public Check<Context> {
private:
135
136
137
    // Size of uint8_t array to holds different address types
    static const size_t IPV6_SIZE = sizeof(struct in6_addr);
    static const size_t IPV4_SIZE = sizeof(struct in_addr);
138

139
public:
140
    /// \brief IPv4 Constructor
Stephen Morris's avatar
Stephen Morris committed
141
    ///
142
    /// Constructs an IPCheck object from a network address given as a
143
    /// 32-bit value in network byte order and a prefix length.
144
    ///
145
    /// \param address IP address to check for (as an address in host-byte
146
    ///        order).  Note host-byte order - this is different to the IPv6
147
    ///        constructor.
148
149
    /// \param prefixlen The prefix length specified as an integer between 0
    ///        and 32. This determines the number of bits of the address to
150
    ///        check. (A value of zero imples match all IPv4 addresses.)
151
    IPCheck(uint32_t address, int prefixlen = 8 * IPV4_SIZE) :
152
            address_(IPV4_SIZE), mask_(), family_(AF_INET)
153
    {
154
155
        // The address is stored in network-byte order, so the MS byte should
        // be stored at the lowest address in the array.
156
157
158
159
160
        address_[3] = static_cast<uint8_t>((address      ) & 0xff);
        address_[2] = static_cast<uint8_t>((address >>  8) & 0xff);
        address_[1] = static_cast<uint8_t>((address >> 16) & 0xff);
        address_[0] = static_cast<uint8_t>((address >> 24) & 0xff);

161
        setMask(prefixlen);
162
163
    }

164
    /// \brief IPv6 Constructor
Stephen Morris's avatar
Stephen Morris committed
165
    ///
166
    /// Constructs an IPv6 Check object from a network address given as a
167
    /// 16-byte array in network-byte order and a prefix length.
Stephen Morris's avatar
Stephen Morris committed
168
169
170
    ///
    /// \param address IP address to check for (as an address in network-byte
    ///        order).
171
172
173
    /// \param prefixlen The prefix length specified as an integer between 0
    ///        and 128.  This determines the number of bits of the address to
    ///        check.
174
    IPCheck(const uint8_t* address, int prefixlen = 8 * IPV6_SIZE) :
175
            address_(address, address + IPV6_SIZE), mask_(), family_(AF_INET6)
Stephen Morris's avatar
Stephen Morris committed
176
    {
177
        setMask(prefixlen);
Stephen Morris's avatar
Stephen Morris committed
178
179
180
181
    }

    /// \brief String Constructor
    ///
182
183
    /// Constructs an IP Check object from an address or address prefix in the
    /// form <ip-address>/n".
184
    ///
185
    /// Also allowed are the special keywords "any4" and "any6", which match
186
    /// any IPv4 or IPv6 address.  These must be specified exactly as-is
187
    /// (i.e. lowercase, with no leading or trailing spaces).
Stephen Morris's avatar
Stephen Morris committed
188
    ///
189
    /// \param ipprefix IP address prefix in the form "<ip-address>/n"
190
    ///        (where the "/n" part is optional and should be valid for the
191
    ///        address).  If "n" is specified as zero, the match is for any
192
193
    ///        address in that address family.  The address can also be
    ///        given as "any4" or "any6".
194
    IPCheck(const std::string& ipprefix) : address_(), mask_(), family_(0) {
195
        // Check for special cases first.
196
        if (ipprefix == "any4") {
197
            family_ = AF_INET;
Stephen Morris's avatar
Stephen Morris committed
198

199
        } else if (ipprefix == "any6") {
200
            family_ = AF_INET6;
201

202
203
204
205
        } else {

            // General address prefix.  Split into address part and prefix
            // length.
206
            const std::pair<std::string, int> result =
207
                internal::splitIPAddress(ipprefix);
208
209
210
211

            // Try to convert the address.  If successful, the result is in
            // network-byte order (most significant components at lower
            // addresses).
212
213
            BOOST_STATIC_ASSERT(IPV6_SIZE > IPV4_SIZE);
            uint8_t address_bytes[IPV6_SIZE];
214
            int status = inet_pton(AF_INET6, result.first.c_str(),
215
216
                                   address_bytes);
            if (status == 1) {
217
                // It was an IPv6 address, copy into the address store
218
219
220
221
222
                std::copy(address_bytes, address_bytes + IPV6_SIZE,
                          std::back_inserter(address_));
                family_ = AF_INET6;

            } else {
223
                // Not IPv6, try IPv4
224
                int status = inet_pton(AF_INET, result.first.c_str(),
225
226
227
228
229
230
231
                                       address_bytes);
                if (status == 1) {
                    std::copy(address_bytes, address_bytes + IPV4_SIZE,
                              std::back_inserter(address_));
                    family_ = AF_INET;

                } else {
232
                    isc_throw(isc::InvalidParameter, "address prefix of " <<
233
                              ipprefix << " is a not valid");
234
                }
235
            }
Stephen Morris's avatar
Stephen Morris committed
236

237
            // All done, so set the mask used in address comparison.
238
239
            setMask(result.second);
        }
240
241
    }

Stephen Morris's avatar
Stephen Morris committed
242
    /// \brief Destructor
243
    virtual ~IPCheck() {}
Stephen Morris's avatar
Stephen Morris committed
244
245
246
247

    /// \brief The check itself
    ///
    /// Matches the passed argument to the condition stored here.  Different
248
    /// specialisations must be provided for different argument types, and the
Stephen Morris's avatar
Stephen Morris committed
249
250
    /// program will fail to compile if a required specialisation is not
    /// provided.
Stephen Morris's avatar
Stephen Morris committed
251
252
    ///
    /// \param context Information to be matched
Stephen Morris's avatar
Stephen Morris committed
253
    virtual bool matches(const Context& context) const;
Stephen Morris's avatar
Stephen Morris committed
254
255
256

    /// \brief Estimated cost
    ///
257
    /// Assume that the cost of the match is linear and depends on the
258
259
260
    /// maximum number of comparison operations.
    ///
    /// \return Estimated cost of the comparison
Stephen Morris's avatar
Stephen Morris committed
261
    virtual unsigned cost() const {
262
        return ((family_ == AF_INET) ? IPV4_SIZE : IPV6_SIZE);
Stephen Morris's avatar
Stephen Morris committed
263
264
265
266
267
268
269
    }

    ///@{
    /// Access methods - mainly for testing

    /// \return Stored IP address
    std::vector<uint8_t> getAddress() const {
270
        return (address_);
Stephen Morris's avatar
Stephen Morris committed
271
272
273
    }

    /// \return Network mask applied to match
274
    std::vector<uint8_t> getMask() const {
275
        return (mask_);
276
277
    }

278
279
    /// \return Prefix length of the match
    size_t getPrefixlen() const {
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
        // Work this out by shifting bits out of the mask
        size_t count = 0;
        for (size_t i = 0; i < mask_.size(); ++i) {
            if (mask_[i] == 0xff) {
                // Full byte, 8 bit set
                count += 8;

            } else if (mask_[i] != 0) {
                // Partial set, count the bits
                uint8_t byte = mask_[i];
                for (int i = 0; i < 8 * sizeof(uint8_t); ++i) {
                    count += byte & 0x01;   // Add one if the bit is set
                    byte >>= 1;             // Go for next bit
                }

                // There won't be any more bits set after this, so exit
                break;
            }
        }
        return (count);
Stephen Morris's avatar
Stephen Morris committed
300
301
    }

302
303
    /// \return Address family
    int getFamily() const {
304
        // Check that a family_  value of 0 does not imply IPv4 or IPv6.
305
306
307
308
309
310
311
        // This avoids confusion if getFamily() is called on an object that
        // has been initialized by default.
        BOOST_STATIC_ASSERT(AF_INET != 0);
        BOOST_STATIC_ASSERT(AF_INET6 != 0);

        return (family_);
    }
Stephen Morris's avatar
Stephen Morris committed
312
313
    ///@}

314
private:
Stephen Morris's avatar
Stephen Morris committed
315
316
317
318
319
320
    /// \brief Comparison
    ///
    /// This is the actual comparison function that checks the IP address passed
    /// to this class with the matching information in the class itself.  It is
    /// expected to be called from matches().
    ///
321
322
    /// \param testaddr Address (in network byte order) to test against the
    ///                 check condition in the class.  This is expected to
323
324
    ///                 be IPV6_SIZE or IPV4_SIZE bytes long.
    /// \param family   Address family of testaddr.
Stephen Morris's avatar
Stephen Morris committed
325
326
    ///
    /// \return true if the address matches, false if it does not.
327
328
329
330
    virtual bool compare(const uint8_t* testaddr, int family) const {

        if (family != family_) {
            // Can't match if the address is of the wrong family
331
            return (false);
332
333
        }

334
        // Simple check failed, so have to do a complete match.  To check that
335
336
337
338
339
340
341
342
343
344
        // the address given matches the stored network address and mask, we
        // check the simple condition that:
        //
        //     address_given & mask_ == stored_address & mask_
        //
        // The result is checked for all bytes for which there are bits set in
        // the mask.  We stop at the first non-match (or when we run out of bits
        // in the mask). (Note that the mask represents a contiguous set of
        // bits.  As such, as soon as we find a mask byte of zeroes, we have run
        // past the part of the address where we need to match.
345
346
        //
        // Note that if the passed address was any4 or any6, we rely on the
347
348
        // fact that the size of address_ is zero - the loop will terminate
        // before the first iteration.
349
350
351
352
353
354
355

        bool match = true;
        for (int i = 0; match && (i < address_.size()) &&
                       (mask_[i] != 0); ++i) {
             match = ((testaddr[i] & mask_[i]) == (address_[i] & mask_[i]));
        }
        return (match);
356
357
358
    }


359
    /// \brief Set Mask
Stephen Morris's avatar
Stephen Morris committed
360
    ///
361
362
    /// Sets up the mask from the prefix length.  This involves setting
    /// an individual mask in each byte of the mask array.
363
    ///
364
    /// The actual allowed value of the prefix length depends on the address
365
366
    /// family.
    ///
367
368
369
370
371
    /// \param requested Requested prefix length size.  If negative, the
    ///        maximum for the address family is assumed.  (A negative value
    ///        will arise if the string constructor was used and no mask size
    ///        was given.)
    void setMask(int requested) {
372

373
374
375
376
377
        mask_.clear();
        mask_.resize((family_ == AF_INET) ? IPV4_SIZE : IPV6_SIZE);

        // Set the maximum number of bits allowed in the mask.
        int maxmask = 8 * (mask_.size());
378
379
380
        if (requested < 0) {
            requested = maxmask;
        }
Stephen Morris's avatar
Stephen Morris committed
381
382

        // Validate that the mask is valid.
383
        if (requested <= maxmask) {
Stephen Morris's avatar
Stephen Morris committed
384

385
            // Loop, setting the bits in the set of mask bytes until all the
386
            // specified bits have been used up.  As both IPv4 and IPv6
387
            // addresses are stored in network-byte order, this works in
388
            // both cases.
389
            size_t bits_left = requested;   // Bits remaining to set
Stephen Morris's avatar
Stephen Morris committed
390
391
392
            int i = -1;
            while (bits_left > 0) {
                if (bits_left >= 8) {
393
                    mask_[++i] = ~0;  // All bits set
Stephen Morris's avatar
Stephen Morris committed
394
395
396
                    bits_left -= 8;

                } else if (bits_left > 0) {
397
                    mask_[++i] = internal::createMask<uint8_t>(bits_left);
Stephen Morris's avatar
Stephen Morris committed
398
399
400
401
402
403
                    bits_left = 0;

                }
            }
        } else {
            isc_throw(isc::OutOfRange,
404
                      "mask size of " << requested << " is invalid " <<
405
                      "for the given address");
Stephen Morris's avatar
Stephen Morris committed
406
407
408
        }
    }

Stephen Morris's avatar
Stephen Morris committed
409
410
    // Member variables

411
412
413
    std::vector<uint8_t> address_;  ///< Address in binary form
    std::vector<uint8_t> mask_;     ///< Address mask
    int         family_;            ///< Address family
414
415
};

416
417
418
419
} // namespace acl
} // namespace isc

#endif // __IP_CHECK_H