libdhcp++.cc 28.1 KB
Newer Older
Tomek Mrugalski's avatar
Tomek Mrugalski committed
1
// Copyright (C) 2011-2013 Internet Systems Consortium, Inc. ("ISC")
2
3
4
5
6
7
8
9
10
11
12
13
14
//
// 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
16
#include <config.h>

17
#include <dhcp/dhcp4.h>
18
#include <dhcp/dhcp6.h>
19
#include <dhcp/libdhcp++.h>
20
#include <dhcp/option.h>
21
#include <dhcp/option_vendor.h>
22
23
#include <dhcp/option6_ia.h>
#include <dhcp/option6_iaaddr.h>
24
#include <dhcp/option_definition.h>
25
#include <dhcp/option_int_array.h>
26
#include <dhcp/std_option_defs.h>
27
#include <dhcp/docsis3_option_defs.h>
28
29
#include <exceptions/exceptions.h>
#include <util/buffer.h>
30
#include <dhcp/option_definition.h>
31
32
33

#include <boost/shared_array.hpp>
#include <boost/shared_ptr.hpp>
34

35
using namespace std;
36
using namespace isc::dhcp;
37
using namespace isc::util;
38

39
40
41
// static array with factories for options
std::map<unsigned short, Option::Factory*> LibDHCP::v4factories_;

42
// static array with factories for options
43
44
std::map<unsigned short, Option::Factory*> LibDHCP::v6factories_;

45
46
47
48
49
50
// Static container with DHCPv4 option definitions.
OptionDefContainer LibDHCP::v4option_defs_;

// Static container with DHCPv6 option definitions.
OptionDefContainer LibDHCP::v6option_defs_;

51
52
53
54
VendorOptionDefContainers LibDHCP::vendor4_defs_;

VendorOptionDefContainers LibDHCP::vendor6_defs_;

Tomek Mrugalski's avatar
Tomek Mrugalski committed
55
56
57
58
59
60
61
62
// Those two vendor classes are used for cable modems:

/// DOCSIS3.0 compatible cable modem
const char* isc::dhcp::DOCSIS3_CLASS_MODEM = "docsis3.0";

/// DOCSIS3.0 cable modem that has router built-in
const char* isc::dhcp::DOCSIS3_CLASS_EROUTER = "eRouter1.0";

63
64
65
66
67
68
// Let's keep it in .cc file. Moving it to .h would require including optionDefParams
// definitions there
void initOptionSpace(OptionDefContainer& defs,
                     const OptionDefParams* params,
                     size_t params_size);

69
const OptionDefContainer&
70
LibDHCP::getOptionDefs(const Option::Universe u) {
71
72
    switch (u) {
    case Option::V4:
73
74
        if (v4option_defs_.empty()) {
            initStdOptionDefs4();
75
            initVendorOptsDocsis4();
76
        }
77
78
        return (v4option_defs_);
    case Option::V6:
79
        if (v6option_defs_.empty()) {
80
            initStdOptionDefs6();
81
            initVendorOptsDocsis6();
82
        }
83
84
85
86
87
        return (v6option_defs_);
    default:
        isc_throw(isc::BadValue, "invalid universe " << u << " specified");
    }
}
88

89
const OptionDefContainer*
90
LibDHCP::getVendorOption4Defs(const uint32_t vendor_id) {
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105

    if (vendor_id == VENDOR_ID_CABLE_LABS &&
        vendor4_defs_.find(VENDOR_ID_CABLE_LABS) == vendor4_defs_.end()) {
        initVendorOptsDocsis4();
    }

    VendorOptionDefContainers::const_iterator def = vendor4_defs_.find(vendor_id);
    if (def == vendor4_defs_.end()) {
        // No such vendor-id space
        return (NULL);
    }
    return (&(def->second));
}

