Kea 2.2.0
host_reservation_parser.cc
Go to the documentation of this file.
1// Copyright (C) 2014-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>
9#include <dhcpsrv/cfgmgr.h>
12#include <boost/foreach.hpp>
13#include <boost/lexical_cast.hpp>
14#include <algorithm>
15#include <sys/socket.h>
16#include <sstream>
17#include <string>
18
19using namespace isc::asiolink;
20using namespace isc::data;
21
22namespace {
23
32const std::set<std::string>&
33getSupportedParams4(const bool identifiers_only = false) {
34 // Holds set of host identifiers.
35 static std::set<std::string> identifiers_set;
36 // Holds set of all supported parameters, including identifiers.
37 static std::set<std::string> params_set;
38 // If this is first execution of this function, we need
39 // to initialize the set.
40 if (identifiers_set.empty()) {
41 identifiers_set.insert("hw-address");
42 identifiers_set.insert("duid");
43 identifiers_set.insert("circuit-id");
44 identifiers_set.insert("client-id");
45 identifiers_set.insert("flex-id");
46 }
47 // Copy identifiers and add all other parameters.
48 if (params_set.empty()) {
49 params_set = identifiers_set;
50 params_set.insert("hostname");
51 params_set.insert("ip-address");
52 params_set.insert("option-data");
53 params_set.insert("next-server");
54 params_set.insert("server-hostname");
55 params_set.insert("boot-file-name");
56 params_set.insert("client-classes");
57 params_set.insert("user-context");
58 }
59 return (identifiers_only ? identifiers_set : params_set);
60}
61
70const std::set<std::string>&
71getSupportedParams6(const bool identifiers_only = false) {
72 // Holds set of host identifiers.
73 static std::set<std::string> identifiers_set;
74 // Holds set of all supported parameters, including identifiers.
75 static std::set<std::string> params_set;
76 // If this is first execution of this function, we need
77 // to initialize the set.
78 if (identifiers_set.empty()) {
79 identifiers_set.insert("hw-address");
80 identifiers_set.insert("duid");
81 identifiers_set.insert("flex-id");
82 }
83 // Copy identifiers and add all other parameters.
84 if (params_set.empty()) {
85 params_set = identifiers_set;
86 params_set.insert("hostname");
87 params_set.insert("ip-addresses");
88 params_set.insert("prefixes");
89 params_set.insert("option-data");
90 params_set.insert("client-classes");
91 params_set.insert("user-context");
92 }
93 return (identifiers_only ? identifiers_set : params_set);
94}
95
96}
97
98namespace isc {
99namespace dhcp {
100
103 isc::data::ConstElementPtr reservation_data) {
104 return (parseInternal(subnet_id, reservation_data));
105}
106
109 isc::data::ConstElementPtr reservation_data) {
110 std::string identifier;
111 std::string identifier_name;
112 std::string hostname;
113 ConstElementPtr user_context;
114 HostPtr host;
115
116 try {
117 // Gather those parameters that are common for both IPv4 and IPv6
118 // reservations.
119 BOOST_FOREACH(auto element, reservation_data->mapValue()) {
120 // Check if we support this parameter.
121 if (!isSupportedParameter(element.first)) {
122 isc_throw(DhcpConfigError, "unsupported configuration"
123 " parameter '" << element.first << "'");
124 }
125
126 if (isIdentifierParameter(element.first)) {
127 if (!identifier.empty()) {
128 isc_throw(DhcpConfigError, "the '" << element.first
129 << "' and '" << identifier_name
130 << "' are mutually exclusive");
131 }
132 identifier = element.second->stringValue();
133 identifier_name = element.first;
134
135 } else if (element.first == "hostname") {
136 hostname = element.second->stringValue();
137 } else if (element.first == "user-context") {
138 user_context = element.second;
139 }
140 }
141
142 // Host identifier is a must.
143 if (identifier_name.empty()) {
144 // If there is no identifier specified, we have to display an
145 // error message and include the information what identifiers
146 // are supported.
147 std::ostringstream s;
148 BOOST_FOREACH(std::string param_name, getSupportedParameters(true)) {
149 if (s.tellp() != std::streampos(0)) {
150 s << ", ";
151 }
152 s << param_name;
153 }
154 isc_throw(DhcpConfigError, "one of the supported identifiers must"
155 " be specified for host reservation: "
156 << s.str());
157
158 }
159
160 // Create a host object from the basic parameters we already parsed.
161 host.reset(new Host(identifier, identifier_name, SUBNET_ID_UNUSED,
162 SUBNET_ID_UNUSED, IOAddress("0.0.0.0"), hostname));
163
164 // Add user context
165 if (user_context) {
166 host->setContext(user_context);
167 }
168 } catch (const std::exception& ex) {
169 // Append line number where the error occurred.
170 isc_throw(DhcpConfigError, ex.what() << " ("
171 << reservation_data->getPosition() << ")");
172 }
173
174 return (host);
175}
176
177bool
178HostReservationParser::isIdentifierParameter(const std::string& param_name) const {
179 return (getSupportedParameters(true).count(param_name) > 0);
180}
181
182bool
183HostReservationParser::isSupportedParameter(const std::string& param_name) const {
184 return (getSupportedParameters(false).count(param_name) > 0);
185}
186
189 isc::data::ConstElementPtr reservation_data) {
190 HostPtr host = HostReservationParser::parseInternal(subnet_id, reservation_data);
191
192 host->setIPv4SubnetID(subnet_id);
193
194 BOOST_FOREACH(auto element, reservation_data->mapValue()) {
195 // For 'option-data' element we will use another parser which
196 // already returns errors with position appended, so don't
197 // surround it with try-catch.
198 if (element.first == "option-data") {
199 CfgOptionPtr cfg_option = host->getCfgOption4();
200
201 // This parser is converted to SimpleParser already. It
202 // parses the Element structure immediately, there's no need
203 // to go through build/commit phases.
204 OptionDataListParser parser(AF_INET);
205 parser.parse(cfg_option, element.second);
206
207 // Everything else should be surrounded with try-catch to append
208 // position.
209 } else {
210 try {
211 if (element.first == "ip-address") {
212 host->setIPv4Reservation(IOAddress(element.second->
213 stringValue()));
214 } else if (element.first == "next-server") {
215 host->setNextServer(IOAddress(element.second->stringValue()));
216
217 } else if (element.first == "server-hostname") {
218 host->setServerHostname(element.second->stringValue());
219
220 } else if (element.first == "boot-file-name") {
221 host->setBootFileName(element.second->stringValue());
222
223 } else if (element.first == "client-classes") {
224 BOOST_FOREACH(ConstElementPtr class_element,
225 element.second->listValue()) {
226 host->addClientClass4(class_element->stringValue());
227 }
228 }
229
230 } catch (const std::exception& ex) {
231 // Append line number where the error occurred.
232 isc_throw(DhcpConfigError, ex.what() << " ("
233 << element.second->getPosition() << ")");
234 }
235 }
236 }
237
238 return (host);
239}
240
241const std::set<std::string>&
242HostReservationParser4::getSupportedParameters(const bool identifiers_only) const {
243 return (getSupportedParams4(identifiers_only));
244}
245
248 isc::data::ConstElementPtr reservation_data) {
249 HostPtr host = HostReservationParser::parseInternal(subnet_id, reservation_data);
250
251 host->setIPv6SubnetID(subnet_id);
252
253 BOOST_FOREACH(auto element, reservation_data->mapValue()) {
254 // Parse option values. Note that the configuration option parser
255 // returns errors with position information appended, so there is no
256 // need to surround it with try-clause (and rethrow with position
257 // appended).
258 if (element.first == "option-data") {
259 CfgOptionPtr cfg_option = host->getCfgOption6();
260
261 // This parser is converted to SimpleParser already. It
262 // parses the Element structure immediately, there's no need
263 // to go through build/commit phases.
264 OptionDataListParser parser(AF_INET6);
265 parser.parse(cfg_option, element.second);
266
267 } else if (element.first == "ip-addresses" || element.first == "prefixes") {
268 BOOST_FOREACH(ConstElementPtr prefix_element,
269 element.second->listValue()) {
270 try {
271 // For the IPv6 address the prefix length is 128 and the
272 // value specified in the list is a reserved address.
274 std::string prefix = prefix_element->stringValue();
275 uint8_t prefix_len = 128;
276
277 // If we're dealing with prefixes, instead of addresses,
278 // we will have to extract the prefix length from the value
279 // specified in the following format: 2001:db8:2000::/64.
280 if (element.first == "prefixes") {
281 // The slash is mandatory for prefixes. If there is no
282 // slash, return an error.
283 size_t len_pos = prefix.find('/');
284 if (len_pos == std::string::npos) {
285 isc_throw(DhcpConfigError, "prefix reservation"
286 " requires prefix length be specified"
287 " in '" << prefix << "'");
288
289 // If there is nothing after the slash, we should also
290 // report an error.
291 } else if (len_pos >= prefix.length() - 1) {
292 isc_throw(DhcpConfigError, "prefix '" << prefix
293 << "' requires length after '/'");
294
295 }
296
297 // Convert the prefix length from the string to the
298 // number. Note, that we don't use the uint8_t type
299 // as the lexical cast would expect a character, e.g.
300 // 'a', instead of prefix length, e.g. '64'.
301 try {
302 prefix_len = boost::lexical_cast<
303 unsigned int>(prefix.substr(len_pos + 1));
304
305 } catch (const boost::bad_lexical_cast&) {
306 isc_throw(DhcpConfigError, "prefix length value '"
307 << prefix.substr(len_pos + 1)
308 << "' is invalid");
309 }
310
311 // Remove the slash character and the prefix length
312 // from the parsed value.
313 prefix.erase(len_pos);
314
315 // Finally, set the reservation type.
316 resrv_type = IPv6Resrv::TYPE_PD;
317 }
318
319 // Create a reservation for an address or prefix.
320 host->addReservation(IPv6Resrv(resrv_type,
321 IOAddress(prefix),
322 prefix_len));
323
324 } catch (const std::exception& ex) {
325 // Append line number where the error occurred.
326 isc_throw(DhcpConfigError, ex.what() << " ("
327 << prefix_element->getPosition() << ")");
328 }
329 }
330
331
332 } else if (element.first == "client-classes") {
333 try {
334 BOOST_FOREACH(ConstElementPtr class_element,
335 element.second->listValue()) {
336 host->addClientClass6(class_element->stringValue());
337 }
338 } catch (const std::exception& ex) {
339 // Append line number where the error occurred.
340 isc_throw(DhcpConfigError, ex.what() << " ("
341 << element.second->getPosition() << ")");
342 }
343 }
344 }
345
346 return (host);
347}
348
349const std::set<std::string>&
350HostReservationParser6::getSupportedParameters(const bool identifiers_only) const {
351 return (getSupportedParams6(identifiers_only));
352}
353
355 : staging_cfg_() {
356}
357
358void
360 parseInternal(ids_list);
361}
362
363void
365 // Remove existing identifier types.
366 staging_cfg_->clearIdentifierTypes();
367
368 BOOST_FOREACH(ConstElementPtr element, ids_list->listValue()) {
369 std::string id_name = element->stringValue();
370 try {
371 if (id_name != "auto") {
372 if (!isSupportedIdentifier(id_name)) {
373 isc_throw(isc::BadValue, "unsupported identifier '"
374 << id_name << "'");
375 }
376 staging_cfg_->addIdentifierType(id_name);
377
378 } else {
379 // 'auto' is mutually exclusive with other values. If there
380 // are any values in the configuration already it means that
381 // some other values have already been specified.
382 if (!staging_cfg_->getIdentifierTypes().empty()) {
383 isc_throw(isc::BadValue, "if 'auto' keyword is used,"
384 " no other values can be specified within '"
385 "host-reservation-identifiers' list");
386 }
387 // Iterate over all identifier types and for those supported
388 // in a given context (DHCPv4 or DHCPv6) add the identifier type
389 // to the configuration.
390 for (unsigned int i = 0;
391 i <= static_cast<unsigned int>(Host::LAST_IDENTIFIER_TYPE);
392 ++i) {
393 std::string supported_id_name =
395 if (isSupportedIdentifier(supported_id_name)) {
396 staging_cfg_->addIdentifierType(supported_id_name);
397 }
398 }
399 }
400
401 } catch (const std::exception& ex) {
402 // Append line number where the error occurred.
403 isc_throw(DhcpConfigError, ex.what() << " ("
404 << element->getPosition() << ")");
405 }
406 }
407
408 // The parsed list must not be empty.
409 if (staging_cfg_->getIdentifierTypes().empty()) {
410 isc_throw(DhcpConfigError, "'host-reservation-identifiers' list must not"
411 " be empty (" << ids_list->getPosition() << ")");
412 }
413
414}
415
418 staging_cfg_ = CfgMgr::instance().getStagingCfg()->getCfgHostOperations4();
419}
420
421bool
422HostReservationIdsParser4::isSupportedIdentifier(const std::string& id_name) const {
423 return (getSupportedParams4(true).count(id_name) > 0);
424}
425
428 staging_cfg_ = CfgMgr::instance().getStagingCfg()->getCfgHostOperations6();
429}
430
431bool
432HostReservationIdsParser6::isSupportedIdentifier(const std::string& id_name) const {
433 return (getSupportedParams6(true).count(id_name) > 0);
434}
435
436} // end of namespace isc::dhcp
437} // end of namespace isc
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
static CfgMgr & instance()
returns a single instance of Configuration Manager
Definition: cfgmgr.cc:25
SrvConfigPtr getStagingCfg()
Returns a pointer to the staging configuration.
Definition: cfgmgr.cc:167
To be removed. Please use ConfigError instead.
virtual bool isSupportedIdentifier(const std::string &id_name) const
Checks if specified identifier name is supported for DHCPv4.
virtual bool isSupportedIdentifier(const std::string &id_name) const
Checks if specified identifier name is supported for DHCPv6.
Parser for a list of host identifiers.
CfgHostOperationsPtr staging_cfg_
Pointer to the object holding configuration.
virtual bool isSupportedIdentifier(const std::string &id_name) const =0
Checks if specified identifier name is supported in the context of the parser.
void parse(isc::data::ConstElementPtr ids_list)
Parses a list of host identifiers.
virtual void parseInternal(isc::data::ConstElementPtr ids_list)
Parses a list of host identifiers.
virtual const std::set< std::string > & getSupportedParameters(const bool identifiers_only) const
Returns set of the supported parameters for DHCPv4.
virtual HostPtr parseInternal(const SubnetID &subnet_id, isc::data::ConstElementPtr reservation_data)
Parses a single host reservation for DHCPv4.
virtual HostPtr parseInternal(const SubnetID &subnet_id, isc::data::ConstElementPtr reservation_data)
Parses a single host reservation for DHCPv6.
virtual const std::set< std::string > & getSupportedParameters(const bool identifiers_only) const
Returns set of the supported parameters for DHCPv6.
virtual bool isIdentifierParameter(const std::string &param_name) const
Checks if the specified parameter is a host identifier.
virtual HostPtr parseInternal(const SubnetID &subnet_id, isc::data::ConstElementPtr reservation_data)
Parses a single entry for host reservation.
virtual HostPtr parse(const SubnetID &subnet_id, isc::data::ConstElementPtr reservation_data) final
Parses a single entry for host reservation.
virtual const std::set< std::string > & getSupportedParameters(const bool identifiers_only) const =0
Returns set of the supported parameters.
virtual bool isSupportedParameter(const std::string &param_name) const
Checks if the specified parameter is supported by the parser.
Represents a device with IPv4 and/or IPv6 reservations.
Definition: host.h:297
IdentifierType
Type of the host identifier.
Definition: host.h:307
static const IdentifierType LAST_IDENTIFIER_TYPE
Constant pointing to the last identifier of the IdentifierType enumeration.
Definition: host.h:317
static std::string getIdentifierName(const IdentifierType &type)
Returns name of the identifier of a specified type.
Definition: host.cc:293
IPv6 reservation for a host.
Definition: host.h:161
Type
Type of the reservation.
Definition: host.h:167
Parser for option data values within a subnet.
void parse(const CfgOptionPtr &cfg, isc::data::ConstElementPtr option_data_list)
Parses a list of options, instantiates them and stores in cfg.
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
boost::shared_ptr< const Element > ConstElementPtr
Definition: data.h:27
boost::shared_ptr< CfgOption > CfgOptionPtr
Non-const pointer.
Definition: cfg_option.h:706
boost::shared_ptr< Host > HostPtr
Pointer to the Host object.
Definition: host.h:785
uint32_t SubnetID
Defines unique IPv4 or IPv6 subnet identifier.
Definition: subnet_id.h:24
Defines the logger used by the top-level component of kea-lfc.