Kea 1.5.0
client_class_def_parser.cc
Go to the documentation of this file.
1// Copyright (C) 2015-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>
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
24using namespace isc::data;
25using namespace isc::asiolink;
26using namespace std;
27
31
32namespace isc {
33namespace dhcp {
34
35// ********************** ExpressionParser ****************************
36
37void
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
69void
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 using std::placeholders::_1;
90 auto check_defined =
91 [&class_dictionary, &depend_on_known]
92 (const ClientClass& cclass) {
93 return (isClientClassDefined(class_dictionary,
94 depend_on_known,
95 cclass));
96 };
97 parser.parse(match_expr, test_cfg, family, check_defined);
98 test = test_cfg->stringValue();
99 }
100
101 // Parse option def
102 CfgOptionDefPtr defs(new CfgOptionDef());
103 ConstElementPtr option_defs = class_def_cfg->get("option-def");
104 if (option_defs) {
105 // Apply defaults
107 family == AF_INET ?
110
111 OptionDefParser parser;
112 BOOST_FOREACH(ConstElementPtr option_def, option_defs->listValue()) {
114
115 def = parser.parse(option_def);
116 // Verify if the defition is for an option which are
117 // in a deferred processing list.
118 if (!LibDHCP::shouldDeferOptionUnpack(def.second,
119 def.first->getCode())) {
121 "Not allowed option definition for code '"
122 << def.first->getCode() << "' in space '"
123 << def.second << "' at ("
124 << option_def->getPosition() << ")");
125 }
126 try {
127 defs->add(def.first, def.second);
128 } catch (const std::exception& ex) {
129 // Sanity check: it should never happen
130 isc_throw(DhcpConfigError, ex.what() << " ("
131 << option_def->getPosition() << ")");
132 }
133 }
134 }
135
136 // Parse option data
137 CfgOptionPtr options(new CfgOption());
138 ConstElementPtr option_data = class_def_cfg->get("option-data");
139 if (option_data) {
140 OptionDataListParser opts_parser(family, defs);
141 opts_parser.parse(options, option_data);
142 }
143
144 // Parse user context
145 ConstElementPtr user_context = class_def_cfg->get("user-context");
146
147 // Let's try to parse the only-if-required flag
148 bool required = false;
149 if (class_def_cfg->contains("only-if-required")) {
150 required = getBoolean(class_def_cfg, "only-if-required");
151 }
152
153 // Let's try to parse the next-server field
154 IOAddress next_server("0.0.0.0");
155 if (class_def_cfg->contains("next-server")) {
156 std::string next_server_txt = getString(class_def_cfg, "next-server");
157 try {
158 next_server = IOAddress(next_server_txt);
159 } catch (const IOError& ex) {
161 "Invalid next-server value specified: '"
162 << next_server_txt << "' ("
163 << getPosition("next-server", class_def_cfg) << ")");
164 }
165
166 if (next_server.getFamily() != AF_INET) {
167 isc_throw(DhcpConfigError, "Invalid next-server value: '"
168 << next_server_txt << "', must be IPv4 address ("
169 << getPosition("next-server", class_def_cfg) << ")");
170 }
171
172 if (next_server.isV4Bcast()) {
173 isc_throw(DhcpConfigError, "Invalid next-server value: '"
174 << next_server_txt << "', must not be a broadcast ("
175 << getPosition("next-server", class_def_cfg) << ")");
176 }
177 }
178
179 // Let's try to parse server-hostname
180 std::string sname;
181 if (class_def_cfg->contains("server-hostname")) {
182 sname = getString(class_def_cfg, "server-hostname");
183
184 if (sname.length() >= Pkt4::MAX_SNAME_LEN) {
185 isc_throw(DhcpConfigError, "server-hostname must be at most "
186 << Pkt4::MAX_SNAME_LEN - 1 << " bytes long, it is "
187 << sname.length() << " ("
188 << getPosition("server-hostname", class_def_cfg) << ")");
189 }
190 }
191
192 // Let's try to parse boot-file-name
193 std::string filename;
194 if (class_def_cfg->contains("boot-file-name")) {
195 filename = getString(class_def_cfg, "boot-file-name");
196
197 if (filename.length() > Pkt4::MAX_FILE_LEN) {
198 isc_throw(DhcpConfigError, "boot-file-name must be at most "
199 << Pkt4::MAX_FILE_LEN - 1 << " bytes long, it is "
200 << filename.length() << " ("
201 << getPosition("boot-file-name", class_def_cfg) << ")");
202 }
203
204 }
205
206 // Add the client class definition
207 try {
208 class_dictionary->addClass(name, match_expr, test, required,
209 depend_on_known, options, defs,
210 user_context, next_server, sname, filename);
211 } catch (const std::exception& ex) {
212 std::ostringstream s;
213 s << "Can't add class: " << ex.what();
214 // Append position of the error in JSON string if required.
215 if (append_error_position) {
216 s << " (" << class_def_cfg->getPosition() << ")";
217 }
218 isc_throw(DhcpConfigError, s.str());
219 }
220}
221
222void
224 const uint16_t family) {
225 // Make sure that the client class definition is stored in a map.
226 if (!class_def_cfg || (class_def_cfg->getType() != Element::map)) {
227 isc_throw(DhcpConfigError, "client class definition is not a map");
228 }
229
230 // Common v4 and v6 parameters supported for the client class.
231 static std::set<std::string> supported_params = { "name",
232 "test",
233 "option-data",
234 "user-context",
235 "only-if-required" };
236
237 // The v4 client class supports additional parmeters.
238 static std::set<std::string> supported_params_v4 = { "option-def",
239 "next-server",
240 "server-hostname",
241 "boot-file-name" };
242
243 // Iterate over the specified parameters and check if they are all supported.
244 for (auto name_value_pair : class_def_cfg->mapValue()) {
245 if ((supported_params.count(name_value_pair.first) > 0) ||
246 ((family == AF_INET) && (supported_params_v4.count(name_value_pair.first) > 0))) {
247 continue;
248
249 } else {
250 isc_throw(DhcpConfigError, "unsupported client class parameter '"
251 << name_value_pair.first << "'");
252 }
253 }
254}
255
256
257// ****************** ClientClassDefListParser ************************
258
261 uint16_t family) {
263 BOOST_FOREACH(ConstElementPtr client_class_def,
264 client_class_def_list->listValue()) {
266 parser.parse(dictionary, client_class_def, family);
267 }
268 return (dictionary);
269}
270
271} // end of namespace isc::dhcp
272} // end of namespace isc
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
static size_t setListDefaults(isc::data::ConstElementPtr list, const SimpleDefaults &default_values)
Sets the default values for all entries in a list.
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:248
ClientClassDictionaryPtr parse(isc::data::ConstElementPtr class_def_list, uint16_t family)
Parse configuration entries.
Parser for a single client class definition.
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.
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:297
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.
Parser for a single option definition.
Definition: dhcp_parsers.h:228
OptionDefinitionTuple parse(isc::data::ConstElementPtr option_def)
Parses an entry that describes single option definition.
static const size_t MAX_SNAME_LEN
length of the SNAME field in DHCPv4 message
Definition: pkt4.h:45
static const size_t MAX_FILE_LEN
length of the FILE field in DHCPv4 message
Definition: pkt4.h:48
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.
Evaluation context, an interface to the expression evaluation.
Definition: eval_context.h:35
std::function< bool(const ClientClass &)> CheckDefined
Type of the check defined function.
Definition: eval_context.h:45
bool parseString(const std::string &str, ParserType type=PARSER_BOOL)
Run the parser on the string specified.
Definition: eval_context.cc:38
isc::dhcp::Expression expression
Parsed expression (output tokens are stored here)
Definition: eval_context.h:68
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:23
std::string ClientClass
Defines a single class name.
Definition: classify.h:37
boost::shared_ptr< CfgOption > CfgOptionPtr
Non-const pointer.
Definition: cfg_option.h:497
boost::shared_ptr< CfgOptionDef > CfgOptionDefPtr
Non-const pointer.
boost::shared_ptr< Expression > ExpressionPtr
Definition: token.h:30
std::pair< isc::dhcp::OptionDefinitionPtr, std::string > OptionDefinitionTuple
Definition: dhcp_parsers.h:223
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
Defines the logger used by the top-level component of kea-dhcp-ddns.