const OptionDefContainer*
106
LibDHCP::getVendorOption6Defs(const uint32_t vendor_id) {
107
108
109
110
111
112
113
114
115
116
117
118
119
120

    if (vendor_id == VENDOR_ID_CABLE_LABS &&
        vendor6_defs_.find(VENDOR_ID_CABLE_LABS) == vendor6_defs_.end()) {
        initVendorOptsDocsis6();
    }

    VendorOptionDefContainers::const_iterator def = vendor6_defs_.find(vendor_id);
    if (def == vendor6_defs_.end()) {
        // No such vendor-id space
        return (NULL);
    }
    return (&(def->second));
}

121
122
123
124
125
126
127
128
129
130
131
OptionDefinitionPtr
LibDHCP::getOptionDef(const Option::Universe u, const uint16_t code) {
    const OptionDefContainer& defs = getOptionDefs(u);
    const OptionDefContainerTypeIndex& idx = defs.get<1>();
    const OptionDefContainerTypeRange& range = idx.equal_range(code);
    if (range.first != range.second) {
        return (*range.first);
    }
    return (OptionDefinitionPtr());
}

132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
OptionDefinitionPtr
LibDHCP::getVendorOptionDef(const Option::Universe u, const uint32_t vendor_id,
                            const uint16_t code) {
    const OptionDefContainer* defs = NULL;
    if (u == Option::V4) {
        defs = getVendorOption4Defs(vendor_id);
    } else if (u == Option::V6) {
        defs = getVendorOption6Defs(vendor_id);
    }

    if (!defs) {
        // Weird universe or unknown vendor_id. We don't care. No definitions
        // one way or another
        // What is it anyway?
        return (OptionDefinitionPtr());
    }

    const OptionDefContainerTypeIndex& idx = defs->get<1>();
    const OptionDefContainerTypeRange& range = idx.equal_range(code);
    if (range.first != range.second) {
        return (*range.first);
    }
    return (OptionDefinitionPtr());
}

157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
bool
LibDHCP::isStandardOption(const Option::Universe u, const uint16_t code) {
    if (u == Option::V6) {
        if (code < 79 &&
            code != 10 &&
            code != 35) {
            return (true);
        }

    } else if (u == Option::V4) {
        if (!(code == 84 ||
              code == 96 ||
              (code > 101 && code < 112) ||
              code == 115 ||
              code == 126 ||
              code == 127 ||
              (code > 146 && code < 150) ||
174
              (code > 177 && code < 208) ||
175
              (code > 213 && code <  220) ||
176
              (code > 221 && code < 255))) {
177
178
179
180
181
182
183
184
                return (true);
            }

    }

    return (false);
}

185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
OptionPtr
LibDHCP::optionFactory(Option::Universe u,
                       uint16_t type,
                       const OptionBuffer& buf) {
    FactoryMap::iterator it;
    if (u == Option::V4) {
        it = v4factories_.find(type);
        if (it == v4factories_.end()) {
            isc_throw(BadValue, "factory function not registered "
            "for DHCP v4 option type " << type);
        }
    } else if (u == Option::V6) {
        it = v6factories_.find(type);
        if (it == v6factories_.end()) {
            isc_throw(BadValue, "factory function not registered "
                      "for DHCPv6 option type " << type);
        }
    } else {
        isc_throw(BadValue, "invalid universe specified (expected "
                  "Option::V4 or Option::V6");
    }
206
    return (it->second(u, type, buf));
207
208
209
}


