summaryrefslogtreecommitdiffstats
path: root/src/lib/dhcpsrv/cfg_option_def.cc
blob: 02de922ee111ba2fbbf27cef2715bd46d1e17666 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
// Copyright (C) 2014-2021 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#include <config.h>
#include <dhcp/libdhcp++.h>
#include <dhcp/option_data_types.h>
#include <dhcp/option_definition.h>
#include <dhcp/option_space.h>
#include <dhcpsrv/cfg_option_def.h>
#include <sstream>

using namespace isc::data;

namespace isc {
namespace dhcp {

void
CfgOptionDef::copyTo(CfgOptionDef& new_config) const {
    // Remove any existing option definitions from the destination.
    new_config.option_definitions_.clearItems();
    const std::list<std::string>& names =
        option_definitions_.getOptionSpaceNames();
    for (std::list<std::string>::const_iterator name = names.begin();
         name != names.end(); ++name) {
        OptionDefContainerPtr defs = getAll(*name);
        for (OptionDefContainer::const_iterator def = defs->begin();
             def != defs->end(); ++def) {
            OptionDefinitionPtr new_def =
                OptionDefinitionPtr(new OptionDefinition(**def));
            new_config.add(new_def);
        }
    }
}

bool
CfgOptionDef::equals(const CfgOptionDef& other) const {
    // Get our option space names.
    const std::list<std::string>& names = option_definitions_.getOptionSpaceNames();
    // Get option space names held by the other object.
    const std::list<std::string>&
        other_names = other.option_definitions_.getOptionSpaceNames();
    // Compare that sizes are the same. If they hold different number of
    // option space names the objects are not equal.
    if (names.size() != other_names.size()) {
        return (false);
    }
    // Iterate over all option space names and get the definitions for each
    // of them.
    for (std::list<std::string>::const_iterator name = names.begin();
         name != names.end(); ++name) {
        // Get all definitions.
        OptionDefContainerPtr defs = getAll(*name);
        OptionDefContainerPtr other_defs = other.getAll(*name);
        // Compare sizes. If they hold different number of definitions,
        // they are unequal.
        if (defs->size() != defs->size()) {
            return (false);
        }
        // For each option definition, try to find one in the other object.
        for (OptionDefContainer::const_iterator def = defs->begin();
             def != defs->end(); ++def) {
            OptionDefinitionPtr
                other_def = other.get(*name, (*def)->getCode());
            // Actually compare them.
            if (!other_def || (*other_def != **def)) {
                return (false);
            }
        }
    }

    // All checks passed.
    return (true);
}

void
CfgOptionDef::add(const OptionDefinitionPtr& def) {
    // Option definition being added must be a valid pointer.
    if (!def) {
        isc_throw(MalformedOptionDefinition,
                  "option definition must not be NULL");
    }
    const std::string& option_space = def->getOptionSpaceName();

    // Must not duplicate an option definition.
    if (get(option_space, def->getCode())) {
        isc_throw(DuplicateOptionDefinition, "option definition with code '"
                  << def->getCode() << "' already exists in option"
                  " space '" << option_space << "'");
    } else if (get(option_space, def->getName())) {
        isc_throw(DuplicateOptionDefinition, "option definition with name '"
                  << def->getName() << "' already exists in option"
                  " space '" << option_space << "'");

    // Must not override standard option definition.
    } else if (LibDHCP::getOptionDef(option_space, def->getCode())) {
        isc_throw(BadValue, "unable to override definition of option '"
                  << def->getCode() << "' in standard option space '"
                  << option_space << "'");
    } else if (LibDHCP::getOptionDef(option_space, def->getName())) {
        isc_throw(BadValue, "unable to override definition of option '"
                  << def->getName() << "' in standard option space '"
                  << option_space << "'");
    }
    // Add the definition.
    option_definitions_.addItem(def);
}

OptionDefContainerPtr
CfgOptionDef::getAll(const std::string& option_space) const {
    /// @todo Does option space require any validation here?
    return (option_definitions_.getItems(option_space));
}

OptionDefinitionPtr
CfgOptionDef::get(const std::string& option_space,
                  const uint16_t option_code) const {
    // Get the pointer to collection of the option definitions that belong
    // to the particular option space.
    OptionDefContainerPtr defs = getAll(option_space);
    // If there are any option definitions for this option space, get the
    // one that has the specified option code.
    if (defs && !defs->empty()) {
        const OptionDefContainerTypeIndex& idx = defs->get<1>();
        const OptionDefContainerTypeRange& range = idx.equal_range(option_code);
        // If there is more than one definition matching the option code,
        // return the first one. In fact, it shouldn't happen that we have
        // more than one because we check for duplicates when we add them.
        if (std::distance(range.first, range.second) > 0) {
            return (*range.first);
        }
    }
    // Nothing found. Return NULL pointer.
    return (OptionDefinitionPtr());
}

OptionDefinitionPtr
CfgOptionDef::get(const std::string& option_space,
                  const std::string& option_name) const {
    // Get the pointer to collection of the option definitions that belong
    // to the particular option space.
    OptionDefContainerPtr defs = getAll(option_space);
    // If there are any option definitions for this option space, get the
    // one that has the specified option name.
    if (defs && !defs->empty()) {
        const OptionDefContainerNameIndex& idx = defs->get<2>();
        const OptionDefContainerNameRange& range = idx.equal_range(option_name);
        // If there is more than one definition matching the option name,
        // return the first one. In fact, it shouldn't happen that we have
        // more than one because we check for duplicates when we add them.
        if (std::distance(range.first, range.second) > 0) {
            return (*range.first);
        }
    }
    // Nothing found. Return NULL pointer.
    return (OptionDefinitionPtr());
}

uint64_t
CfgOptionDef::del(const uint64_t id) {
    return (option_definitions_.deleteItems(id));
}

ElementPtr
CfgOptionDef::toElement() const {
    return (toElementWithMetadata(false));
}

ElementPtr
CfgOptionDef::toElementWithMetadata(const bool include_metadata) const {
    // option-defs value is a list of maps
    ElementPtr result = Element::createList();
    // Iterate through the container by names and definitions
    const std::list<std::string>& names =
        option_definitions_.getOptionSpaceNames();
    for (std::list<std::string>::const_iterator name = names.begin();
         name != names.end(); ++name) {
        OptionDefContainerPtr defs = getAll(*name);
        for (OptionDefContainer::const_iterator def = defs->begin();
             def != defs->end(); ++def) {
            // Get and fill the map for this definition
            ElementPtr map = Element::createMap();
            // Set user context
            (*def)->contextToElement(map);
            // Set space from parent iterator
            map->set("space", Element::create(*name));
            // Set required items: name, code and type
            map->set("name", Element::create((*def)->getName()));
            map->set("code", Element::create((*def)->getCode()));
            std::string data_type =
                OptionDataTypeUtil::getDataTypeName((*def)->getType());
            map->set("type", Element::create(data_type));
            // Set the array type
            bool array_type = (*def)->getArrayType();
            map->set("array", Element::create(array_type));
            // Set the encapsulate space
            std::string encapsulates = (*def)->getEncapsulatedSpace();
            map->set("encapsulate", Element::create(encapsulates));
            // Set the record field types
            OptionDefinition::RecordFieldsCollection fields =
                (*def)->getRecordFields();
            if (!fields.empty()) {
                std::ostringstream oss;
                for (OptionDefinition::RecordFieldsCollection::const_iterator
                         field = fields.begin();
                     field != fields.end(); ++field) {
                    if (field != fields.begin()) {
                        oss << ", ";
                    }
                    oss << OptionDataTypeUtil::getDataTypeName(*field);
                }
                map->set("record-types", Element::create(oss.str()));
            } else {
                map->set("record-types", Element::create(std::string()));
            }

            // Include metadata if requested.
            if (include_metadata) {
                map->set("metadata", (*def)->getMetadata());
            }

            // Push on the list
            result->add(map);
        }
    }
    return (result);
}

void
CfgOptionDef::merge(CfgOptionDef& other) {
    // The definitions in "other" are presumed to be valid and
    // not in conflict with standard definitions.
    if (other.getContainer().getOptionSpaceNames().empty()) {
        // Nothing to merge, don't waste cycles.
        return;
    }

    // Iterate over this config's definitions in each space.
    // If either a definition's name or code already exist in
    // that space in "other", skip it.  Otherwise, add it to "other".
    for (auto space : option_definitions_.getOptionSpaceNames()) {
        for (auto my_def : *(getAll(space))) {
            if ((other.get(space, my_def->getName())) ||
                (other.get(space, my_def->getCode()))) {
                // Already in "other" so skip it.
                continue;
            }

            // Not in "other" so add it.
            other.add(my_def);
        }
    }

    // Replace the current definitions with the merged set.
    other.copyTo(*this);
}

} // end of namespace isc::dhcp
} // end of namespace isc