Kea 3.3.1
flex_option.h
Go to the documentation of this file.
1// Copyright (C) 2019-2026 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>
16#include <dhcp/option_vendor.h>
18#include <eval/evaluate.h>
19#include <eval/token.h>
20#include <util/str.h>
21
22#include <boost/algorithm/string/split.hpp>
23#include <boost/algorithm/string/classification.hpp>
24
25#include <map>
26#include <string>
27
28namespace isc {
29namespace flex_option {
30
38public:
39
52
57 public:
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
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
141 void setSource(bool source_is_query) {
142 source_is_query_ = source_is_query;
143 }
144
148 bool getSource() const {
149 return (source_is_query_);
150 }
151
156 void setDestination(bool dest_is_response) {
157 dest_is_response_ = dest_is_response;
158 }
159
163 bool getDestination() const {
164 return (dest_is_response_);
165 }
166
167 private:
169 uint16_t code_;
170
174
176 Action action_;
177
179 std::string text_;
180
183
186
188 bool source_is_query_;
189
191 bool dest_is_response_;
192 };
193
195 typedef boost::shared_ptr<OptionConfig> OptionConfigPtr;
196
198 typedef std::list<OptionConfigPtr> OptionConfigList;
199
201 typedef std::map<uint16_t, OptionConfigList> OptionConfigMap;
202
207 public:
214 OptionConfigPtr container);
215
217 virtual ~SubOptionConfig();
218
222 void setVendorId(uint32_t vendor_id) {
223 vendor_id_ = vendor_id;
224 }
225
229 uint32_t getVendorId() const {
230 return (vendor_id_);
231 }
232
236 uint16_t getContainerCode() const {
237 return (container_->getCode());
238 }
239
244 return (container_->getOptionDef());
245 }
246
251 return (container_->getClass());
252 }
253
258 container_action_ = action;
259 }
260
265 return (container_action_);
266 }
267
268 private:
270 OptionConfigPtr container_;
271
273 uint32_t vendor_id_;
274
276 Action container_action_;
277 };
278
280 typedef boost::shared_ptr<SubOptionConfig> SubOptionConfigPtr;
281
284 typedef std::map<uint16_t, SubOptionConfigPtr> SubOptionConfigMap;
285
288 typedef std::map<uint16_t, SubOptionConfigMap> SubOptionConfigMapMap;
289
292
295
300 return (option_config_map_);
301 }
302
307 return (sub_option_config_map_);
308 }
309
315
322 template <typename PktType>
324 PktType query, PktType response) {
325 // Handle the case where the source is the response and
326 // there is a TokenMember in an expression.
327 if (query && response && need_copy_classes_to_response_) {
328 response->copyClasses(*query);
329 }
330 for (auto const& pair : getOptionConfigMap()) {
331 for (const OptionConfigPtr& opt_cfg : pair.second) {
332 if (!opt_cfg->getDestination() && response) {
333 // Called by send but in a receive entry.
334 continue;
335 }
336 const isc::dhcp::ClientClass& client_class =
337 opt_cfg->getClass();
338 if (!client_class.empty()) {
339 if (!query->inClass(client_class)) {
340 logClass(client_class, opt_cfg->getCode());
341 continue;
342 }
343 }
344 PktType source = (opt_cfg->getSource() ? query : response);
345 PktType dest = (opt_cfg->getDestination() ? response : query);
346 if (!source || !dest) {
347 continue;
348 }
349 std::string value;
351 uint16_t code = opt_cfg->getCode();
352 isc::dhcp::OptionPtr opt = dest->getOption(code);
353 isc::dhcp::OptionDefinitionPtr def = opt_cfg->getOptionDef();
354 switch (opt_cfg->getAction()) {
355 case NONE:
356 break;
357 case ADD:
358 // Don't add if option is already there.
359 if (opt) {
360 break;
361 }
362 // Do nothing is the expression evaluates to empty.
363 value = isc::dhcp::evaluateString(*opt_cfg->getExpr(),
364 *source);
365 if (value.empty()) {
366 break;
367 }
368 // Set the value.
369 if (def) {
370 std::vector<std::string> split_vec =
371 isc::util::str::tokens(value, ",", true);
372 opt = def->optionFactory(universe, code, split_vec);
373 } else {
374 buffer.assign(value.begin(), value.end());
375 opt.reset(new isc::dhcp::Option(universe, code,
376 buffer));
377 }
378 // Add the option.
379 dest->addOption(opt);
380 logAction(ADD, code, value);
381 break;
382 case SUPERSEDE:
383 // Do nothing is the expression evaluates to empty.
384 value = isc::dhcp::evaluateString(*opt_cfg->getExpr(),
385 *source);
386 if (value.empty()) {
387 break;
388 }
389 // Set the value.
390 if (def) {
391 std::vector<std::string> split_vec =
392 isc::util::str::tokens(value, ",", true);
393 opt = def->optionFactory(universe, code,
394 split_vec);
395 } else {
396 buffer.assign(value.begin(), value.end());
397 opt.reset(new isc::dhcp::Option(universe,
398 code,
399 buffer));
400 }
401 // Remove the option if already there.
402 while (dest->getOption(code)) {
403 dest->delOption(code);
404 }
405 // Add the option.
406 dest->addOption(opt);
407 logAction(SUPERSEDE, code, value);
408 break;
409 case REMOVE:
410 // Nothing to remove if option is not present.
411 if (!opt) {
412 break;
413 }
414 // Do nothing is the expression evaluates to false.
415 if (!isc::dhcp::evaluateBool(*opt_cfg->getExpr(), *source)) {
416 break;
417 }
418 // Remove the option.
419 while (dest->getOption(code)) {
420 dest->delOption(code);
421 }
422 logAction(REMOVE, code, "");
423 break;
424 }
425 }
426 }
427 for (auto const& pair : getSubOptionConfigMap()) {
428 for (auto const& sub_pair : pair.second) {
429 const SubOptionConfigPtr& sub_cfg = sub_pair.second;
430 if (!sub_cfg->getDestination() && response) {
431 // Called by send but in a receive entry.
432 continue;
433 }
434 uint16_t sub_code = sub_cfg->getCode();
435 uint16_t opt_code = sub_cfg->getContainerCode();
436 const isc::dhcp::ClientClass& opt_class =
437 sub_cfg->getContainerClass();
438 if (!opt_class.empty()) {
439 if (!query->inClass(opt_class)) {
440 logClass(opt_class, opt_code);
441 continue;
442 }
443 }
444 const isc::dhcp::ClientClass& sub_class =
445 sub_cfg->getClass();
446 if (!sub_class.empty()) {
447 if (!query->inClass(sub_class)) {
448 logSubClass(sub_class, sub_code, opt_code);
449 continue;
450 }
451 }
452 PktType source = (sub_cfg->getSource() ? query : response);
453 PktType dest = (sub_cfg->getDestination() ? response : query);
454 if (!source || !dest) {
455 continue;
456 }
457 std::string value;
459 isc::dhcp::OptionPtr opt = dest->getOption(opt_code);
461 isc::dhcp::OptionDefinitionPtr def = sub_cfg->getOptionDef();
462 uint32_t vendor_id = sub_cfg->getVendorId();
463 switch (sub_cfg->getAction()) {
464 case NONE:
465 break;
466 case ADD:
467 // If no container and no magic add return
468 if (!opt && (sub_cfg->getContainerAction() != ADD)) {
469 break;
470 }
471 // Do nothing is the expression evaluates to empty.
472 value = isc::dhcp::evaluateString(*sub_cfg->getExpr(),
473 *source);
474 if (value.empty()) {
475 break;
476 }
477 // Check vendor id mismatch.
478 if (opt && vendor_id && !checkVendor(opt, vendor_id)) {
479 break;
480 }
481 // Don't add if sub-option is already there.
482 if (opt && opt->getOption(sub_code)) {
483 break;
484 }
485 // Set the value.
486 if (def) {
487 std::vector<std::string> split_vec =
488 isc::util::str::tokens(value, ",", true);
489 sub = def->optionFactory(universe, sub_code,
490 split_vec);
491 } else {
492 buffer.assign(value.begin(), value.end());
493 sub.reset(new isc::dhcp::Option(universe, sub_code,
494 buffer));
495 }
496 // If the container does not exist add it.
497 if (!opt) {
498 if (!vendor_id) {
499 opt.reset(new isc::dhcp::Option(universe,
500 opt_code));
501 } else {
502 opt.reset(new isc::dhcp::OptionVendor(universe,
503 vendor_id));
504 }
505 dest->addOption(opt);
506 if (vendor_id) {
507 logAction(ADD, opt_code, vendor_id);
508 } else {
509 logAction(ADD, opt_code, "");
510 }
511 }
512 // Add the sub-option.
513 opt->addOption(sub);
514 logSubAction(ADD, sub_code, opt_code, value);
515 break;
516 case SUPERSEDE:
517 // If no container and no magic add return
518 if (!opt && (sub_cfg->getContainerAction() != ADD)) {
519 break;
520 }
521 // Do nothing is the expression evaluates to empty.
522 value = isc::dhcp::evaluateString(*sub_cfg->getExpr(),
523 *source);
524 if (value.empty()) {
525 break;
526 }
527 // Check vendor id mismatch.
528 if (opt && vendor_id && !checkVendor(opt, vendor_id)) {
529 break;
530 }
531 // Set the value.
532 if (def) {
533 std::vector<std::string> split_vec =
534 isc::util::str::tokens(value, ",", true);
535 sub = def->optionFactory(universe, sub_code,
536 split_vec);
537 } else {
538 buffer.assign(value.begin(), value.end());
539 sub.reset(new isc::dhcp::Option(universe, sub_code,
540 buffer));
541 }
542 // Remove the sub-option if already there.
543 if (opt) {
544 while (opt->getOption(sub_code)) {
545 opt->delOption(sub_code);
546 }
547 }
548 // If the container does not exist add it.
549 if (!opt) {
550 if (!vendor_id) {
551 opt.reset(new isc::dhcp::Option(universe,
552 opt_code));
553 } else {
554 opt.reset(new isc::dhcp::OptionVendor(universe,
555 vendor_id));
556 }
557 dest->addOption(opt);
558 if (vendor_id) {
559 logAction(ADD, opt_code, vendor_id);
560 } else {
561 logAction(ADD, opt_code, "");
562 }
563 }
564 // Add the sub-option.
565 opt->addOption(sub);
566 logSubAction(SUPERSEDE, sub_code, opt_code, value);
567 break;
568 case REMOVE:
569 // Nothing to remove if container is not present.
570 if (!opt) {
571 break;
572 }
573 sub = opt->getOption(sub_code);
574 // Nothing to remove if sub-option is not present.
575 if (!sub) {
576 break;
577 }
578 // Do nothing is the expression evaluates to false.
579 if (!isc::dhcp::evaluateBool(*sub_cfg->getExpr(), *source)) {
580 break;
581 }
582 // Check vendor id mismatch.
583 if (opt && vendor_id && !checkVendor(opt, vendor_id)) {
584 break;
585 }
586 // Remove the sub-option.
587 while (opt->getOption(sub_code)) {
588 opt->delOption(sub_code);
589 }
590 logSubAction(REMOVE, sub_code, opt_code, "");
591 // Remove the empty container when wanted.
592 if ((sub_cfg->getContainerAction() == REMOVE) &&
593 opt->getOptions().empty()) {
594 dest->delOption(opt_code);
595 logAction(REMOVE, opt_code, "");
596 }
597 break;
598 }
599 }
600 }
601 }
602
607 static void logClass(const isc::dhcp::ClientClass &client_class,
608 uint16_t code);
609
615 static void logAction(Action action, uint16_t code,
616 const std::string& value);
617
623 static void logAction(Action action, uint16_t code, uint32_t vendor_id);
624
630 static void logSubClass(const isc::dhcp::ClientClass &client_class,
631 uint16_t code, uint16_t container_code);
632
639 static void logSubAction(Action action, uint16_t code,
640 uint16_t container_code,
641 const std::string& value);
642
647 static bool checkVendor(isc::dhcp::OptionPtr opt, uint32_t vendor_id);
648
649protected:
654 return (option_config_map_);
655 }
656
661 return (sub_option_config_map_);
662 }
663
664private:
666 static const data::SimpleKeywords OPTION_PARAMETERS;
667
669 static const data::SimpleKeywords SUB_OPTION_PARAMETERS;
670
672 OptionConfigMap option_config_map_;
673
675 SubOptionConfigMapMap sub_option_config_map_;
676
678 bool need_copy_classes_to_response_;
679
684 void parseOptionConfig(isc::data::ConstElementPtr option);
685
691 void parseSubOption(isc::data::ConstElementPtr sub_option,
692 OptionConfigPtr opt_cfg,
694
700 void parseSubOptions(isc::data::ConstElementPtr sub_options,
701 OptionConfigPtr opt_cfg,
703};
704
706typedef boost::shared_ptr<FlexOptionImpl> FlexOptionImplPtr;
707
708} // end of namespace flex_option
709} // end of namespace isc
710#endif
Defines elements for storing the names of client classes.
This class represents vendor-specific information option.
Universe
defines option universe DHCPv4 or DHCPv6
Definition option.h:90
void setDestination(bool dest_is_response)
Set destination.
void setExpr(const isc::dhcp::ExpressionPtr &expr)
Set match expression.
void setSource(bool source_is_query)
Set source.
const isc::dhcp::ExpressionPtr & getExpr() const
Get match expression.
uint16_t getCode() const
Return option code.
Definition flex_option.h:70
OptionConfig(uint16_t code, isc::dhcp::OptionDefinitionPtr def)
Constructor.
Action getAction() const
Return action.
Definition flex_option.h:91
bool getDestination() const
Get destination.
const isc::dhcp::ClientClass & getClass() const
Get client class.
void setClass(const isc::dhcp::ClientClass &class_name)
Set client class.
isc::dhcp::OptionDefinitionPtr getOptionDef() const
Return option definition.
Definition flex_option.h:77
void setText(const std::string &text)
Set textual expression.
Definition flex_option.h:98
void setAction(Action action)
Set action.
Definition flex_option.h:84
const std::string & getText() const
Get textual expression.
isc::dhcp::OptionDefinitionPtr getContainerDef() const
Return container definition.
Action getContainerAction() const
Return action on the container.
SubOptionConfig(uint16_t code, isc::dhcp::OptionDefinitionPtr def, OptionConfigPtr container)
Constructor.
uint32_t getVendorId() const
Return vendor id.
void setVendorId(uint32_t vendor_id)
Set vendor id.
const isc::dhcp::ClientClass & getContainerClass() const
Return container client class.
void setContainerAction(Action action)
Set action on the container.
uint16_t getContainerCode() const
Return container code.
void configure(isc::data::ConstElementPtr options)
Configure the Flex Option implementation.
const OptionConfigMap & getOptionConfigMap() const
Get the option config map.
boost::shared_ptr< OptionConfig > OptionConfigPtr
The type of shared pointers to option config.
std::map< uint16_t, OptionConfigList > OptionConfigMap
The type of the option config map.
std::map< uint16_t, SubOptionConfigMap > SubOptionConfigMapMap
The type of the map of sub-option config maps.
boost::shared_ptr< SubOptionConfig > SubOptionConfigPtr
The type of shared pointers to sub-option config.
static void logAction(Action action, uint16_t code, const std::string &value)
Log the action for option.
void process(isc::dhcp::Option::Universe universe, PktType query, PktType response)
Process a query / response pair.
static void logSubClass(const isc::dhcp::ClientClass &client_class, uint16_t code, uint16_t container_code)
Log the client class for sub-option.
SubOptionConfigMapMap & getMutableSubOptionConfigMap()
Get a mutable reference to the sub-option config map of maps.
OptionConfigMap & getMutableOptionConfigMap()
Get a mutable reference to the option config map.
static void logSubAction(Action action, uint16_t code, uint16_t container_code, const std::string &value)
Log the action for sub-option.
std::list< OptionConfigPtr > OptionConfigList
The type of lists of shared pointers to option config.
static bool checkVendor(isc::dhcp::OptionPtr opt, uint32_t vendor_id)
Check vendor option vendor id mismatch.
std::map< uint16_t, SubOptionConfigPtr > SubOptionConfigMap
The type of the sub-option config map.
static void logClass(const isc::dhcp::ClientClass &client_class, uint16_t code)
Log the client class for option.
const SubOptionConfigMapMap & getSubOptionConfigMap() const
Get the sub-option config map of maps.
STL class.
boost::shared_ptr< const Element > ConstElementPtr
Definition data.h:30
std::map< std::string, isc::data::Element::types > SimpleKeywords
This specifies all accepted keywords with their types.
std::string ClientClass
Defines a single class name.
Definition classify.h:45
std::string evaluateString(const Expression &expr, Pkt &pkt)
Evaluate a RPN expression for a v4 or v6 packet and return a string value.
Definition evaluate.cc:45
boost::shared_ptr< OptionDefinition > OptionDefinitionPtr
Pointer to option definition object.
boost::shared_ptr< Expression > ExpressionPtr
Definition token.h:31
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:34
std::vector< uint8_t > OptionBuffer
buffer types used in DHCP code.
Definition option.h:24
boost::shared_ptr< Option > OptionPtr
Definition option.h:37
boost::shared_ptr< FlexOptionImpl > FlexOptionImplPtr
The type of shared pointers to Flex Option implementations.
vector< string > tokens(const string &text, const string &delim, bool escape)
Split string into tokens.
Definition str.cc:52
Defines the logger used by the top-level component of kea-lfc.