Tomek Mrugalski's avatar
Tomek Mrugalski committed
210
size_t LibDHCP::unpackOptions6(const OptionBuffer& buf,
211
                               const std::string& option_space,
212
                               isc::dhcp::OptionCollection& options,
213
214
                               size_t* relay_msg_offset /* = 0 */,
                               size_t* relay_msg_len /* = 0 */) {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
215
    size_t offset = 0;
216
217
    size_t length = buf.size();

218
219
220
221
222
223
224
225
226
    // Get the list of standard option definitions.
    OptionDefContainer option_defs;
    if (option_space == "dhcp6") {
        option_defs = LibDHCP::getOptionDefs(Option::V6);
    }
    // @todo Once we implement other option spaces we should add else clause
    // here and gather option definitions for them. For now leaving option_defs
    // empty will imply creation of generic Option.

227
228
229
230
231
232
233
    // Get the search index #1. It allows to search for option definitions
    // using option code.
    const OptionDefContainerTypeIndex& idx = option_defs.get<1>();

    // The buffer being read comprises a set of options, each starting with
    // a two-byte type code and a two-byte length field.
    while (offset + 4 <= length) {
234
        uint16_t opt_type = isc::util::readUint16(&buf[offset], 2);
235
        offset += 2;
236

237
        uint16_t opt_len = isc::util::readUint16(&buf[offset], 2);
238
239
        offset += 2;

240
        if (offset + opt_len > length) {
241
            // @todo: consider throwing exception here.
242
243
244
245
246

            // We peeked at the option header of the next option, but discovered
            // that it would end up beyond buffer end, so the option is
            // truncated. Hence we can't parse it. Therefore we revert
            // back by those four bytes (as if we never parsed them).
247
            return (offset - 4);
248
        }
249

250
        if (opt_type == D6O_RELAY_MSG && relay_msg_offset && relay_msg_len) {
251
252
            // remember offset of the beginning of the relay-msg option
            *relay_msg_offset = offset;
253
254
255
256
257
            *relay_msg_len = opt_len;

            // do not create that relay-msg option
            offset += opt_len;
            continue;
258
259
        }

260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
        if (opt_type == D6O_VENDOR_OPTS) {
            if (offset + 4 > length) {
                // Truncated vendor-option. There is expected at least 4 bytes
                // long enterprise-id field
                return (offset);
            }

            // Parse this as vendor option
            OptionPtr vendor_opt(new OptionVendor(Option::V6, buf.begin() + offset,
                                                  buf.begin() + offset + opt_len));
            options.insert(std::make_pair(opt_type, vendor_opt));

            offset += opt_len;
            continue;
        }


277
278
279
280
        // Get all definitions with the particular option code. Note that option
        // code is non-unique within this container however at this point we
        // expect to get one option definition with the particular code. If more
        // are returned we report an error.
281
282
283
        const OptionDefContainerTypeRange& range = idx.equal_range(opt_type);
        // Get the number of returned option definitions for the option code.
        size_t num_defs = distance(range.first, range.second);
284

285
        OptionPtr opt;
286
287
288
289
290
291
292
293
        if (num_defs > 1) {
            // Multiple options of the same code are not supported right now!
            isc_throw(isc::Unexpected, "Internal error: multiple option definitions"
                      " for option type " << opt_type << " returned. Currently it is not"
                      " supported to initialize multiple option definitions"
                      " for the same option code. This will be supported once"
                      " support for option spaces is implemented");
        } else if (num_defs == 0) {
294
            // @todo Don't crash if definition does not exist because only a few
295
296
297
298
            // option definitions are initialized right now. In the future
            // we will initialize definitions for all options and we will
            // remove this elseif. For now, return generic option.
            opt = OptionPtr(new Option(Option::V6, opt_type,
299
300
                                       buf.begin() + offset,
                                       buf.begin() + offset + opt_len));
301
302
303
304
305
306
307
308
        } else {
            // The option definition has been found. Use it to create
            // the option instance from the provided buffer chunk.
            const OptionDefinitionPtr& def = *(range.first);
            assert(def);
            opt = def->optionFactory(Option::V6, opt_type,
                                     buf.begin() + offset,
                                     buf.begin() + offset + opt_len);
309
        }
310
        // add option to options
311
        options.insert(std::make_pair(opt_type, opt));
312
313
314
315
316
317
        offset += opt_len;
    }

    return (offset);
}

