Kea  1.9.9-git
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>
16 #include <eval/eval_context.h>
17 #include <asiolink/io_address.h>
18 #include <asiolink/io_error.h>
19 
20 #include <boost/foreach.hpp>
21 #include <algorithm>
22 #include <sstream>
23 
24 using namespace isc::data;
25 using namespace isc::asiolink;
26 using namespace std;
27 
31 
32 namespace isc {
33 namespace dhcp {
34 
35 // ********************** ExpressionParser ****************************
36 
37 void
38 ExpressionParser::parse(ExpressionPtr& expression,
39  ConstElementPtr expression_cfg,
40  uint16_t family,
41  EvalContext::CheckDefined check_defined) {
42  if (expression_cfg->getType() != Element::string) {
43  isc_throw(DhcpConfigError, "expression ["
44  << expression_cfg->str() << "] must be a string, at ("
45  << expression_cfg->getPosition() << ")");
46  }
47 
48  // Get the expression's text via getValue() as the text returned
49  // by str() enclosed in quotes.
50  std::string value;
51  expression_cfg->getValue(value);
52  try {
53  EvalContext eval_ctx(family == AF_INET ? Option::V4 : Option::V6,
54  check_defined);
55  eval_ctx.parseString(value);
56  expression.reset(new Expression());
57  *expression = eval_ctx.expression;
58  } catch (const std::exception& ex) {
59  // Append position if there is a failure.
61  "expression: [" << value
62  << "] error: " << ex.what() << " at ("
63  << expression_cfg->getPosition() << ")");
64  }
65 }
66 
67 // ********************** ClientClassDefParser ****************************
68 
69 void
70 ClientClassDefParser::parse(ClientClassDictionaryPtr& class_dictionary,
71  ConstElementPtr class_def_cfg,
72  uint16_t family,
73  bool append_error_position) {
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]
91  (const ClientClass& cclass) {
92  return (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 ?
107  SimpleParser4::OPTION4_DEF_DEFAULTS :
108  SimpleParser6::OPTION6_DEF_DEFAULTS);
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 defition is for an option which are
115  // in a deferred 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  OptionDataListParser opts_parser(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  // Sanity checks on built-in classes
208  for (auto bn : builtinNames) {
209  if (name == bn) {
210  if (required) {
211  isc_throw(DhcpConfigError, "built-in class '" << name
212  << "' only-if-required flag must be false");
213  }
214  if (!test.empty()) {
215  isc_throw(DhcpConfigError, "built-in class '" << name
216  << "' test expression must be empty");
217  }
218  }
219  }
220 
221  // Sanity checks on DROP
222  if (name == "DROP") {
223  if (required) {
224  isc_throw(DhcpConfigError, "special class '" << name
225  << "' only-if-required flag must be false");
226  }
227  // depend_on_known is now allowed
228  }
229 
230  // Add the client class definition
231  try {
232  class_dictionary->addClass(name, match_expr, test, required,
233  depend_on_known, options, defs,
234  user_context, next_server, sname, filename, valid_lft);
235  } catch (const std::exception& ex) {
236  std::ostringstream s;
237  s << "Can't add class: " << ex.what();
238  // Append position of the error in JSON string if required.
239  if (append_error_position) {
240  s << " (" << class_def_cfg->getPosition() << ")";
241  }
242  isc_throw(DhcpConfigError, s.str());
243  }
244 }
245 
246 void
247 ClientClassDefParser::checkParametersSupported(const ConstElementPtr& class_def_cfg,
248  const uint16_t family) {
249  // Make sure that the client class definition is stored in a map.
250  if (!class_def_cfg || (class_def_cfg->getType() != Element::map)) {
251  isc_throw(DhcpConfigError, "client class definition is not a map");
252  }
253 
254  // Common v4 and v6 parameters supported for the client class.
255  static std::set<std::string> supported_params = { "name",
256  "test",
257  "option-data",
258  "user-context",
259  "only-if-required" };
260 
261  // The v4 client class supports additional parameters.
262  static std::set<std::string> supported_params_v4 = { "option-def",
263  "next-server",
264  "server-hostname",
265  "boot-file-name" };
266 
267  // Iterate over the specified parameters and check if they are all supported.
268  for (auto name_value_pair : class_def_cfg->mapValue()) {
269  if ((supported_params.count(name_value_pair.first) > 0) ||
270  ((family == AF_INET) && (supported_params_v4.count(name_value_pair.first) > 0))) {
271  continue;
272 
273  } else {
274  isc_throw(DhcpConfigError, "unsupported client class parameter '"
275  << name_value_pair.first << "'");
276  }
277  }
278 }
279 
280 
281 // ****************** ClientClassDefListParser ************************
282 
284 ClientClassDefListParser::parse(ConstElementPtr client_class_def_list,
285  uint16_t family) {
287  BOOST_FOREACH(ConstElementPtr client_class_def,
288  client_class_def_list->listValue()) {
289  ClientClassDefParser parser;
290  parser.parse(dictionary, client_class_def, family);
291  }
292  return (dictionary);
293 }
294 
295 } // end of namespace isc::dhcp
296 } // end of namespace isc
Parser for a single client class definition.
void parse(const CfgOptionPtr &cfg, isc::data::ConstElementPtr option_data_list)
Parses a list of options, instantiates them and stores in cfg.
boost::shared_ptr< CfgOption > CfgOptionPtr
Non-const pointer.
Definition: cfg_option.h:706
void parse(ClientClassDictionaryPtr &class_dictionary, isc::data::ConstElementPtr client_class_def, uint16_t family, bool append_error_position=true)
Parses an entry that describes single client class definition.
isc::dhcp::Expression expression
Parsed expression (output tokens are stored here)
Definition: eval_context.h:67
STL namespace.
boost::shared_ptr< CfgOptionDef > CfgOptionDefPtr
Non-const pointer.
OptionDefinitionPtr parse(isc::data::ConstElementPtr option_def)
Parses an entry that describes single option definition.
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.
Maintains a list of ClientClassDef's.
bool isClientClassDefined(ClientClassDictionaryPtr &class_dictionary, bool &depend_on_known, const ClientClass &client_class)
Check if a client class name is already defined, i.e.
Parser for a logical expression.
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
Represents option data configuration for the DHCP server.
Definition: cfg_option.h:314
Parsers for client class definitions.
To be removed. Please use ConfigError instead.
Parser for a single option definition.
Definition: dhcp_parsers.h:227
boost::shared_ptr< ClientClassDictionary > ClientClassDictionaryPtr
Defines a pointer to a ClientClassDictionary.
Evaluation context, an interface to the expression evaluation.
Definition: eval_context.h:34
boost::shared_ptr< const Element > ConstElementPtr
Definition: data.h:23
Represents option definitions used by the DHCP server.
Parser for option data values within a subnet.
std::list< std::string > builtinNames
List of built-in client class names.
static size_t setListDefaults(isc::data::ConstElementPtr list, const SimpleDefaults &default_values)
Sets the default values for all entries in a list.
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
Defines the logger used by the top-level component of kea-dhcp-ddns.
std::vector< TokenPtr > Expression
This is a structure that holds an expression converted to RPN.
Definition: token.h:28
boost::shared_ptr< OptionDefinition > OptionDefinitionPtr
Pointer to option definition object.
std::string ClientClass
Defines a single class name.
Definition: classify.h:37
std::function< bool(const ClientClass &)> CheckDefined
Type of the check defined function.
Definition: eval_context.h:44
Defines classes for storing client class definitions.
boost::shared_ptr< Expression > ExpressionPtr
Definition: token.h:30
bool parseString(const std::string &str, ParserType type=PARSER_BOOL)
Run the parser on the string specified.
Definition: eval_context.cc:37