1 // Copyright (C) 2019-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 <dhcpsrv/triplet.h>
9 #include <dhcpsrv/parsers/base_network_parser.h>
10 #include <util/optional.h>
11 #include <util/strutil.h>
12 
13 using namespace isc::data;
14 using namespace isc::util;
15 
16 namespace isc {
17 namespace dhcp {
18 
19 void
moveReservationMode(ElementPtr config)20 BaseNetworkParser::moveReservationMode(ElementPtr config) {
21     if (!config->contains("reservation-mode")) {
22         return;
23     }
24     if (config->contains("reservations-global") ||
25         config->contains("reservations-in-subnet") ||
26         config->contains("reservations-out-of-pool")) {
27         isc_throw(DhcpConfigError, "invalid use of both 'reservation-mode'"
28                   " and one of 'reservations-global', 'reservations-in-subnet'"
29                   " or 'reservations-out-of-pool' parameters");
30     }
31     std::string hr_mode = getString(config, "reservation-mode");
32     if ((hr_mode == "disabled") || (hr_mode == "off")) {
33         config->set("reservations-global", Element::create(false));
34         config->set("reservations-in-subnet", Element::create(false));
35     } else if (hr_mode == "out-of-pool") {
36         config->set("reservations-global", Element::create(false));
37         config->set("reservations-in-subnet", Element::create(true));
38         config->set("reservations-out-of-pool", Element::create(true));
39     } else if (hr_mode == "global") {
40         config->set("reservations-global", Element::create(true));
41         config->set("reservations-in-subnet", Element::create(false));
42     } else if (hr_mode == "all") {
43         config->set("reservations-global", Element::create(false));
44         config->set("reservations-in-subnet", Element::create(true));
45         config->set("reservations-out-of-pool", Element::create(false));
46     } else {
47         isc_throw(DhcpConfigError, "invalid reservation-mode parameter: '"
48                   << hr_mode << "' ("
49                   << getPosition("reservation-mode", config) << ")");
50     }
51     config->remove("reservation-mode");
52 }
53 
54 void
parseCommon(const ConstElementPtr & network_data,NetworkPtr & network)55 BaseNetworkParser::parseCommon(const ConstElementPtr& network_data,
56                                NetworkPtr& network) {
57     bool has_renew = network_data->contains("renew-timer");
58     bool has_rebind = network_data->contains("rebind-timer");
59     int64_t renew = -1;
60     int64_t rebind = -1;
61 
62     if (has_renew) {
63         renew = getInteger(network_data, "renew-timer");
64         if (renew < 0) {
65             isc_throw(DhcpConfigError, "the value of renew-timer ("
66                       << renew << ") must be a positive number");
67         }
68         network->setT1(renew);
69     }
70 
71     if (has_rebind) {
72         rebind = getInteger(network_data, "rebind-timer");
73         if (rebind < 0) {
74             isc_throw(DhcpConfigError, "the value of rebind-timer ("
75                       << rebind << ") must be a positive number");
76         }
77         network->setT2(rebind);
78     }
79 
80     if (has_renew && has_rebind && (renew > rebind)) {
81         isc_throw(DhcpConfigError, "the value of renew-timer (" << renew
82                   << ") is greater than the value of rebind-timer ("
83                   << rebind << ")");
84     }
85 
86     network->setValid(parseIntTriplet(network_data, "valid-lifetime"));
87 
88     if (network_data->contains("store-extended-info")) {
89         network->setStoreExtendedInfo(getBoolean(network_data,
90                                                  "store-extended-info"));
91     }
92 
93     if (network_data->contains("reservations-global")) {
94         network->setReservationsGlobal(getBoolean(network_data,
95                                                   "reservations-global"));
96     }
97 
98     if (network_data->contains("reservations-in-subnet")) {
99         network->setReservationsInSubnet(getBoolean(network_data,
100                                                     "reservations-in-subnet"));
101     }
102 
103     if (network_data->contains("reservations-out-of-pool")) {
104         network->setReservationsOutOfPool(getBoolean(network_data,
105                                                      "reservations-out-of-pool"));
106     }
107 }
108 
109 void
parseTeePercents(const ConstElementPtr & network_data,NetworkPtr & network)110 BaseNetworkParser::parseTeePercents(const ConstElementPtr& network_data,
111                                     NetworkPtr& network) {
112     bool calculate_tee_times = network->getCalculateTeeTimes();
113     if (network_data->contains("calculate-tee-times")) {
114         calculate_tee_times = getBoolean(network_data, "calculate-tee-times");
115         network->setCalculateTeeTimes(calculate_tee_times);
116     }
117 
118     Optional<double> t2_percent;
119     if (network_data->contains("t2-percent")) {
120         t2_percent = getDouble(network_data, "t2-percent");
121     }
122 
123     Optional<double> t1_percent;
124     if (network_data->contains("t1-percent")) {
125         t1_percent = getDouble(network_data, "t1-percent");
126     }
127     if (calculate_tee_times) {
128         if (!t2_percent.unspecified() && ((t2_percent.get() <= 0.0) ||
129                                           (t2_percent.get() >= 1.0))) {
130             isc_throw(DhcpConfigError, "t2-percent:  " << t2_percent.get()
131                       << " is invalid, it must be greater than 0.0 and less than 1.0");
132         }
133 
134         if (!t1_percent.unspecified() && ((t1_percent.get() <= 0.0) ||
135                                           (t1_percent.get() >= 1.0))) {
136             isc_throw(DhcpConfigError, "t1-percent:  " << t1_percent.get()
137                       << " is invalid it must be greater than 0.0 and less than 1.0");
138         }
139 
140         if (!t1_percent.unspecified() && !t2_percent.unspecified() &&
141             (t1_percent.get() >= t2_percent.get())) {
142             isc_throw(DhcpConfigError, "t1-percent:  " << t1_percent.get()
143                       << " is invalid, it must be less than t2-percent: "
144                       << t2_percent.get());
145         }
146     }
147 
148     network->setT2Percent(t2_percent);
149     network->setT1Percent(t1_percent);
150 }
151 
152 void
parseCacheParams(const ConstElementPtr & network_data,NetworkPtr & network)153 BaseNetworkParser::parseCacheParams(const ConstElementPtr& network_data,
154                                     NetworkPtr& network) {
155     if (network_data->contains("cache-threshold")) {
156         double cache_threshold = getDouble(network_data, "cache-threshold");
157         if ((cache_threshold <= 0.0) || (cache_threshold >= 1.0)) {
158             isc_throw(DhcpConfigError, "cache-threshold: " << cache_threshold
159                       << " is invalid, it must be greater than 0.0 and less than 1.0");
160         }
161         network->setCacheThreshold(cache_threshold);
162     }
163 
164     if (network_data->contains("cache-max-age")) {
165         network->setCacheMaxAge(getInteger(network_data, "cache-max-age"));
166     }
167 }
168 
169 void
parseDdnsParams(const data::ConstElementPtr & network_data,NetworkPtr & network)170 BaseNetworkParser::parseDdnsParams(const data::ConstElementPtr& network_data,
171                                    NetworkPtr& network) {
172 
173     if (network_data->contains("ddns-send-updates")) {
174         network->setDdnsSendUpdates(getBoolean(network_data, "ddns-send-updates"));
175     }
176 
177     if (network_data->contains("ddns-override-no-update")) {
178         network->setDdnsOverrideNoUpdate(getBoolean(network_data, "ddns-override-no-update"));
179     }
180 
181     if (network_data->contains("ddns-override-client-update")) {
182         network->setDdnsOverrideClientUpdate(getBoolean(network_data, "ddns-override-client-update"));
183     }
184 
185     if (network_data->contains("ddns-replace-client-name")) {
186         network->setDdnsReplaceClientNameMode(getAndConvert<D2ClientConfig::ReplaceClientNameMode,
187                                                             D2ClientConfig::stringToReplaceClientNameMode>
188                                                             (network_data, "ddns-replace-client-name",
189                                                              "ReplaceClientName mode"));
190     }
191 
192     if (network_data->contains("ddns-generated-prefix")) {
193         network->setDdnsGeneratedPrefix(getString(network_data, "ddns-generated-prefix"));
194     }
195 
196     if (network_data->contains("ddns-qualifying-suffix")) {
197         network->setDdnsQualifyingSuffix(getString(network_data, "ddns-qualifying-suffix"));
198     }
199 
200     std::string hostname_char_set;
201     if (network_data->contains("hostname-char-set")) {
202         hostname_char_set = getString(network_data, "hostname-char-set");
203         network->setHostnameCharSet(hostname_char_set);
204     }
205 
206     std::string hostname_char_replacement;
207     if (network_data->contains("hostname-char-replacement")) {
208         hostname_char_replacement = getString(network_data, "hostname-char-replacement");
209         network->setHostnameCharReplacement(hostname_char_replacement);
210     }
211 
212     // We need to validate sanitizer values here so we can detect problems and
213     // cause a configuration.  We don't retain the compilation because it's not
214     // something we can inherit.
215     if (!hostname_char_set.empty()) {
216         try {
217             str::StringSanitizerPtr sanitizer(new str::StringSanitizer(hostname_char_set,
218                                                                        hostname_char_replacement));
219         } catch (const std::exception& ex) {
220             isc_throw(BadValue, "hostname-char-set '" << hostname_char_set
221                       << "' is not a valid regular expression");
222         }
223     }
224 
225     if (network_data->contains("ddns-update-on-renew")) {
226         network->setDdnsUpdateOnRenew(getBoolean(network_data, "ddns-update-on-renew"));
227     }
228 
229     if (network_data->contains("ddns-use-conflict-resolution")) {
230         network->setDdnsUseConflictResolution(getBoolean(network_data, "ddns-use-conflict-resolution"));
231     }
232 }
233 
234 } // end of namespace isc::dhcp
235 } // end of namespace isc
236