Tomek Mrugalski's avatar
Tomek Mrugalski committed
318
size_t LibDHCP::unpackOptions4(const OptionBuffer& buf,
319
                               const std::string& option_space,
320
                               isc::dhcp::OptionCollection& options) {
321
322
    size_t offset = 0;

323
    // Get the list of stdandard option definitions.
324
325
326
327
328
329
330
331
    OptionDefContainer option_defs;
    if (option_space == "dhcp4") {
        option_defs = LibDHCP::getOptionDefs(Option::V4);
    }
    // @todo Once we implement other option spaces we should add else clause
    // here and gather option definitions for them. For now leaving option_defs
    // empty will imply creation of generic Option.

332
333
334
335
336
337
    // Get the search index #1. It allows to search for option definitions
    // using option code.
    const OptionDefContainerTypeIndex& idx = option_defs.get<1>();

    // The buffer being read comprises a set of options, each starting with
    // a one-byte type code and a one-byte length field.
338
    while (offset + 1 <= buf.size()) {
339
        uint8_t opt_type = buf[offset++];
340

Tomek Mrugalski's avatar
Tomek Mrugalski committed
341
        // DHO_END is a special, one octet long option
342
        if (opt_type == DHO_END)
343
            return (offset); // just return. Don't need to add DHO_END option
344

Tomek Mrugalski's avatar
Tomek Mrugalski committed
345
346
347
348
349
        // DHO_PAD is just a padding after DHO_END. Let's continue parsing
        // in case we receive a message without DHO_END.
        if (opt_type == DHO_PAD)
            continue;

350
        if (offset + 1 >= buf.size()) {
351
352
            // opt_type must be cast to integer so as it is not treated as
            // unsigned char value (a number is presented in error message).
353
            isc_throw(OutOfRange, "Attempt to parse truncated option "
354
                      << static_cast<int>(opt_type));
355
356
        }

357
        uint8_t opt_len =  buf[offset++];
358
        if (offset + opt_len > buf.size()) {
359
360
361
362
363
364
365
366
367
368

            // We peeked at the option header of the next option, but discovered
            // that it would end up beyond buffer end, so the option is
            // truncated. Hence we can't parse it. Therefore we revert
            // back by two bytes (as if we never parsed them).
            return (offset - 2);

            // isc_throw(OutOfRange, "Option parse failed. Tried to parse "
            //          << offset + opt_len << " bytes from " << buf.size()
            //          << "-byte long buffer.");
369
370
        }

371
        // Get all definitions with the particular option code. Note that option code
372
373
374
375
376
377
378
        // is non-unique within this container however at this point we expect
        // to get one option definition with the particular code. If more are
        // returned we report an error.
        const OptionDefContainerTypeRange& range = idx.equal_range(opt_type);
        // Get the number of returned option definitions for the option code.
        size_t num_defs = distance(range.first, range.second);

379
        OptionPtr opt;
380
381
382
383
384
385
386
387
388
        if (num_defs > 1) {
            // Multiple options of the same code are not supported right now!
            isc_throw(isc::Unexpected, "Internal error: multiple option definitions"
                      " for option type " << static_cast<int>(opt_type)
                      << " returned. Currently it is not supported to initialize"
                      << " multiple option definitions for the same option code."
                      << " This will be supported once support for option spaces"
                      << " is implemented");
        } else if (num_defs == 0) {
389
            opt = OptionPtr(new Option(Option::V4, opt_type,
390
391
392
393
394
395
396
397
398
399
                                       buf.begin() + offset,
                                       buf.begin() + offset + opt_len));
        } else {
            // The option definition has been found. Use it to create
            // the option instance from the provided buffer chunk.
            const OptionDefinitionPtr& def = *(range.first);
            assert(def);
            opt = def->optionFactory(Option::V4, opt_type,
                                     buf.begin() + offset,
                                     buf.begin() + offset + opt_len);
400
401
        }

402
        options.insert(std::make_pair(opt_type, opt));
403
404
        offset += opt_len;
    }
405
    return (offset);
406
407
}

