Kea 2.0.0
client_class_def_parser.cc
Go to the documentation of this file.
1// Copyright (C) 2015-2021 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>
8#include <dhcp/libdhcp++.h>
9#include <dhcpsrv/cfgmgr.h>
15#include <eval/eval_context.h>
16#include <asiolink/io_address.h>
17#include <asiolink/io_error.h>
18
19#include <boost/foreach.hpp>
20#include <algorithm>
21#include <sstream>
22
23using namespace isc::data;
24using namespace isc::asiolink;
25using namespace std;
26
30
31namespace isc {
32namespace dhcp {
33
34// ********************** ExpressionParser ****************************
35
36void
38 ConstElementPtr expression_cfg,
39 uint16_t family,
40 EvalContext::CheckDefined check_defined) {
41 if (expression_cfg->getType() != Element::string) {
42 isc_throw(DhcpConfigError, "expression ["
43 << expression_cfg->str() << "] must be a string, at ("
44 << expression_cfg->getPosition() << ")");
45 }
46
47 // Get the expression's text via getValue() as the text returned
48 // by str() enclosed in quotes.
49 std::string value;
50 expression_cfg->getValue(value);
51 try {
52 EvalContext eval_ctx(family == AF_INET ? Option::V4 : Option::V6,
53 check_defined);
54 eval_ctx.parseString(value);
55 expression.reset(new Expression());
56 *expression = eval_ctx.expression;
57 } catch (const std::exception& ex) {
58 // Append position if there is a failure.
60 "expression: [" << value
61 << "] error: " << ex.what() << " at ("
62 << expression_cfg->getPosition() << ")");
63 }
64}
65
66// ********************** ClientClassDefParser ****************************
67
68void
70 ConstElementPtr class_def_cfg,
71 uint16_t family,
72 bool append_error_position,
73 bool check_dependencies) {
74 // name is now mandatory, so let's deal with it first.
75 std::string name = getString(class_def_cfg, "name");
76 if (name.empty()) {
78 "not empty parameter 'name' is required "
79 << getPosition("name", class_def_cfg) << ")");
80 }
81
82 // Parse matching expression
83 ExpressionPtr match_expr;
84 ConstElementPtr test_cfg = class_def_cfg->get("test");
85 std::string test;
86 bool depend_on_known = false;
87 if (test_cfg) {
88 ExpressionParser parser;
89 auto check_defined =
90 [&class_dictionary, &depend_on_known, check_dependencies]
91 (const ClientClass& cclass) {
92 return (!check_dependencies || isClientClassDefined(class_dictionary,
93 depend_on_known,
94 cclass));
95 };
96 parser.parse(match_expr, test_cfg, family, check_defined);
97 test = test_cfg->stringValue();
98 }
99
100 // Parse option def
101 CfgOptionDefPtr defs(new CfgOptionDef());
102 ConstElementPtr option_defs = class_def_cfg->get("option-def");
103 if (option_defs) {
104 // Apply defaults
105 SimpleParser::setListDefaults(option_defs,
106 family == AF_INET ?
109
110 OptionDefParser parser(family);
111 BOOST_FOREACH(ConstElementPtr option_def, option_defs->listValue()) {
112 OptionDefinitionPtr def = parser.parse(option_def);
113
114 // Verify if the definition is for an option which is in a deferred
115 // processing list.
116 if (!LibDHCP::shouldDeferOptionUnpack(def->getOptionSpaceName(),
117 def->getCode())) {
119 "Not allowed option definition for code '"
120 << def->getCode() << "' in space '"
121 << def->getOptionSpaceName() << "' at ("
122 << option_def->getPosition() << ")");
123 }
124 try {
125 defs->add(def);
126 } catch (const std::exception& ex) {
127 // Sanity check: it should never happen
128 isc_throw(DhcpConfigError, ex.what() << " ("
129 << option_def->getPosition() << ")");
130 }
131 }
132 }
133
134 // Parse option data
135 CfgOptionPtr options(new CfgOption());
136 ConstElementPtr option_data = class_def_cfg->get("option-data");
137 if (option_data) {
138 auto opts_parser = createOptionDataListParser(family, defs);
139 opts_parser->parse(options, option_data);
140 }
141
142 // Parse user context
143 ConstElementPtr user_context = class_def_cfg->get("user-context");
144
145 // Let's try to parse the only-if-required flag
146 bool required = false;
147 if (class_def_cfg->contains("only-if-required")) {
148 required = getBoolean(class_def_cfg, "only-if-required");
149 }
150
151 // Let's try to parse the next-server field
152 IOAddress next_server("0.0.0.0");
153 if (class_def_cfg->contains("next-server")) {
154 std::string next_server_txt = getString(class_def_cfg, "next-server");
155 try {
156 next_server = IOAddress(next_server_txt);
157 } catch (const IOError& ex) {
159 "Invalid next-server value specified: '"
160 << next_server_txt << "' ("
161 << getPosition("next-server", class_def_cfg) << ")");
162 }
163
164 if (next_server.getFamily() != AF_INET) {
165 isc_throw(DhcpConfigError, "Invalid next-server value: '"
166 << next_server_txt << "', must be IPv4 address ("
167 << getPosition("next-server", class_def_cfg) << ")");
168 }
169
170 if (next_server.isV4Bcast()) {
171 isc_throw(DhcpConfigError, "Invalid next-server value: '"
172 << next_server_txt << "', must not be a broadcast ("
173 << getPosition("next-server", class_def_cfg) << ")");
174 }
175 }
176
177 // Let's try to parse server-hostname
178 std::string sname;
179 if (class_def_cfg->contains("server-hostname")) {
180 sname = getString(class_def_cfg, "server-hostname");
181
182 if (sname.length() >= Pkt4::MAX_SNAME_LEN) {
183 isc_throw(DhcpConfigError, "server-hostname must be at most "
184 << Pkt4::MAX_SNAME_LEN - 1 << " bytes long, it is "
185 << sname.length() << " ("
186 << getPosition("server-hostname", class_def_cfg) << ")");
187 }
188 }
189
190 // Let's try to parse boot-file-name
191 std::string filename;
192 if (class_def_cfg->contains("boot-file-name")) {
193 filename = getString(class_def_cfg, "boot-file-name");
194
195 if (filename.length() > Pkt4::MAX_FILE_LEN) {
196 isc_throw(DhcpConfigError, "boot-file-name must be at most "
197 << Pkt4::MAX_FILE_LEN - 1 << " bytes long, it is "
198 << filename.length() << " ("
199 << getPosition("boot-file-name", class_def_cfg) << ")");
200 }
201
202 }
203
204 // Parse valid lifetime triplet.
205 Triplet<uint32_t> valid_lft = parseIntTriplet(class_def_cfg, "valid-lifetime");
206
207 Triplet<uint32_t> preferred_lft;
208 if (family != AF_INET) {
209 // Parse preferred lifetime triplet.
210 preferred_lft = parseIntTriplet(class_def_cfg, "preferred-lifetime");
211 }
212
213 // Sanity checks on built-in classes
214 for (auto bn : builtinNames) {
215 if (name == bn) {
216 if (required) {
217 isc_throw(DhcpConfigError, "built-in class '" << name
218 << "' only-if-required flag must be false");
219 }
220 if (!test.empty()) {
221 isc_throw(DhcpConfigError, "built-in class '" << name
222 << "' test expression must be empty");
223 }
224 }
225 }
226
227 // Sanity checks on DROP
228 if (name == "DROP") {
229 if (required) {
230 isc_throw(DhcpConfigError, "special class '" << name
231 << "' only-if-required flag must be false");
232 }
233 // depend_on_known is now allowed
234 }
235
236 // Add the client class definition
237 try {
238 class_dictionary->addClass(name, match_expr, test, required,
239 depend_on_known, options, defs,
240 user_context, next_server, sname, filename,
241 valid_lft, preferred_lft);
242 } catch (const std::exception& ex) {
243 std::ostringstream s;
244 s << "Can't add class: " << ex.what();
245 // Append position of the error in JSON string if required.
246 if (append_error_position) {
247 s << " (" << class_def_cfg->getPosition() << ")";
248 }
249 isc_throw(DhcpConfigError, s.str());
250 }
251}
252
253void
255 const uint16_t family) {
256 // Make sure that the client class definition is stored in a map.
257 if (!class_def_cfg || (class_def_cfg->getType() != Element::map)) {
258 isc_throw(DhcpConfigError, "client class definition is not a map");
259 }
260
261 // Common v4 and v6 parameters supported for the client class.
262 static std::set<std::string> supported_params = { "name",
263 "test",
264 "option-data",
265 "user-context",
266 "only-if-required",
267 "valid-lifetime",
268 "min-valid-lifetime",
269 "max-valid-lifetime" };
270
271
272 // The v4 client class supports additional parameters.
273 static std::set<std::string> supported_params_v4 = { "option-def",
274 "next-server",
275 "server-hostname",
276 "boot-file-name" };
277
278 // The v6 client class supports additional parameters.
279 static std::set<std::string> supported_params_v6 = { "preferred-lifetime",
280 "min-preferred-lifetime",
281 "max-preferred-lifetime" };
282
283 // Iterate over the specified parameters and check if they are all supported.
284 for (auto name_value_pair : class_def_cfg->mapValue()) {
285 if ((supported_params.count(name_value_pair.first) > 0) ||
286 ((family == AF_INET) && (supported_params_v4.count(name_value_pair.first) > 0)) ||
287 ((family != AF_INET) && (supported_params_v6.count(name_value_pair.first) > 0))) {
288 continue;
289 } else {
290 isc_throw(DhcpConfigError, "unsupported client class parameter '"
291 << name_value_pair.first << "'");
292 }
293 }
294}
295
296boost::shared_ptr<OptionDataListParser>
298 CfgOptionDefPtr cfg_option_def) const {
299 auto parser = boost::make_shared<OptionDataListParser>(address_family, cfg_option_def);
300 return (parser);
301}
302
303// ****************** ClientClassDefListParser ************************
304
307 uint16_t family, bool check_dependencies) {
309 BOOST_FOREACH(ConstElementPtr client_class_def,
310 client_class_def_list->listValue()) {
312 parser.parse(dictionary, client_class_def, family, true, check_dependencies);
313 }
314 return (dictionary);
315}
316
317} // end of namespace isc::dhcp
318} // end of namespace isc
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
const dhcp::Triplet< uint32_t > parseIntTriplet(const data::ConstElementPtr &scope, const std::string &name)
Parses an integer triplet.
static const data::Element::Position & getPosition(const std::string &name, const data::ConstElementPtr parent)
Utility method that returns position of an element.
static std::string getString(isc::data::ConstElementPtr scope, const std::string &name)
Returns a string parameter from a scope.
static bool getBoolean(isc::data::ConstElementPtr scope, const std::string &name)
Returns a boolean parameter from a scope.
Represents option definitions used by the DHCP server.
Represents option data configuration for the DHCP server.
Definition: cfg_option.h:314
ClientClassDictionaryPtr parse(isc::data::ConstElementPtr class_def_list, uint16_t family, bool check_dependencies=true)
Parse configuration entries.
Parser for a single client class definition.
virtual boost::shared_ptr< OptionDataListParser > createOptionDataListParser(const uint16_t address_family, CfgOptionDefPtr cfg_option_def) const
Returns an instance of the OptionDataListParser to be used in parsing the option-data structure.
void parse(ClientClassDictionaryPtr &class_dictionary, isc::data::ConstElementPtr client_class_def, uint16_t family, bool append_error_position=true, bool check_dependencies=true)
Parses an entry that describes single client class definition.
void checkParametersSupported(const isc::data::ConstElementPtr &class_def_cfg, const uint16_t family)
Iterates over class parameters and checks if they are supported.
Maintains a list of ClientClassDef's.
To be removed. Please use ConfigError instead.
Parser for a logical expression.
void parse(ExpressionPtr &expression, isc::data::ConstElementPtr expression_cfg, uint16_t family, isc::eval::EvalContext::CheckDefined check_defined=isc::eval::EvalContext::acceptAll)
Parses an expression configuration element into an Expression.
static bool shouldDeferOptionUnpack(const std::string &space, const uint16_t code)
Checks if an option unpacking has to be deferred.
Definition: libdhcp++.cc:278
Parser for a single option definition.
Definition: dhcp_parsers.h:228
OptionDefinitionPtr parse(isc::data::ConstElementPtr option_def)
Parses an entry that describes single option definition.
Definition: dhcp_parsers.cc:98
static const size_t MAX_SNAME_LEN
length of the SNAME field in DHCPv4 message
Definition: pkt4.h:44
static const size_t MAX_FILE_LEN
length of the FILE field in DHCPv4 message
Definition: pkt4.h:47
static const isc::data::SimpleDefaults OPTION4_DEF_DEFAULTS
This table defines default values for option definitions in DHCPv4.
static const isc::data::SimpleDefaults OPTION6_DEF_DEFAULTS
This table defines default values for option definitions in DHCPv6.
This template specifies a parameter value.
Definition: triplet.h:37
Evaluation context, an interface to the expression evaluation.
Definition: eval_context.h:34
std::function< bool(const ClientClass &)> CheckDefined
Type of the check defined function.
Definition: eval_context.h:44
bool parseString(const std::string &str, ParserType type=PARSER_BOOL)
Run the parser on the string specified.
Definition: eval_context.cc:37
isc::dhcp::Expression expression
Parsed expression (output tokens are stored here)
Definition: eval_context.h:67
Defines classes for storing client class definitions.
Parsers for client class definitions.
#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
std::string ClientClass
Defines a single class name.
Definition: classify.h:37
boost::shared_ptr< CfgOption > CfgOptionPtr
Non-const pointer.
Definition: cfg_option.h:706
boost::shared_ptr< CfgOptionDef > CfgOptionDefPtr
Non-const pointer.
boost::shared_ptr< OptionDefinition > OptionDefinitionPtr
Pointer to option definition object.
boost::shared_ptr< Expression > ExpressionPtr
Definition: token.h:30
boost::shared_ptr< ClientClassDictionary > ClientClassDictionaryPtr
Defines a pointer to a ClientClassDictionary.
bool isClientClassDefined(ClientClassDictionaryPtr &class_dictionary, bool &depend_on_known, const ClientClass &client_class)
Check if a client class name is already defined, i.e.
std::vector< TokenPtr > Expression
This is a structure that holds an expression converted to RPN.
Definition: token.h:28
std::list< std::string > builtinNames
List of built-in client class names.
Defines the logger used by the top-level component of kea-lfc.