Kea  2.3.5-git
flex_option.h
Go to the documentation of this file.
1 // Copyright (C) 2019-2022 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 #ifndef FLEX_OPTION_H
8 #define FLEX_OPTION_H
9 
10 #include <cc/data.h>
11 #include <cc/simple_parser.h>
12 #include <dhcp/classify.h>
13 #include <dhcp/libdhcp++.h>
14 #include <dhcp/option.h>
15 #include <dhcp/option_definition.h>
16 #include <dhcp/option_vendor.h>
17 #include <dhcp/std_option_defs.h>
18 #include <eval/evaluate.h>
19 #include <eval/token.h>
20 #include <util/strutil.h>
21 
22 #include <boost/algorithm/string/split.hpp>
23 #include <boost/algorithm/string/classification.hpp>
24 
25 #include <map>
26 #include <string>
27 
28 namespace isc {
29 namespace flex_option {
30 
38 public:
39 
46  enum Action {
48  ADD,
51  };
52 
56  class OptionConfig {
57  public:
62  OptionConfig(uint16_t code, isc::dhcp::OptionDefinitionPtr def);
63 
65  virtual ~OptionConfig();
66 
70  uint16_t getCode() const {
71  return (code_);
72  }
73 
78  return (def_);
79  }
80 
84  void setAction(Action action) {
85  action_ = action;
86  }
87 
91  Action getAction() const {
92  return (action_);
93  }
94 
98  void setText(const std::string& text) {
99  text_ = text;
100  };
101 
105  const std::string& getText() const {
106  return (text_);
107  }
108 
112  void setExpr(const isc::dhcp::ExpressionPtr& expr) {
113  expr_ = expr;
114  }
115 
120  return (expr_);
121  }
122 
126  void setClass(const isc::dhcp::ClientClass& class_name) {
127  class_ = class_name;
128  }
129 
134  return (class_);
135  }
136 
137  private:
139  uint16_t code_;
140 
144 
146  Action action_;
147 
149  std::string text_;
150 
153 
155  isc::dhcp::ClientClass class_;
156  };
157 
159  typedef boost::shared_ptr<OptionConfig> OptionConfigPtr;
160 
162  typedef std::list<OptionConfigPtr> OptionConfigList;
163 
165  typedef std::map<uint16_t, OptionConfigList> OptionConfigMap;
166 
170  class SubOptionConfig : public OptionConfig {
171  public:
178  OptionConfigPtr container);
179 
181  virtual ~SubOptionConfig();
182 
186  void setVendorId(uint32_t vendor_id) {
187  vendor_id_ = vendor_id;
188  }
189 
193  uint32_t getVendorId() const {
194  return (vendor_id_);
195  }
196 
200  uint16_t getContainerCode() const {
201  return (container_->getCode());
202  }
203 
208  return (container_->getOptionDef());
209  }
210 
215  return (container_->getClass());
216  }
217 
221  void setContainerAction(Action action) {
222  container_action_ = action;
223  }
224 
229  return (container_action_);
230  }
231 
232  private:
234  OptionConfigPtr container_;
235 
237  uint32_t vendor_id_;
238 
240  Action container_action_;
241  };
242 
244  typedef boost::shared_ptr<SubOptionConfig> SubOptionConfigPtr;
245 
248  typedef std::map<uint16_t, SubOptionConfigPtr> SubOptionConfigMap;
249 
252  typedef std::map<uint16_t, SubOptionConfigMap> SubOptionConfigMapMap;
253 
255  FlexOptionImpl();
256 
258  ~FlexOptionImpl();
259 
263  const OptionConfigMap& getOptionConfigMap() const {
264  return (option_config_map_);
265  }
266 
270  const SubOptionConfigMapMap& getSubOptionConfigMap() const {
271  return (sub_option_config_map_);
272  }
273 
279 
286  template <typename PktType>
288  PktType query, PktType response) {
289  for (auto pair : getOptionConfigMap()) {
290  for (const OptionConfigPtr& opt_cfg : pair.second) {
291  const isc::dhcp::ClientClass& client_class =
292  opt_cfg->getClass();
293  if (!client_class.empty()) {
294  if (!query->inClass(client_class)) {
295  logClass(client_class, opt_cfg->getCode());
296  continue;
297  }
298  }
299  std::string value;
301  uint16_t code = opt_cfg->getCode();
302  isc::dhcp::OptionPtr opt = response->getOption(code);
303  isc::dhcp::OptionDefinitionPtr def = opt_cfg->getOptionDef();
304  switch (opt_cfg->getAction()) {
305  case NONE:
306  break;
307  case ADD:
308  // Don't add if option is already there.
309  if (opt) {
310  break;
311  }
312  // Do nothing is the expression evaluates to empty.
313  value = isc::dhcp::evaluateString(*opt_cfg->getExpr(),
314  *query);
315  if (value.empty()) {
316  break;
317  }
318  // Set the value.
319  if (def) {
320  std::vector<std::string> split_vec =
321  isc::util::str::tokens(value, ",", true);
322  opt = def->optionFactory(universe, code, split_vec);
323  } else {
324  buffer.assign(value.begin(), value.end());
325  opt.reset(new isc::dhcp::Option(universe, code,
326  buffer));
327  }
328  // Add the option.
329  response->addOption(opt);
330  logAction(ADD, code, value);
331  break;
332  case SUPERSEDE:
333  // Do nothing is the expression evaluates to empty.
334  value = isc::dhcp::evaluateString(*opt_cfg->getExpr(),
335  *query);
336  if (value.empty()) {
337  break;
338  }
339  // Set the value.
340  if (def) {
341  std::vector<std::string> split_vec =
342  isc::util::str::tokens(value, ",", true);
343  opt = def->optionFactory(universe, code,
344  split_vec);
345  } else {
346  buffer.assign(value.begin(), value.end());
347  opt.reset(new isc::dhcp::Option(universe,
348  code,
349  buffer));
350  }
351  // Remove the option if already there.
352  while (response->getOption(code)) {
353  response->delOption(code);
354  }
355  // Add the option.
356  response->addOption(opt);
357  logAction(SUPERSEDE, code, value);
358  break;
359  case REMOVE:
360  // Nothing to remove if option is not present.
361  if (!opt) {
362  break;
363  }
364  // Do nothing is the expression evaluates to false.
365  if (!isc::dhcp::evaluateBool(*opt_cfg->getExpr(), *query)) {
366  break;
367  }
368  // Remove the option.
369  while (response->getOption(code)) {
370  response->delOption(code);
371  }
372  logAction(REMOVE, code, "");
373  break;
374  }
375  }
376  }
377  for (auto pair : getSubOptionConfigMap()) {
378  for (auto sub_pair : pair.second) {
379  const SubOptionConfigPtr& sub_cfg = sub_pair.second;
380  uint16_t sub_code = sub_cfg->getCode();
381  uint16_t opt_code = sub_cfg->getContainerCode();
382  const isc::dhcp::ClientClass& opt_class =
383  sub_cfg->getContainerClass();
384  if (!opt_class.empty()) {
385  if (!query->inClass(opt_class)) {
386  logClass(opt_class, opt_code);
387  continue;
388  }
389  }
390  const isc::dhcp::ClientClass& sub_class =
391  sub_cfg->getClass();
392  if (!sub_class.empty()) {
393  if (!query->inClass(sub_class)) {
394  logSubClass(sub_class, sub_code, opt_code);
395  continue;
396  }
397  }
398  std::string value;
400  isc::dhcp::OptionPtr opt = response->getOption(opt_code);
402  isc::dhcp::OptionDefinitionPtr def = sub_cfg->getOptionDef();
403  uint32_t vendor_id = sub_cfg->getVendorId();
404  switch (sub_cfg->getAction()) {
405  case NONE:
406  break;
407  case ADD:
408  // If no container and no magic add return
409  if (!opt && (sub_cfg->getContainerAction() != ADD)) {
410  break;
411  }
412  // Do nothing is the expression evaluates to empty.
413  value = isc::dhcp::evaluateString(*sub_cfg->getExpr(),
414  *query);
415  if (value.empty()) {
416  break;
417  }
418  // Check vendor id mismatch.
419  if (opt && vendor_id && !checkVendor(opt, vendor_id)) {
420  break;
421  }
422  // Don't add if sub-option is already there.
423  if (opt && opt->getOption(sub_code)) {
424  break;
425  }
426  // Set the value.
427  if (def) {
428  std::vector<std::string> split_vec =
429  isc::util::str::tokens(value, ",", true);
430  sub = def->optionFactory(universe, sub_code,
431  split_vec);
432  } else {
433  buffer.assign(value.begin(), value.end());
434  sub.reset(new isc::dhcp::Option(universe, sub_code,
435  buffer));
436  }
437  // If the container does not exist add it.
438  if (!opt) {
439  if (!vendor_id) {
440  opt.reset(new isc::dhcp::Option(universe,
441  opt_code));
442  } else {
443  opt.reset(new isc::dhcp::OptionVendor(universe,
444  vendor_id));
445  }
446  response->addOption(opt);
447  if (vendor_id) {
448  logAction(ADD, opt_code, vendor_id);
449  } else {
450  logAction(ADD, opt_code, "");
451  }
452  }
453  // Add the sub-option.
454  opt->addOption(sub);
455  logSubAction(ADD, sub_code, opt_code, value);
456  break;
457  case SUPERSEDE:
458  // If no container and no magic add return
459  if (!opt && (sub_cfg->getContainerAction() != ADD)) {
460  break;
461  }
462  // Do nothing is the expression evaluates to empty.
463  value = isc::dhcp::evaluateString(*sub_cfg->getExpr(),
464  *query);
465  if (value.empty()) {
466  break;
467  }
468  // Check vendor id mismatch.
469  if (opt && vendor_id && !checkVendor(opt, vendor_id)) {
470  break;
471  }
472  // Set the value.
473  if (def) {
474  std::vector<std::string> split_vec =
475  isc::util::str::tokens(value, ",", true);
476  sub = def->optionFactory(universe, sub_code,
477  split_vec);
478  } else {
479  buffer.assign(value.begin(), value.end());
480  sub.reset(new isc::dhcp::Option(universe, sub_code,
481  buffer));
482  }
483  // Remove the sub-option if already there.
484  if (opt) {
485  while (opt->getOption(sub_code)) {
486  opt->delOption(sub_code);
487  }
488  }
489  // If the container does not exist add it.
490  if (!opt) {
491  if (!vendor_id) {
492  opt.reset(new isc::dhcp::Option(universe,
493  opt_code));
494  } else {
495  opt.reset(new isc::dhcp::OptionVendor(universe,
496  vendor_id));
497  }
498  response->addOption(opt);
499  if (vendor_id) {
500  logAction(ADD, opt_code, vendor_id);
501  } else {
502  logAction(ADD, opt_code, "");
503  }
504  }
505  // Add the sub-option.
506  opt->addOption(sub);
507  logSubAction(SUPERSEDE, sub_code, opt_code, value);
508  break;
509  case REMOVE:
510  // Nothing to remove if container is not present.
511  if (!opt) {
512  break;
513  }
514  sub = opt->getOption(sub_code);
515  // Nothing to remove if sub-option is not present.
516  if (!sub) {
517  break;
518  }
519  // Do nothing is the expression evaluates to false.
520  if (!isc::dhcp::evaluateBool(*sub_cfg->getExpr(), *query)) {
521  break;
522  }
523  // Check vendor id mismatch.
524  if (opt && vendor_id && !checkVendor(opt, vendor_id)) {
525  break;
526  }
527  // Remove the sub-option.
528  while (opt->getOption(sub_code)) {
529  opt->delOption(sub_code);
530  }
531  logSubAction(REMOVE, sub_code, opt_code, "");
532  // Remove the empty container when wanted.
533  if ((sub_cfg->getContainerAction() == REMOVE) &&
534  opt->getOptions().empty()) {
535  response->delOption(opt_code);
536  logAction(REMOVE, opt_code, "");
537  }
538  break;
539  }
540  }
541  }
542  }
543 
544 
549  static void logClass(const isc::dhcp::ClientClass &client_class,
550  uint16_t code);
551 
557  static void logAction(Action action, uint16_t code,
558  const std::string& value);
559 
565  static void logAction(Action action, uint16_t code, uint32_t vendor_id);
566 
572  static void logSubClass(const isc::dhcp::ClientClass &client_class,
573  uint16_t code, uint16_t container_code);
574 
581  static void logSubAction(Action action, uint16_t code,
582  uint16_t container_code,
583  const std::string& value);
584 
589  static bool checkVendor(isc::dhcp::OptionPtr opt, uint32_t vendor_id);
590 
591 protected:
595  OptionConfigMap& getMutableOptionConfigMap() {
596  return (option_config_map_);
597  }
598 
602  SubOptionConfigMapMap& getMutableSubOptionConfigMap() {
603  return (sub_option_config_map_);
604  }
605 
606 private:
608  static const data::SimpleKeywords OPTION_PARAMETERS;
609 
611  static const data::SimpleKeywords SUB_OPTION_PARAMETERS;
612 
614  OptionConfigMap option_config_map_;
615 
617  SubOptionConfigMapMap sub_option_config_map_;
618 
623  void parseOptionConfig(isc::data::ConstElementPtr option);
624 
630  void parseSubOption(isc::data::ConstElementPtr sub_option,
631  OptionConfigPtr opt_cfg,
632  isc::dhcp::Option::Universe universe);
633 
639  void parseSubOptions(isc::data::ConstElementPtr sub_options,
640  OptionConfigPtr opt_cfg,
641  isc::dhcp::Option::Universe universe);
642 };
643 
645 typedef boost::shared_ptr<FlexOptionImpl> FlexOptionImplPtr;
646 
647 } // end of namespace flex_option
648 } // end of namespace isc
649 #endif
boost::shared_ptr< FlexOptionImpl > FlexOptionImplPtr
The type of shared pointers to Flex Option implementations.
Definition: flex_option.h:645
uint32_t getVendorId() const
Return vendor id.
Definition: flex_option.h:193
const isc::dhcp::ClientClass & getClass() const
Get client class.
Definition: flex_option.h:133
Action getContainerAction() const
Return action on the container.
Definition: flex_option.h:228
const isc::dhcp::ExpressionPtr & getExpr() const
Get match expression.
Definition: flex_option.h:119
std::map< std::string, isc::data::Element::types > SimpleKeywords
This specifies all accepted keywords with their types.
std::string evaluateString(const Expression &expr, Pkt &pkt)
Definition: evaluate.cc:28
static bool checkVendor(isc::dhcp::OptionPtr opt, uint32_t vendor_id)
Check vendor option vendor id mismatch.
Definition: flex_option.cc:598
OptionConfig(uint16_t code, isc::dhcp::OptionDefinitionPtr def)
Constructor.
Definition: flex_option.cc:104
void setAction(Action action)
Set action.
Definition: flex_option.h:84
Defines elements for storing the names of client classes.
void setContainerAction(Action action)
Set action on the container.
Definition: flex_option.h:221
boost::shared_ptr< Option > OptionPtr
Definition: option.h:36
Universe
defines option universe DHCPv4 or DHCPv6
Definition: option.h:83
void setText(const std::string &text)
Set textual expression.
Definition: flex_option.h:98
const OptionConfigMap & getOptionConfigMap() const
Get the option config map.
Definition: flex_option.h:263
boost::shared_ptr< OptionConfig > OptionConfigPtr
The type of shared pointers to option config.
Definition: flex_option.h:159
std::map< uint16_t, SubOptionConfigPtr > SubOptionConfigMap
The type of the sub-option config map.
Definition: flex_option.h:248
void process(isc::dhcp::Option::Universe universe, PktType query, PktType response)
Process a query / response pair.
Definition: flex_option.h:287
const SubOptionConfigMapMap & getSubOptionConfigMap() const
Get the sub-option config map of maps.
Definition: flex_option.h:270
std::vector< uint8_t > OptionBuffer
buffer types used in DHCP code.
Definition: option.h:24
SubOptionConfigMapMap & getMutableSubOptionConfigMap()
Get a mutable reference to the sub-option config map of maps.
Definition: flex_option.h:602
std::map< uint16_t, SubOptionConfigMap > SubOptionConfigMapMap
The type of the map of sub-option config maps.
Definition: flex_option.h:252
void configure(isc::data::ConstElementPtr options)
Configure the Flex Option implementation.
Definition: flex_option.cc:134
vector< string > tokens(const std::string &text, const std::string &delim, bool escape)
Split String into Tokens.
Definition: strutil.cc:77
Action getAction() const
Return action.
Definition: flex_option.h:91
boost::shared_ptr< const Element > ConstElementPtr
Definition: data.h:27
uint16_t getCode() const
Return option code.
Definition: flex_option.h:70
std::map< uint16_t, OptionConfigList > OptionConfigMap
The type of the option config map.
Definition: flex_option.h:165
void setClass(const isc::dhcp::ClientClass &class_name)
Set client class.
Definition: flex_option.h:126
void setExpr(const isc::dhcp::ExpressionPtr &expr)
Set match expression.
Definition: flex_option.h:112
static void logAction(Action action, uint16_t code, const std::string &value)
Log the action for option.
Definition: flex_option.cc:499
const isc::dhcp::ClientClass & getContainerClass() const
Return container client class.
Definition: flex_option.h:214
isc::dhcp::OptionDefinitionPtr getOptionDef() const
Return option definition.
Definition: flex_option.h:77
bool evaluateBool(const Expression &expr, Pkt &pkt)
Evaluate a RPN expression for a v4 or v6 packet and return a true or false decision.
Definition: evaluate.cc:14
Defines the logger used by the top-level component of kea-lfc.
std::list< OptionConfigPtr > OptionConfigList
The type of lists of shared pointers to option config.
Definition: flex_option.h:162
static void logSubClass(const isc::dhcp::ClientClass &client_class, uint16_t code, uint16_t container_code)
Log the client class for sub-option.
Definition: flex_option.cc:549
uint16_t getContainerCode() const
Return container code.
Definition: flex_option.h:200
OptionConfigMap & getMutableOptionConfigMap()
Get a mutable reference to the option config map.
Definition: flex_option.h:595
boost::shared_ptr< SubOptionConfig > SubOptionConfigPtr
The type of shared pointers to sub-option config.
Definition: flex_option.h:244
static void logSubAction(Action action, uint16_t code, uint16_t container_code, const std::string &value)
Log the action for sub-option.
Definition: flex_option.cc:560
isc::dhcp::OptionDefinitionPtr getContainerDef() const
Return container definition.
Definition: flex_option.h:207
Flex Option implementation.
Definition: flex_option.h:37
boost::shared_ptr< OptionDefinition > OptionDefinitionPtr
Pointer to option definition object.
static void logClass(const isc::dhcp::ClientClass &client_class, uint16_t code)
Log the client class for option.
Definition: flex_option.cc:490
std::string ClientClass
Defines a single class name.
Definition: classify.h:42
const std::string & getText() const
Get textual expression.
Definition: flex_option.h:105
boost::shared_ptr< Expression > ExpressionPtr
Definition: token.h:30
This class represents vendor-specific information option.
Definition: option_vendor.h:30
void setVendorId(uint32_t vendor_id)
Set vendor id.
Definition: flex_option.h:186