408
size_t LibDHCP::unpackVendorOptions6(const uint32_t vendor_id,
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
                                     const OptionBuffer& buf,
                                     isc::dhcp::OptionCollection& options) {
    size_t offset = 0;
    size_t length = buf.size();

    // Get the list of option definitions for this particular vendor-id
    const OptionDefContainer* option_defs = LibDHCP::getVendorOption6Defs(vendor_id);

    // Get the search index #1. It allows to search for option definitions
    // using option code. If there's no such vendor-id space, we're out of luck
    // anyway.
    const OptionDefContainerTypeIndex* idx = NULL;
    if (option_defs) {
        idx = &(option_defs->get<1>());
    }

    // The buffer being read comprises a set of options, each starting with
    // a two-byte type code and a two-byte length field.
    while (offset + 4 <= length) {
428
        uint16_t opt_type = isc::util::readUint16(&buf[offset], 2);
429
430
        offset += 2;

431
        uint16_t opt_len = isc::util::readUint16(&buf[offset], 2);
432
433
434
435
        offset += 2;

        if (offset + opt_len > length) {
            // @todo: consider throwing exception here.
436
437
438
439
440
441

            // We peeked at the option header of the next option, but discovered
            // that it would end up beyond buffer end, so the option is
            // truncated. Hence we can't parse it. Therefore we revert
            // back by those four bytes (as if we never parsed them).
            return (offset - 4);
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
        }

        OptionPtr opt;
        opt.reset();

        // If there is a definition for such a vendor option...
        if (idx) {
            // Get all definitions with the particular option code. Note that option
            // code is non-unique within this container however at this point we
            // expect to get one option definition with the particular code. If more
            // are returned we report an error.
            const OptionDefContainerTypeRange& range = idx->equal_range(opt_type);
            // Get the number of returned option definitions for the option code.
            size_t num_defs = distance(range.first, range.second);

            if (num_defs > 1) {
                // Multiple options of the same code are not supported right now!
                isc_throw(isc::Unexpected, "Internal error: multiple option definitions"
                          " for option type " << opt_type << " returned. Currently it is not"
                          " supported to initialize multiple option definitions"
                          " for the same option code. This will be supported once"
                          " support for option spaces is implemented");
            } else if (num_defs == 1) {
                // The option definition has been found. Use it to create
                // the option instance from the provided buffer chunk.
                const OptionDefinitionPtr& def = *(range.first);
                assert(def);
                opt = def->optionFactory(Option::V6, opt_type,
                                         buf.begin() + offset,
                                         buf.begin() + offset + opt_len);
            }
        }

        // This can happen in one of 2 cases:
        // 1. we do not have definitions for that vendor-space
        // 2. we do have definitions, but that particular option was not defined
        if (!opt) {
            opt = OptionPtr(new Option(Option::V6, opt_type,
                                       buf.begin() + offset,
                                       buf.begin() + offset + opt_len));
        }

        // add option to options
        if (opt) {
            options.insert(std::make_pair(opt_type, opt));
        }
        offset += opt_len;
    }

    return (offset);
}

