Kea 3.1.1
ddns_tuning.cc
Go to the documentation of this file.
1// Copyright (C) 2022-2025 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
9#include <ddns_tuning.h>
10#include <ddns_tuning_log.h>
11#include <eval/token.h>
12#include <eval/eval_context.h>
13#include <cc/simple_parser.h>
14#include <dhcpsrv/cfgmgr.h>
15#include <dhcp/dhcp4.h>
16#include <dhcp/libdhcp++.h>
17
18using namespace isc;
19using namespace isc::data;
20using namespace isc::dhcp;
21using namespace isc::eval;
22using namespace isc::log;
23using namespace isc::util;
24using namespace std;
25
26namespace isc {
27namespace ddns_tuning {
28
31
33 if (!params) {
34 isc_throw(BadValue, "params must not be null");
35 return;
36 }
37
38 if (params->getType() != Element::map) {
39 isc_throw(BadValue, "params must be an Element::map");
40 return;
41 }
42
43 // Discard cached expressions, some or all may be stale.
44 flushCache(false);
45
46 // Handle global hostname-expr if it exists.
47 ConstElementPtr json = params->get("hostname-expr");
48 if (json) {
49 if (json->getType() != Element::string) {
50 isc_throw(BadValue, "'hostname-expr' must be a string");
51 }
52
53 std::string expression_str = json->stringValue();
54 if (!expression_str.empty()) {
55 // Parse and cache the expression.
56 try {
57 ExpressionPtr global_expr = parseExpression(expression_str);
58 setGlobalHostnameExpression(global_expr);
60 } catch (const std::exception& ex) {
61 isc_throw(BadValue, "global expression parsing failed: " << ex.what());
62 }
63 }
64 }
65}
66
67void
69 subnet_exprs_.cacheExpression(subnet_id, expression);
70}
71
74 ExpressionPtr expression;
75 static_cast<void>(subnet_exprs_.findExpression(subnet_id, expression));
76 return (expression);
77}
78
83
85 setHostnameExpression(SUBNET_ID_GLOBAL, expression);
86}
87
90 if (!subnet) {
91 isc_throw(Unexpected, "fetchScopedHostnameExpression: subnet cannot be empty");
92 }
93
94 ExpressionPtr expression;
95 bool found = false;
96
97 // If the subnet has been modified since we last flushed, then we need to flush the
98 // cache and return not found. This will force us to update the subnet's expression.
99 if (subnet->getModificationTime() > subnet_exprs_.getLastFlushTime()) {
100 flushCache();
101 } else {
102 // Subnet hasn't changed since last flush, so fetch its cached expression (if one).
103 found = subnet_exprs_.findExpression(subnet->getID(), expression);
104 }
105
106 // If there's no entry for the subnet, cache an entry for it.
107 if (!found) {
108 expression = cacheExpression(subnet);
109 }
110
111 // We found an expression for the subnet return it. It is possible for the expression
112 // to be present but empty. We return empty expressions to allow subnets to suppress
113 // a global level expression.
114 if (expression) {
116 .arg(subnet->toText());
117 return (expression);
118 }
119
120 // Return the global expression (may be empty);
122}
123
125 ExpressionPtr expression;
126
127 // Check if there's a subnet-specific expression defined in the
128 // user context. If it's there parse it, cache it, and return it.
129 auto ctx = subnet->getContext();
130 if (ctx) {
131 // Look for ddns-tuning map within user-context.
132 ConstElementPtr json = ctx->get("ddns-tuning");
133
134 // If it's there look for hostname-expr within ddns-tuning.
135 if (json && json->getType() == Element::map) {
136 json = json->get("hostname-expr");
137 if (json && json->getType() == Element::string) {
138 try {
139 // Get either the parsed expression specified by the subnet via
140 // user-context::hostname-expr or an empty pointer.
143 .arg(json->stringValue())
144 .arg(subnet->toText());
145 expression = parseExpression(json->stringValue());
146 } catch (const std::exception& ex) {
147 // Cache an empty pointer so we don't retry each time the subnet is presented.
148 setHostnameExpression(subnet->getID(), expression);
149 isc_throw(BadValue, "hostname expression for subnet: " << subnet->toText()
150 << " parsing failed: " << ex.what());
151 }
152 }
153 }
154 }
155
156 // Cache whatever we have, an expression or an empty pointer.
157 setHostnameExpression(subnet->getID(), expression);
158 return (expression);
159}
160
162DdnsTuningImpl::parseExpression(const std::string& expression_str) const {
163 ExpressionPtr expr;
164 try {
165 // We allow empty expressions so higher precedence scopes may
166 // override lower precedence scopes.
167 if (expression_str.empty()) {
168 expr.reset(new Expression());
169 } else {
170 EvalContext eval_ctx(family_ == AF_INET ? Option::V4 : Option::V6);
171 eval_ctx.parseString(expression_str, EvalContext::PARSER_STRING);
172 expr.reset(new Expression(eval_ctx.expression_));
173 }
174 } catch (const std::exception& ex) {
175 isc_throw(BadValue, "error parsing expression: ["
176 << expression_str << "] : " << ex.what());
177 }
178
179 return (expr);
180}
181
182std::string
184 // Look for a hostname expression first by subnet, then globally.
185 auto hostname_expr = fetchScopedHostnameExpression(subnet);
186
187 // If there isn't an expression or the expression is empty
188 // return an empty string.
189 if (!hostname_expr || (hostname_expr->empty())) {
190 return (std::string(""));
191 }
192
193 // We have an expression use it to calculate the hostname.
194 return (isc::dhcp::evaluateString(*hostname_expr, *query));
195}
196
197void
198DdnsTuningImpl::flushCache(bool preserve_global /* = true */) {
199 ExpressionPtr global_expr;
200 if (preserve_global) {
201 global_expr = getGlobalHostnameExpression();
202 }
203
204 subnet_exprs_.clear();
205 if (global_expr) {
206 setGlobalHostnameExpression(global_expr);
207 }
208}
209
210size_t
212 return (subnet_exprs_.size());
213}
214
215boost::posix_time::ptime
217 return (subnet_exprs_.getLastFlushTime());
218}
219
220} // end of namespace ddns_tuning
221} // end of namespace isc
@ map
Definition data.h:147
@ string
Definition data.h:144
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
A generic exception that is thrown when an unexpected error condition occurs.
void configure(isc::data::ConstElementPtr params)
Configure the Ddns Tuning implementation.
dhcp::ExpressionPtr fetchScopedHostnameExpression(dhcp::ConstSubnetPtr subnet)
Fetches the expression that is in scope for the given subnet.
void flushCache(bool preserve_global=true)
Flushes the cache contents.
ExpressionCache subnet_exprs_
Per subnet expression cache.
dhcp::ExpressionPtr getHostnameExpression(const dhcp::SubnetID &subnet_id)
Fetches the hostname for a given subnet_id.
void setHostnameExpression(const dhcp::SubnetID &subnet_id, dhcp::ExpressionPtr &expression)
Set the hostname expression for a given subnet.
dhcp::ExpressionPtr parseExpression(const std::string &expression_str) const
Parses an expression string into an Expression.
boost::posix_time::ptime getLastFlushTime()
Fetches the time (in seconds) of when the cache was last flushed.
void setGlobalHostnameExpression(dhcp::ExpressionPtr &expression)
Set the global hostname expression.
dhcp::ExpressionPtr getGlobalHostnameExpression()
Get the global hostname expression.
std::string calculateHostname(dhcp::PktPtr query, dhcp::ConstSubnetPtr subnet)
Calculate the hostname for a packet given a subnet.
dhcp::ExpressionPtr cacheExpression(dhcp::ConstSubnetPtr subnet)
Caches an expression entry for a given subnet.
size_t getCacheSize()
Fetches the number of entries in the cache.
Evaluation context, an interface to the expression evaluation.
bool parseString(const std::string &str, ParserType type=PARSER_BOOL)
Run the parser on the string specified.
@ PARSER_STRING
expression is expected to evaluate to string
isc::dhcp::Expression expression_
Parsed expression (output tokens are stored here)
const isc::log::MessageID DDNS_TUNING_GLOBAL_EXPR_SET
const isc::log::MessageID DDNS_TUNING_SUBNET_EXPRESSION_PARSE
const isc::log::MessageID DDNS_TUNING_SUBNET_EXPR_CACHED
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
#define LOG_INFO(LOGGER, MESSAGE)
Macro to conveniently test info output and log it.
Definition macros.h:20
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
Definition macros.h:14
boost::shared_ptr< const Element > ConstElementPtr
Definition data.h:29
isc::log::Logger ddns_tuning_logger("ddns-tuning-hooks")
boost::shared_ptr< isc::dhcp::Pkt > PktPtr
A pointer to either Pkt4 or Pkt6 packet.
Definition pkt.h:999
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< Expression > ExpressionPtr
Definition token.h:31
uint32_t SubnetID
Defines unique IPv4 or IPv6 subnet identifier.
Definition subnet_id.h:25
boost::shared_ptr< const Subnet > ConstSubnetPtr
A generic pointer to either const Subnet4 or const Subnet6 object.
Definition subnet.h:452
std::vector< TokenPtr > Expression
This is a structure that holds an expression converted to RPN.
Definition token.h:29
const int DBGLVL_TRACE_BASIC
Trace basic operations.
Defines the logger used by the top-level component of kea-lfc.