494
size_t LibDHCP::unpackVendorOptions4(const uint32_t vendor_id, const OptionBuffer& buf,
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
                                     isc::dhcp::OptionCollection& options) {
    size_t offset = 0;

    // Get the list of stdandard option definitions.
    const OptionDefContainer* option_defs = LibDHCP::getVendorOption4Defs(vendor_id);
    // Get the search index #1. It allows to search for option definitions
    // using option code.
    const OptionDefContainerTypeIndex* idx = NULL;
    if (option_defs) {
        idx = &(option_defs->get<1>());
    }

    // The buffer being read comprises a set of options, each starting with
    // a one-byte type code and a one-byte length field.
    while (offset + 1 <= buf.size()) {

        // Note that Vendor-Specific info option (RFC3925) has a different option
        // format than Vendor-Spec info for DHCPv6. (there's additional layer of
        // data-length
        uint8_t data_len = buf[offset++];

        if (offset + data_len > buf.size()) {
517
518
519
520
521
522
523
            // The option is truncated.

            // We peeked at the data_len, but discovered that it would end up
            // beyond buffer end, so the data block is truncated. Hence we can't
            // parse it. Therefore we revert back by one byte (as if we never
            // parsed it).
            return (offset - 1);
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
        }

        uint8_t offset_end = offset + data_len;

        // beginning of data-chunk parser
        while (offset + 1 <= offset_end) {
            uint8_t opt_type = buf[offset++];

            // DHO_END is a special, one octet long option
            if (opt_type == DHO_END)
                return (offset); // just return. Don't need to add DHO_END option

            // DHO_PAD is just a padding after DHO_END. Let's continue parsing
            // in case we receive a message without DHO_END.
            if (opt_type == DHO_PAD)
                continue;

            if (offset + 1 >= buf.size()) {
                // opt_type must be cast to integer so as it is not treated as
                // unsigned char value (a number is presented in error message).
544
                isc_throw(OutOfRange, "Attempt to parse truncated vendor option "
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
                          << static_cast<int>(opt_type));
            }

            uint8_t opt_len =  buf[offset++];
            if (offset + opt_len > buf.size()) {
                isc_throw(OutOfRange, "Option parse failed. Tried to parse "
                          << offset + opt_len << " bytes from " << buf.size()
                          << "-byte long buffer.");
            }

            OptionPtr opt;
            opt.reset();

            if (idx) {
                // Get all definitions with the particular option code. Note that option code
                // is non-unique within this container however at this point we expect
                // to get one option definition with the particular code. If more are
                // returned we report an error.
                const OptionDefContainerTypeRange& range = idx->equal_range(opt_type);
564
                // Get the number of returned option definitions for the option code.
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
                size_t num_defs = distance(range.first, range.second);

                if (num_defs > 1) {
                    // Multiple options of the same code are not supported right now!
                    isc_throw(isc::Unexpected, "Internal error: multiple option definitions"
                              " for option type " << static_cast<int>(opt_type)
                              << " returned. Currently it is not supported to initialize"
                              << " multiple option definitions for the same option code."
                              << " This will be supported once support for option spaces"
                              << " is implemented");
                } else if (num_defs == 1) {
                    // The option definition has been found. Use it to create
                    // the option instance from the provided buffer chunk.
                    const OptionDefinitionPtr& def = *(range.first);
                    assert(def);
                    opt = def->optionFactory(Option::V4, opt_type,
                                             buf.begin() + offset,
                                             buf.begin() + offset + opt_len);
                }
            }

            if (!opt) {
                opt = OptionPtr(new Option(Option::V4, opt_type,
                                           buf.begin() + offset,
                                           buf.begin() + offset + opt_len));
            }

            options.insert(std::make_pair(opt_type, opt));
            offset += opt_len;

        } // end of data-chunk

    }
    return (offset);
}



603
604
void
LibDHCP::packOptions(isc::util::OutputBuffer& buf,
605
606
                     const OptionCollection& options) {
    for (OptionCollection::const_iterator it = options.begin();
607
         it != options.end(); ++it) {
608
        it->second->pack(buf);
609
610
611
    }
}

612
613
void LibDHCP::OptionFactoryRegister(Option::Universe u,
                                    uint16_t opt_type,
614
                                    Option::Factory* factory) {
615
616
    switch (u) {
    case Option::V6: {
617
        if (v6factories_.find(opt_type) != v6factories_.end()) {
618
619
620
621
            isc_throw(BadValue, "There is already DHCPv6 factory registered "
                     << "for option type "  << opt_type);
        }
        v6factories_[opt_type]=factory;
622
        return;
623
624
    }
    case Option::V4:
625
    {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
626
627
        // Option 0 is special (a one octet-long, equal 0) PAD option. It is never
        // instantiated as an Option object, but rather consumed during packet parsing.
628
629
630
        if (opt_type == 0) {
            isc_throw(BadValue, "Cannot redefine PAD option (code=0)");
        }
Tomek Mrugalski's avatar
Tomek Mrugalski committed
631
        // Option 255 is never instantiated as an option object. It is special
632
633
        // (a one-octet equal 255) option that is added at the end of all options
        // during packet assembly. It is also silently consumed during packet parsing.
634
635
636
637
638
639
640
641
642
        if (opt_type > 254) {
            isc_throw(BadValue, "Too big option type for DHCPv4, only 0-254 allowed.");
        }
        if (v4factories_.find(opt_type)!=v4factories_.end()) {
            isc_throw(BadValue, "There is already DHCPv4 factory registered "
                     << "for option type "  << opt_type);
        }
        v4factories_[opt_type]=factory;
        return;
643
    }
644
645
    default:
        isc_throw(BadValue, "Invalid universe type specified.");
646
647
    }

648
    return;
649
}
650

651
652
void
LibDHCP::initStdOptionDefs4() {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
653
    initOptionSpace(v4option_defs_, OPTION_DEF_PARAMS4, OPTION_DEF_PARAMS_SIZE4);
654
}
655

656
657
void
LibDHCP::initStdOptionDefs6() {
658
659
    initOptionSpace(v6option_defs_, OPTION_DEF_PARAMS6, OPTION_DEF_PARAMS_SIZE6);
}
660

661
662
663
void
LibDHCP::initVendorOptsDocsis4() {
    initOptionSpace(vendor4_defs_[VENDOR_ID_CABLE_LABS], DOCSIS3_V4_DEFS, DOCSIS3_V4_DEFS_SIZE);
664
665
666
}

void
667
668
669
670
671
672
673
674
675
LibDHCP::initVendorOptsDocsis6() {
    vendor6_defs_[VENDOR_ID_CABLE_LABS] = OptionDefContainer();
    initOptionSpace(vendor6_defs_[VENDOR_ID_CABLE_LABS], DOCSIS3_V6_DEFS, DOCSIS3_V6_DEFS_SIZE);
}

void initOptionSpace(OptionDefContainer& defs,
                     const OptionDefParams* params,
                     size_t params_size) {
    defs.clear();
676

677
678
679
    for (int i = 0; i < params_size; ++i) {
        std::string encapsulates(params[i].encapsulates);
        if (!encapsulates.empty() && params[i].array) {
680
            isc_throw(isc::BadValue, "invalid standard option definition: "
681
                      << "option with code '" << params[i].code
682
683
684
685
686
687
688
689
690
691
692
693
                      << "' may not encapsulate option space '"
                      << encapsulates << "' because the definition"
                      << " indicates that this option comprises an array"
                      << " of values");
        }

        // Depending whether an option encapsulates an option space or not
        // we pick different constructor to create an instance of the option
        // definition.
        OptionDefinitionPtr definition;
        if (encapsulates.empty()) {
            // Option does not encapsulate any option space.
694
695
696
697
            definition.reset(new OptionDefinition(params[i].name,
                                                  params[i].code,
                                                  params[i].type,
                                                  params[i].array));
698
699
        } else {
            // Option does encapsulate an option space.
700
701
702
703
            definition.reset(new OptionDefinition(params[i].name,
                                                  params[i].code,
                                                  params[i].type,
                                                  params[i].encapsulates));
704
705

        }
706

707
708
        for (int rec = 0; rec < params[i].records_size; ++rec) {
            definition->addRecordField(params[i].records[rec]);
709
        }
710

711
712
        try {
            definition->validate();
713
        } catch (const isc::Exception& ex) {
714
715
716
717
            // This is unlikely event that validation fails and may
            // be only caused by programming error. To guarantee the
            // data consistency we clear all option definitions that
            // have been added so far and pass the exception forward.
718
            defs.clear();
719
            throw;
720
        }
721
        defs.push_back(definition);
722
723
    }
}